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/dist/index.html
CHANGED
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
<link rel="apple-touch-icon" sizes="180x180" href="/farming/farming-2/app-icon-v2-180.png" />
|
|
16
16
|
<link rel="manifest" href="/farming/farming-2/site.webmanifest" />
|
|
17
17
|
<title>Farming 2</title>
|
|
18
|
-
<script type="module" crossorigin src="/farming/assets/index-
|
|
18
|
+
<script type="module" crossorigin src="/farming/assets/index-iKBvPjWS.js"></script>
|
|
19
19
|
<link rel="modulepreload" crossorigin href="/farming/assets/chunk-DECur_0Z.js">
|
|
20
20
|
<link rel="modulepreload" crossorigin href="/farming/assets/preload-helper-mJowh_hw.js">
|
|
21
21
|
<link rel="modulepreload" crossorigin href="/farming/assets/jsx-runtime-BthL93pI.js">
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
function createClient(options = {}) {
|
|
3
3
|
const getSocket = options.getSocket || (() => null);
|
|
4
4
|
const fetchImpl = options.fetchImpl || global.fetch.bind(global);
|
|
5
|
+
const composerResults = new Map();
|
|
6
|
+
let composerRequestSequence = 0;
|
|
5
7
|
|
|
6
8
|
function send(message) {
|
|
7
9
|
const ws = getSocket();
|
|
@@ -31,13 +33,33 @@
|
|
|
31
33
|
});
|
|
32
34
|
},
|
|
33
35
|
|
|
34
|
-
sendComposerMessage(agentId, message, attachments = []) {
|
|
35
|
-
|
|
36
|
+
sendComposerMessage(agentId, message, attachments = [], options = {}) {
|
|
37
|
+
const requestId = options.onResult
|
|
38
|
+
? `composer-${Date.now().toString(36)}-${++composerRequestSequence}`
|
|
39
|
+
: '';
|
|
40
|
+
const sent = send({
|
|
36
41
|
type: 'composer-input',
|
|
37
42
|
agentId,
|
|
38
43
|
message,
|
|
44
|
+
...(requestId ? { requestId } : {}),
|
|
39
45
|
...(attachments.length > 0 ? { attachments } : {}),
|
|
40
46
|
});
|
|
47
|
+
if (sent && requestId) composerResults.set(requestId, options.onResult);
|
|
48
|
+
return sent;
|
|
49
|
+
},
|
|
50
|
+
|
|
51
|
+
handleServerMessage(message) {
|
|
52
|
+
if (!message || message.type !== 'composer-input-result') return false;
|
|
53
|
+
const callback = composerResults.get(message.requestId);
|
|
54
|
+
if (!callback) return false;
|
|
55
|
+
composerResults.delete(message.requestId);
|
|
56
|
+
callback(message);
|
|
57
|
+
return true;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
rejectPendingComposerMessages(message = 'Connection unavailable') {
|
|
61
|
+
composerResults.forEach(callback => callback({ accepted: false, message }));
|
|
62
|
+
composerResults.clear();
|
|
41
63
|
},
|
|
42
64
|
|
|
43
65
|
interruptAgent(agentId) {
|
|
@@ -79,6 +79,7 @@ let crtAgentPageResizeFrame = null;
|
|
|
79
79
|
let dashboardRenderDeferred = false;
|
|
80
80
|
let lastCrtDashboardSignature = '';
|
|
81
81
|
let crtPreviewRenderTimer = null;
|
|
82
|
+
let crtPreviewRenderFrame = null;
|
|
82
83
|
const pendingCrtPreviewRenders = new Map();
|
|
83
84
|
const crtStructuredPreviewCache = new Map();
|
|
84
85
|
const crtStructuredPreviewTimers = new Map();
|
|
@@ -1233,7 +1234,6 @@ function updateCrtAgentPreviewCard(agent) {
|
|
|
1233
1234
|
}
|
|
1234
1235
|
|
|
1235
1236
|
function flushCrtPreviewCardRenders() {
|
|
1236
|
-
crtPreviewRenderTimer = null;
|
|
1237
1237
|
const pending = Array.from(pendingCrtPreviewRenders.values());
|
|
1238
1238
|
pendingCrtPreviewRenders.clear();
|
|
1239
1239
|
if (pending.length === 0) return;
|
|
@@ -1242,9 +1242,12 @@ function flushCrtPreviewCardRenders() {
|
|
|
1242
1242
|
return;
|
|
1243
1243
|
}
|
|
1244
1244
|
|
|
1245
|
-
pending.forEach(({ agent,
|
|
1245
|
+
pending.forEach(({ agent, previewChanged }) => {
|
|
1246
1246
|
const currentAgent = state && state.agents.find((candidate) => candidate.id === agent.id);
|
|
1247
1247
|
if (!isCrtLiveAgent(currentAgent)) return;
|
|
1248
|
+
// updateCrtAgentPreviewCard prepares a detached preview tree and swaps it
|
|
1249
|
+
// into the existing card atomically. Keeping that card stable avoids the
|
|
1250
|
+
// flash caused by rebuilding or overlaying every sampled frame.
|
|
1248
1251
|
if (!updateCrtAgentPreviewCard(currentAgent)) {
|
|
1249
1252
|
if (currentAgent.id !== state.mainAgentId && !isCrtAgentOnCurrentPage(currentAgent.id)) {
|
|
1250
1253
|
if (previewChanged) pulseCrtBrandForAgent(agent.id);
|
|
@@ -1253,44 +1256,30 @@ function flushCrtPreviewCardRenders() {
|
|
|
1253
1256
|
renderCrtDashboardIfNeeded(true);
|
|
1254
1257
|
return;
|
|
1255
1258
|
}
|
|
1256
|
-
if (previewChanged) {
|
|
1257
|
-
appendCrtPreviewAfterimage(agent.id, previousSnapshot, previousText);
|
|
1258
|
-
pulseCrtBrandForAgent(agent.id);
|
|
1259
|
-
}
|
|
1260
1259
|
});
|
|
1261
1260
|
}
|
|
1262
1261
|
|
|
1263
|
-
function
|
|
1262
|
+
function flushCrtPreviewCardRendersOnFrame() {
|
|
1263
|
+
crtPreviewRenderTimer = null;
|
|
1264
|
+
if (crtPreviewRenderFrame !== null) return;
|
|
1265
|
+
crtPreviewRenderFrame = requestAnimationFrame(() => {
|
|
1266
|
+
crtPreviewRenderFrame = null;
|
|
1267
|
+
flushCrtPreviewCardRenders();
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
function scheduleCrtPreviewCardRender(agent, previewChanged = false) {
|
|
1264
1272
|
if (!isCrtLiveAgent(agent)) return;
|
|
1265
1273
|
const existing = pendingCrtPreviewRenders.get(agent.id);
|
|
1266
1274
|
pendingCrtPreviewRenders.set(agent.id, {
|
|
1267
1275
|
agent,
|
|
1268
|
-
previousSnapshot: existing ? existing.previousSnapshot : previousSnapshot,
|
|
1269
|
-
previousText: existing ? existing.previousText : previousText,
|
|
1270
1276
|
previewChanged: Boolean(previewChanged || (existing && existing.previewChanged)),
|
|
1271
1277
|
});
|
|
1272
|
-
if (!crtPreviewRenderTimer) {
|
|
1273
|
-
crtPreviewRenderTimer = setTimeout(
|
|
1278
|
+
if (!crtPreviewRenderTimer && crtPreviewRenderFrame === null) {
|
|
1279
|
+
crtPreviewRenderTimer = setTimeout(flushCrtPreviewCardRendersOnFrame, CRT_PREVIEW_RENDER_INTERVAL_MS);
|
|
1274
1280
|
}
|
|
1275
1281
|
}
|
|
1276
1282
|
|
|
1277
|
-
function appendCrtPreviewAfterimage(agentId, snapshot, fallbackText) {
|
|
1278
|
-
if (typeof document === 'undefined') return;
|
|
1279
|
-
const block = Array.from(document.querySelectorAll('[data-agent-id]'))
|
|
1280
|
-
.find((candidate) => candidate.dataset.agentId === agentId);
|
|
1281
|
-
const output = block && block.querySelector('.agent-output');
|
|
1282
|
-
if (!output || (!snapshot && !fallbackText)) return;
|
|
1283
|
-
|
|
1284
|
-
const afterimage = document.createElement('div');
|
|
1285
|
-
afterimage.className = 'agent-output-afterimage';
|
|
1286
|
-
const tail = document.createElement('div');
|
|
1287
|
-
tail.className = 'agent-output-tail';
|
|
1288
|
-
if (!renderCrtTerminalSnapshot(tail, snapshot)) tail.textContent = fallbackText;
|
|
1289
|
-
afterimage.appendChild(tail);
|
|
1290
|
-
afterimage.addEventListener('animationend', () => afterimage.remove(), { once: true });
|
|
1291
|
-
output.appendChild(afterimage);
|
|
1292
|
-
}
|
|
1293
|
-
|
|
1294
1283
|
function crtCommandProgram(command) {
|
|
1295
1284
|
const tokens = String(command || '').trim().split(/\s+/).filter(Boolean);
|
|
1296
1285
|
let index = tokens[0] === 'env' ? 1 : 0;
|
|
@@ -1400,8 +1389,8 @@ function isCrtAgentWorking(agent) {
|
|
|
1400
1389
|
if (agent.terminalStatus && agent.terminalStatus.busy === true) return true;
|
|
1401
1390
|
const runtime = agent.runtimeBinding || { kind: 'terminal' };
|
|
1402
1391
|
if (
|
|
1403
|
-
runtime.kind === '
|
|
1404
|
-
['working', 'waiting-for-input', 'interrupting'].includes(runtime.state || '')
|
|
1392
|
+
runtime.kind === 'acp' &&
|
|
1393
|
+
['working', 'waiting-for-permission', 'waiting-for-input', 'interrupting'].includes(runtime.state || '')
|
|
1405
1394
|
) {
|
|
1406
1395
|
return true;
|
|
1407
1396
|
}
|
|
@@ -2273,10 +2262,25 @@ function installCrtTerminalTestApi() {
|
|
|
2273
2262
|
checkpointInFlight: replication.checkpointInFlight,
|
|
2274
2263
|
checkpointInstallInProgress: replication.installInProgress,
|
|
2275
2264
|
initialFocusPending: replication.initialFocusPending,
|
|
2265
|
+
queuedTransitionCount: replication.replayState.queuedTransitions.length,
|
|
2276
2266
|
pendingFitResize: replication.pendingFitResize,
|
|
2277
2267
|
fitResizeTimerPending: replication.fitResizeTimer !== null
|
|
2278
2268
|
};
|
|
2279
2269
|
},
|
|
2270
|
+
getGeometry() {
|
|
2271
|
+
const buffer = terminal?.buffer?.active;
|
|
2272
|
+
const proposed = fitAddon && typeof fitAddon.proposeDimensions === 'function'
|
|
2273
|
+
? fitAddon.proposeDimensions()
|
|
2274
|
+
: null;
|
|
2275
|
+
return {
|
|
2276
|
+
cols: terminal?.cols || 0,
|
|
2277
|
+
rows: terminal?.rows || 0,
|
|
2278
|
+
proposedCols: proposed?.cols || 0,
|
|
2279
|
+
proposedRows: proposed?.rows || 0,
|
|
2280
|
+
baseY: buffer?.baseY || 0,
|
|
2281
|
+
viewportY: buffer?.viewportY || 0
|
|
2282
|
+
};
|
|
2283
|
+
},
|
|
2280
2284
|
getRows() {
|
|
2281
2285
|
const buffer = terminal && terminal.buffer && terminal.buffer.active;
|
|
2282
2286
|
if (!buffer || typeof buffer.getLine !== 'function') return [];
|
|
@@ -2414,7 +2418,7 @@ function finishCrtTerminalReplay(replication = crtTerminalReplication) {
|
|
|
2414
2418
|
requestAnimationFrame(() => {
|
|
2415
2419
|
if (!crtTerminalReplication || crtTerminalReplication !== replication || replication.disposed) return;
|
|
2416
2420
|
document.getElementById('terminal-output')?.classList.remove('crt-terminal-checkpoint-installing');
|
|
2417
|
-
sendSessionResize(replication.agentId);
|
|
2421
|
+
sendSessionResize(replication.agentId, null, { forceDuringRecovery: true });
|
|
2418
2422
|
settleInitialCrtTerminalFocus(replication);
|
|
2419
2423
|
});
|
|
2420
2424
|
}
|
|
@@ -2546,6 +2550,10 @@ function performCrtTerminalCheckpointInstall(replication, sessionView) {
|
|
|
2546
2550
|
runtime.markHydrated(sessionView.renderOutput.length);
|
|
2547
2551
|
syncSessionRuntimeState();
|
|
2548
2552
|
}
|
|
2553
|
+
// The checkpoint geometry belongs to the previous viewer. Reconcile it with
|
|
2554
|
+
// this CRT container as soon as the checkpoint is committed; a continuous
|
|
2555
|
+
// live stream must not postpone the fit until the replay queue becomes idle.
|
|
2556
|
+
sendSessionResize(replication.agentId, null, { forceDuringRecovery: true });
|
|
2549
2557
|
if (drainCrtTerminalCheckpointInstall(replication)) return;
|
|
2550
2558
|
flushCrtTerminalTransitions();
|
|
2551
2559
|
finishCrtTerminalReplay(replication);
|
|
@@ -2600,6 +2608,7 @@ function installCrtTerminalCheckpoint(sessionView) {
|
|
|
2600
2608
|
terminal.rows === sessionView.previewRows
|
|
2601
2609
|
) {
|
|
2602
2610
|
TERMINAL_REPLAY.commitCheckpoint(replication.replayState, checkpoint);
|
|
2611
|
+
sendSessionResize(replication.agentId);
|
|
2603
2612
|
flushCrtTerminalTransitions();
|
|
2604
2613
|
finishCrtTerminalReplay(replication);
|
|
2605
2614
|
return true;
|
|
@@ -2786,6 +2795,9 @@ async function loadGlobalSettings() {
|
|
|
2786
2795
|
...globalSettings,
|
|
2787
2796
|
...(data.settings || {})
|
|
2788
2797
|
};
|
|
2798
|
+
if (globalSettings.instanceName) {
|
|
2799
|
+
document.title = `${globalSettings.instanceName} · Farming CRT`;
|
|
2800
|
+
}
|
|
2789
2801
|
applyCRTEffects(globalSettings.crtSkinEffectsEnabled !== false);
|
|
2790
2802
|
if (state) renderCrtDashboardIfNeeded();
|
|
2791
2803
|
if (agents.length > 0) renderAgentList();
|
|
@@ -2813,6 +2825,9 @@ async function saveGlobalSettings() {
|
|
|
2813
2825
|
...globalSettings,
|
|
2814
2826
|
...(data.settings || {})
|
|
2815
2827
|
};
|
|
2828
|
+
if (globalSettings.instanceName) {
|
|
2829
|
+
document.title = `${globalSettings.instanceName} · Farming CRT`;
|
|
2830
|
+
}
|
|
2816
2831
|
syncWorkspaceSettings();
|
|
2817
2832
|
}
|
|
2818
2833
|
} catch (error) {
|
|
@@ -5297,6 +5312,7 @@ function connect() {
|
|
|
5297
5312
|
socket.onmessage = (event) => {
|
|
5298
5313
|
if (ws !== socket) return;
|
|
5299
5314
|
const data = JSON.parse(event.data);
|
|
5315
|
+
if (getSessionClient()?.handleServerMessage(data)) return;
|
|
5300
5316
|
if (data.type === 'protocol-hello') {
|
|
5301
5317
|
if (data.protocolVersion !== CRT_PROTOCOL_VERSION) {
|
|
5302
5318
|
socket.close(4002, `Unsupported Farming protocol version ${data.protocolVersion}`);
|
|
@@ -5315,6 +5331,18 @@ function connect() {
|
|
|
5315
5331
|
terminalPreviewSnapshots.forEach((_snapshot, agentId) => {
|
|
5316
5332
|
if (!activeAgentIds.has(agentId)) terminalPreviewSnapshots.delete(agentId);
|
|
5317
5333
|
});
|
|
5334
|
+
if (
|
|
5335
|
+
focusedAgentId
|
|
5336
|
+
&& !activeAgentIds.has(focusedAgentId)
|
|
5337
|
+
&& !runtimeSwitchPending
|
|
5338
|
+
&& !pendingRuntimeSwitchAgentId
|
|
5339
|
+
) {
|
|
5340
|
+
// Another viewer may kill or archive the Agent while this CRT session
|
|
5341
|
+
// is open. Runtime replacement has its own bounded handoff and may
|
|
5342
|
+
// briefly remove the old id, so do not mistake that legal transition
|
|
5343
|
+
// for a dead session.
|
|
5344
|
+
closeSession();
|
|
5345
|
+
}
|
|
5318
5346
|
state.agents.forEach((agent) => {
|
|
5319
5347
|
if (terminalPreviewSnapshots.has(agent.id)) {
|
|
5320
5348
|
agent.previewSnapshot = terminalPreviewSnapshots.get(agent.id);
|
|
@@ -5359,21 +5387,23 @@ function connect() {
|
|
|
5359
5387
|
} else if (data.type === 'session-preview') {
|
|
5360
5388
|
const preview = data.preview;
|
|
5361
5389
|
if (preview && preview.agentId) {
|
|
5362
|
-
|
|
5390
|
+
if (preview.previewSnapshot && typeof preview.previewSnapshot === 'object') {
|
|
5391
|
+
terminalPreviewSnapshots.set(preview.agentId, preview.previewSnapshot);
|
|
5392
|
+
}
|
|
5363
5393
|
const agent = state && state.agents.find((candidate) => candidate.id === preview.agentId);
|
|
5364
5394
|
if (agent) {
|
|
5365
|
-
const previousSnapshot = agent.previewSnapshot;
|
|
5366
|
-
const previousText = getAgentDisplayText(agent);
|
|
5367
5395
|
const previewChanged = typeof preview.previewText === 'string'
|
|
5368
5396
|
&& preview.previewText !== agent.previewText;
|
|
5369
5397
|
agent.previewText = preview.previewText || agent.previewText;
|
|
5370
5398
|
agent.previewCols = preview.cols || agent.previewCols;
|
|
5371
5399
|
agent.previewRows = preview.rows || agent.previewRows;
|
|
5372
|
-
|
|
5400
|
+
// A null preview is not a completed new frame. Retain the last
|
|
5401
|
+
// authoritative snapshot until a replacement is ready.
|
|
5402
|
+
agent.previewSnapshot = preview.previewSnapshot || agent.previewSnapshot || null;
|
|
5373
5403
|
if (preview.terminalStatus) agent.terminalStatus = preview.terminalStatus;
|
|
5374
5404
|
if (preview.runtimeObservation) agent.runtimeObservation = preview.runtimeObservation;
|
|
5375
5405
|
if (isCrtSessionOpen()) dashboardRenderDeferred = true;
|
|
5376
|
-
else scheduleCrtPreviewCardRender(agent,
|
|
5406
|
+
else scheduleCrtPreviewCardRender(agent, previewChanged);
|
|
5377
5407
|
}
|
|
5378
5408
|
}
|
|
5379
5409
|
} else if (data.type === 'session-output') {
|
|
@@ -5404,7 +5434,8 @@ function connect() {
|
|
|
5404
5434
|
const agent = activity && state && state.agents.find((candidate) => candidate.id === activity.agentId);
|
|
5405
5435
|
if (agent) {
|
|
5406
5436
|
Object.assign(agent, activity);
|
|
5407
|
-
|
|
5437
|
+
if (isCrtSessionOpen()) dashboardRenderDeferred = true;
|
|
5438
|
+
else scheduleCrtPreviewCardRender(agent);
|
|
5408
5439
|
}
|
|
5409
5440
|
} else if (data.type === 'system-stats') {
|
|
5410
5441
|
updateSystemStats(data.stats, data.uptime, data.usageRate);
|
|
@@ -5420,6 +5451,7 @@ function connect() {
|
|
|
5420
5451
|
socket.onclose = () => {
|
|
5421
5452
|
if (ws !== socket) return;
|
|
5422
5453
|
ws = null;
|
|
5454
|
+
getSessionClient()?.rejectPendingComposerMessages();
|
|
5423
5455
|
console.log('Disconnected from server');
|
|
5424
5456
|
if (crtTerminalReplication) {
|
|
5425
5457
|
clearPendingCrtTerminalFitResize(crtTerminalReplication);
|
|
@@ -5842,7 +5874,6 @@ function structuredRuntimeKind(agent) {
|
|
|
5842
5874
|
const kind = agent && agent.runtimeBinding && agent.runtimeBinding.kind;
|
|
5843
5875
|
if (kind === 'acp') return 'ACP';
|
|
5844
5876
|
if (kind === 'json') return 'JSON';
|
|
5845
|
-
if (kind === 'app-server') return 'APP SERVER';
|
|
5846
5877
|
return '';
|
|
5847
5878
|
}
|
|
5848
5879
|
|
|
@@ -5868,6 +5899,19 @@ function canSwitchCrtAgentRuntime(agent) {
|
|
|
5868
5899
|
);
|
|
5869
5900
|
}
|
|
5870
5901
|
|
|
5902
|
+
function crtRuntimeSwitchUnavailableReason(agent) {
|
|
5903
|
+
if (!agent || !agent.providerCapabilities?.runtimeSwitch || !agent.providerCapabilities?.supportsChat) {
|
|
5904
|
+
return '';
|
|
5905
|
+
}
|
|
5906
|
+
if (agent.providerSessionTemporary === true && agent.terminalInputReceived === true) {
|
|
5907
|
+
return 'MSG unavailable: this temporary Terminal session already has input and cannot be resumed safely.';
|
|
5908
|
+
}
|
|
5909
|
+
if (!String(agent.providerSessionId || '').trim()) {
|
|
5910
|
+
return 'MSG unavailable: this Agent has no resumable provider session yet.';
|
|
5911
|
+
}
|
|
5912
|
+
return '';
|
|
5913
|
+
}
|
|
5914
|
+
|
|
5871
5915
|
function isCrtRuntimeSwitchShortcut(event) {
|
|
5872
5916
|
return Boolean(
|
|
5873
5917
|
event
|
|
@@ -5931,11 +5975,27 @@ function updateCrtRuntimeSwitchControl(agent) {
|
|
|
5931
5975
|
const terminalButton = document.getElementById('crt-runtime-terminal');
|
|
5932
5976
|
if (!control || !chatButton || !terminalButton) return;
|
|
5933
5977
|
const supported = canSwitchCrtAgentRuntime(agent);
|
|
5978
|
+
const unavailableReason = crtRuntimeSwitchUnavailableReason(agent);
|
|
5934
5979
|
const view = crtRuntimeView(agent);
|
|
5935
|
-
control
|
|
5980
|
+
// Keep a disabled control visible for a provider that supports Chat when
|
|
5981
|
+
// this particular session cannot be resumed. Hiding it made a live
|
|
5982
|
+
// Terminal look like a product capability was missing, while silently
|
|
5983
|
+
// enabling it would risk replacing a temporary session and losing input.
|
|
5984
|
+
control.hidden = !supported && !unavailableReason;
|
|
5936
5985
|
control.setAttribute('aria-busy', runtimeSwitchPending ? 'true' : 'false');
|
|
5937
5986
|
chatButton.disabled = !supported || runtimeSwitchPending;
|
|
5938
5987
|
terminalButton.disabled = !supported || runtimeSwitchPending;
|
|
5988
|
+
if (unavailableReason) {
|
|
5989
|
+
control.title = unavailableReason;
|
|
5990
|
+
control.setAttribute('aria-label', unavailableReason);
|
|
5991
|
+
chatButton.title = unavailableReason;
|
|
5992
|
+
terminalButton.title = unavailableReason;
|
|
5993
|
+
} else {
|
|
5994
|
+
control.removeAttribute('title');
|
|
5995
|
+
control.removeAttribute('aria-label');
|
|
5996
|
+
chatButton.title = 'Chat view [Alt+M]';
|
|
5997
|
+
terminalButton.title = 'Terminal view [Alt+M]';
|
|
5998
|
+
}
|
|
5939
5999
|
chatButton.classList.toggle('active', view === 'chat');
|
|
5940
6000
|
terminalButton.classList.toggle('active', view === 'terminal');
|
|
5941
6001
|
chatButton.setAttribute('aria-pressed', view === 'chat' ? 'true' : 'false');
|
|
@@ -5973,7 +6033,7 @@ async function switchCrtSessionRuntimeMode(mode) {
|
|
|
5973
6033
|
if (runtimeSwitchPending || !focusedAgentId || !state) return;
|
|
5974
6034
|
const agent = state.agents.find((candidate) => candidate.id === focusedAgentId);
|
|
5975
6035
|
if (!canSwitchCrtAgentRuntime(agent)) return;
|
|
5976
|
-
const targetMode = mode === 'terminal' ? 'terminal' : '
|
|
6036
|
+
const targetMode = mode === 'terminal' ? 'terminal' : 'chat';
|
|
5977
6037
|
if (crtRuntimeView(agent) === (targetMode === 'terminal' ? 'terminal' : 'chat')) return;
|
|
5978
6038
|
|
|
5979
6039
|
const requestSequence = ++runtimeSwitchRequestSequence;
|
|
@@ -6006,14 +6066,17 @@ async function switchCrtSessionRuntimeMode(mode) {
|
|
|
6006
6066
|
function toggleCrtSessionRuntimeMode() {
|
|
6007
6067
|
if (!focusedAgentId || !state) return;
|
|
6008
6068
|
const agent = state.agents.find((candidate) => candidate.id === focusedAgentId);
|
|
6009
|
-
if (!canSwitchCrtAgentRuntime(agent))
|
|
6010
|
-
|
|
6069
|
+
if (!canSwitchCrtAgentRuntime(agent)) {
|
|
6070
|
+
const reason = crtRuntimeSwitchUnavailableReason(agent);
|
|
6071
|
+
if (reason) setCrtRuntimeSwitchStatus(reason, true);
|
|
6072
|
+
return;
|
|
6073
|
+
}
|
|
6074
|
+
void switchCrtSessionRuntimeMode(crtRuntimeView(agent) === 'chat' ? 'terminal' : 'chat');
|
|
6011
6075
|
}
|
|
6012
6076
|
|
|
6013
6077
|
function structuredTranscriptEndpoint(agent) {
|
|
6014
6078
|
if (agent.runtimeBinding.kind === 'acp') return 'acp-transcript';
|
|
6015
|
-
|
|
6016
|
-
return 'codex-app-server-transcript';
|
|
6079
|
+
return 'json-cli-transcript';
|
|
6017
6080
|
}
|
|
6018
6081
|
|
|
6019
6082
|
function structuredRuntimeStatus(agent) {
|
|
@@ -6033,7 +6096,6 @@ function structuredComposerAction(agent, draft = '') {
|
|
|
6033
6096
|
const working = ['working', 'waiting-for-permission'].includes(status);
|
|
6034
6097
|
if (working) {
|
|
6035
6098
|
if (agent.runtimeBinding?.kind === 'acp' && String(draft || '').trim()) return 'send';
|
|
6036
|
-
if (agent.runtimeBinding?.kind === 'app-server' && String(draft || '').trim()) return 'steer';
|
|
6037
6099
|
return 'interrupt';
|
|
6038
6100
|
}
|
|
6039
6101
|
if (['starting', 'interrupting'].includes(status)) return 'disabled';
|
|
@@ -6042,9 +6104,9 @@ function structuredComposerAction(agent, draft = '') {
|
|
|
6042
6104
|
: 'disabled';
|
|
6043
6105
|
}
|
|
6044
6106
|
|
|
6045
|
-
function queueStructuredComposerFollowUp(agentId, message) {
|
|
6107
|
+
function queueStructuredComposerFollowUp(agentId, message, attachments = []) {
|
|
6046
6108
|
const queue = structuredComposerPendingFollowUps.get(agentId) || [];
|
|
6047
|
-
queue.push(message);
|
|
6109
|
+
queue.push({ message, attachments });
|
|
6048
6110
|
structuredComposerPendingFollowUps.set(agentId, queue);
|
|
6049
6111
|
}
|
|
6050
6112
|
|
|
@@ -6052,10 +6114,10 @@ function flushStructuredComposerFollowUp(agent) {
|
|
|
6052
6114
|
if (!agent || structuredRuntimeStatus(agent) !== 'idle') return;
|
|
6053
6115
|
const queue = structuredComposerPendingFollowUps.get(agent.id);
|
|
6054
6116
|
if (!queue || queue.length === 0) return;
|
|
6055
|
-
const
|
|
6117
|
+
const next = queue.shift();
|
|
6056
6118
|
if (queue.length === 0) structuredComposerPendingFollowUps.delete(agent.id);
|
|
6057
|
-
if (!getSessionClient()?.sendComposerMessage(agent.id, message)) {
|
|
6058
|
-
queue.unshift(
|
|
6119
|
+
if (!getSessionClient()?.sendComposerMessage(agent.id, next.message, next.attachments)) {
|
|
6120
|
+
queue.unshift(next);
|
|
6059
6121
|
structuredComposerPendingFollowUps.set(agent.id, queue);
|
|
6060
6122
|
return;
|
|
6061
6123
|
}
|
|
@@ -6481,9 +6543,7 @@ function updateStructuredComposerState(agent) {
|
|
|
6481
6543
|
}
|
|
6482
6544
|
sendButton.disabled = action === 'disabled';
|
|
6483
6545
|
sendButton.dataset.action = action;
|
|
6484
|
-
sendButton.textContent = action === 'interrupt'
|
|
6485
|
-
? 'BREAK [ESC]'
|
|
6486
|
-
: action === 'steer' ? 'STEER [ENTER]' : 'SEND [ENTER]';
|
|
6546
|
+
sendButton.textContent = action === 'interrupt' ? 'BREAK [ESC]' : 'SEND [ENTER]';
|
|
6487
6547
|
input.placeholder = error
|
|
6488
6548
|
? 'Session unavailable'
|
|
6489
6549
|
: (busy ? 'Agent is changing state...' : 'Type a message...');
|
|
@@ -6542,6 +6602,7 @@ function readStructuredTextFile(file) {
|
|
|
6542
6602
|
async function prepareStructuredAttachment(file) {
|
|
6543
6603
|
const attachment = {
|
|
6544
6604
|
id: structuredAttachmentId(file),
|
|
6605
|
+
kind: String(file.type || '').startsWith('audio/') ? 'audio' : 'image',
|
|
6545
6606
|
name: file.name || 'attachment',
|
|
6546
6607
|
status: 'uploading',
|
|
6547
6608
|
messageBlock: ''
|
|
@@ -6549,15 +6610,21 @@ async function prepareStructuredAttachment(file) {
|
|
|
6549
6610
|
structuredComposerAttachments.push(attachment);
|
|
6550
6611
|
renderStructuredComposerAttachments();
|
|
6551
6612
|
try {
|
|
6552
|
-
if (String(file.type || '').startsWith('image/')) {
|
|
6553
|
-
const
|
|
6613
|
+
if (String(file.type || '').startsWith('image/') || String(file.type || '').startsWith('audio/')) {
|
|
6614
|
+
const image = attachment.kind === 'image';
|
|
6615
|
+
const response = await fetch(farmingApiPath(image ? '/attachments/image' : '/attachments/audio'), {
|
|
6554
6616
|
method: 'POST',
|
|
6555
|
-
headers: { 'Content-Type': file.type || 'image/png' },
|
|
6617
|
+
headers: { 'Content-Type': file.type || (image ? 'image/png' : 'audio/wav') },
|
|
6556
6618
|
body: file
|
|
6557
6619
|
});
|
|
6558
6620
|
const body = await response.json().catch(() => null);
|
|
6559
|
-
if (!response.ok || !body || !body.path) throw new Error(
|
|
6560
|
-
attachment.
|
|
6621
|
+
if (!response.ok || !body || !body.path) throw new Error(`${image ? 'Image' : 'Audio'} upload failed (${response.status})`);
|
|
6622
|
+
attachment.path = body.path;
|
|
6623
|
+
attachment.type = body.type || file.type || 'image/png';
|
|
6624
|
+
attachment.size = Number(body.size) || file.size || 0;
|
|
6625
|
+
attachment.messageBlock = image
|
|
6626
|
+
? `Attached image: ${body.name || attachment.name}\n\nImage path: ${body.path}`
|
|
6627
|
+
: `Attached audio: ${body.name || attachment.name}\n\nAudio path: ${body.path}`;
|
|
6561
6628
|
} else {
|
|
6562
6629
|
const content = await readStructuredTextFile(file);
|
|
6563
6630
|
const limit = 50000;
|
|
@@ -6581,11 +6648,25 @@ function addStructuredAttachmentFiles(files) {
|
|
|
6581
6648
|
|
|
6582
6649
|
function structuredComposerMessage(draft) {
|
|
6583
6650
|
return structuredComposerAttachments
|
|
6584
|
-
.filter((attachment) => attachment.status === 'ready' && attachment.messageBlock)
|
|
6651
|
+
.filter((attachment) => attachment.status === 'ready' && attachment.messageBlock && !attachment.path)
|
|
6585
6652
|
.reduce((message, attachment) => structuredAttachmentBlock(message, attachment.messageBlock), String(draft || ''))
|
|
6586
6653
|
.trim();
|
|
6587
6654
|
}
|
|
6588
6655
|
|
|
6656
|
+
function structuredComposerPromptAttachments() {
|
|
6657
|
+
return structuredComposerAttachments.flatMap((attachment) => (
|
|
6658
|
+
attachment.status === 'ready' && attachment.path
|
|
6659
|
+
? [{
|
|
6660
|
+
kind: attachment.kind,
|
|
6661
|
+
path: attachment.path,
|
|
6662
|
+
name: attachment.name,
|
|
6663
|
+
type: attachment.type,
|
|
6664
|
+
size: attachment.size,
|
|
6665
|
+
}]
|
|
6666
|
+
: []
|
|
6667
|
+
));
|
|
6668
|
+
}
|
|
6669
|
+
|
|
6589
6670
|
function structuredComposerHistoryFor(agentId) {
|
|
6590
6671
|
if (!agentId) return [];
|
|
6591
6672
|
return structuredComposerHistory.get(agentId) || [];
|
|
@@ -7013,11 +7094,11 @@ function setupStructuredSessionComposer() {
|
|
|
7013
7094
|
updateStructuredComposerState(agent);
|
|
7014
7095
|
});
|
|
7015
7096
|
input.addEventListener('paste', (event) => {
|
|
7016
|
-
const
|
|
7017
|
-
.filter((file) => String(file.type || '')
|
|
7018
|
-
if (!
|
|
7097
|
+
const mediaFiles = Array.from(event.clipboardData && event.clipboardData.files || [])
|
|
7098
|
+
.filter((file) => /^(image|audio)\//.test(String(file.type || '')));
|
|
7099
|
+
if (!mediaFiles.length) return;
|
|
7019
7100
|
event.preventDefault();
|
|
7020
|
-
addStructuredAttachmentFiles(
|
|
7101
|
+
addStructuredAttachmentFiles(mediaFiles);
|
|
7021
7102
|
});
|
|
7022
7103
|
input.addEventListener('compositionend', () => {
|
|
7023
7104
|
structuredComposerCompositionEndAt = Date.now();
|
|
@@ -7166,11 +7247,25 @@ function setupStructuredSessionComposer() {
|
|
|
7166
7247
|
}
|
|
7167
7248
|
const draft = input.value;
|
|
7168
7249
|
const message = structuredComposerMessage(draft);
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
? (queueStructuredComposerFollowUp(focusedAgentId, message), true)
|
|
7172
|
-
: getSessionClient()?.sendComposerMessage(focusedAgentId, message);
|
|
7250
|
+
const promptAttachments = structuredComposerPromptAttachments();
|
|
7251
|
+
if (!message && promptAttachments.length === 0) return;
|
|
7173
7252
|
const statusNode = document.getElementById('crt-structured-composer-status');
|
|
7253
|
+
const completeSubmission = () => {
|
|
7254
|
+
addStructuredComposerHistory(focusedAgentId, draft);
|
|
7255
|
+
input.value = '';
|
|
7256
|
+
structuredComposerAttachments = [];
|
|
7257
|
+
renderStructuredComposerAttachments();
|
|
7258
|
+
resizeStructuredComposerInput(input);
|
|
7259
|
+
if (statusNode) {
|
|
7260
|
+
statusNode.textContent = 'SENDING...';
|
|
7261
|
+
statusNode.classList.remove('error');
|
|
7262
|
+
}
|
|
7263
|
+
updateStructuredComposerState(agent);
|
|
7264
|
+
setTimeout(() => void refreshStructuredSession(focusedAgentId, true), 160);
|
|
7265
|
+
};
|
|
7266
|
+
const sent = action === 'send' && structuredRuntimeStatus(agent) !== 'idle'
|
|
7267
|
+
? (queueStructuredComposerFollowUp(focusedAgentId, message, promptAttachments), true)
|
|
7268
|
+
: getSessionClient()?.sendComposerMessage(focusedAgentId, message, promptAttachments);
|
|
7174
7269
|
if (!sent) {
|
|
7175
7270
|
if (statusNode) {
|
|
7176
7271
|
statusNode.textContent = 'Connection unavailable';
|
|
@@ -7178,17 +7273,7 @@ function setupStructuredSessionComposer() {
|
|
|
7178
7273
|
}
|
|
7179
7274
|
return;
|
|
7180
7275
|
}
|
|
7181
|
-
|
|
7182
|
-
input.value = '';
|
|
7183
|
-
structuredComposerAttachments = [];
|
|
7184
|
-
renderStructuredComposerAttachments();
|
|
7185
|
-
resizeStructuredComposerInput(input);
|
|
7186
|
-
if (statusNode) {
|
|
7187
|
-
statusNode.textContent = action === 'steer' ? 'STEERING...' : 'SENDING...';
|
|
7188
|
-
statusNode.classList.remove('error');
|
|
7189
|
-
}
|
|
7190
|
-
updateStructuredComposerState(agent);
|
|
7191
|
-
setTimeout(() => void refreshStructuredSession(focusedAgentId, true), 160);
|
|
7276
|
+
completeSubmission();
|
|
7192
7277
|
});
|
|
7193
7278
|
}
|
|
7194
7279
|
|
|
@@ -7536,7 +7621,11 @@ function clearPendingCrtTerminalFitResize(replication) {
|
|
|
7536
7621
|
replication.pendingFitResize = null;
|
|
7537
7622
|
}
|
|
7538
7623
|
|
|
7539
|
-
function commitCrtTerminalResize(
|
|
7624
|
+
function commitCrtTerminalResize(
|
|
7625
|
+
agentId,
|
|
7626
|
+
normalizedDimensions,
|
|
7627
|
+
{ allowDuringRecovery = false } = {}
|
|
7628
|
+
) {
|
|
7540
7629
|
const replication = crtTerminalReplication;
|
|
7541
7630
|
if (
|
|
7542
7631
|
!agentId ||
|
|
@@ -7544,7 +7633,7 @@ function commitCrtTerminalResize(agentId, normalizedDimensions) {
|
|
|
7544
7633
|
!replication ||
|
|
7545
7634
|
replication.disposed ||
|
|
7546
7635
|
replication.agentId !== agentId ||
|
|
7547
|
-
replication.replayState.recovering ||
|
|
7636
|
+
(!allowDuringRecovery && replication.replayState.recovering) ||
|
|
7548
7637
|
(
|
|
7549
7638
|
normalizedDimensions.cols === replication.lastResizeCols &&
|
|
7550
7639
|
normalizedDimensions.rows === replication.lastResizeRows
|
|
@@ -7586,7 +7675,11 @@ function scheduleCrtTerminalFitResize(replication, agentId, normalizedDimensions
|
|
|
7586
7675
|
}, CRT_TERMINAL_RESIZE_SETTLE_MS);
|
|
7587
7676
|
}
|
|
7588
7677
|
|
|
7589
|
-
function sendSessionResize(
|
|
7678
|
+
function sendSessionResize(
|
|
7679
|
+
agentId = focusedAgentId,
|
|
7680
|
+
requestedDimensions = null,
|
|
7681
|
+
{ forceDuringRecovery = false } = {}
|
|
7682
|
+
) {
|
|
7590
7683
|
const replication = crtTerminalReplication;
|
|
7591
7684
|
if (
|
|
7592
7685
|
!agentId ||
|
|
@@ -7594,7 +7687,7 @@ function sendSessionResize(agentId = focusedAgentId, requestedDimensions = null)
|
|
|
7594
7687
|
!fitAddon ||
|
|
7595
7688
|
!replication ||
|
|
7596
7689
|
replication.agentId !== agentId ||
|
|
7597
|
-
replication.replayState.recovering
|
|
7690
|
+
(!forceDuringRecovery && replication.replayState.recovering)
|
|
7598
7691
|
) return;
|
|
7599
7692
|
const dimensions = requestedDimensions || (
|
|
7600
7693
|
typeof fitAddon.proposeDimensions === 'function'
|
|
@@ -7618,6 +7711,25 @@ function sendSessionResize(agentId = focusedAgentId, requestedDimensions = null)
|
|
|
7618
7711
|
normalizedDimensions.rows === replication.lastResizeRows
|
|
7619
7712
|
) {
|
|
7620
7713
|
clearPendingCrtTerminalFitResize(replication);
|
|
7714
|
+
// A checkpoint can temporarily restore the previous viewer's geometry
|
|
7715
|
+
// after this surface's resize was already delivered. Reapply the known
|
|
7716
|
+
// local geometry without sending a duplicate resize to the backend.
|
|
7717
|
+
if (
|
|
7718
|
+
terminal.cols !== normalizedDimensions.cols ||
|
|
7719
|
+
terminal.rows !== normalizedDimensions.rows
|
|
7720
|
+
) {
|
|
7721
|
+
replication.applyingLocalResize = true;
|
|
7722
|
+
try {
|
|
7723
|
+
terminal.resize(normalizedDimensions.cols, normalizedDimensions.rows);
|
|
7724
|
+
} finally {
|
|
7725
|
+
replication.applyingLocalResize = false;
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
return;
|
|
7729
|
+
}
|
|
7730
|
+
if (forceDuringRecovery) {
|
|
7731
|
+
clearPendingCrtTerminalFitResize(replication);
|
|
7732
|
+
commitCrtTerminalResize(agentId, normalizedDimensions, { allowDuringRecovery: true });
|
|
7621
7733
|
return;
|
|
7622
7734
|
}
|
|
7623
7735
|
scheduleCrtTerminalFitResize(replication, agentId, normalizedDimensions);
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
inset: 3px 0 0;
|
|
175
175
|
display: flex;
|
|
176
176
|
flex-direction: column;
|
|
177
|
-
justify-content: flex-
|
|
177
|
+
justify-content: flex-start;
|
|
178
178
|
gap: 5px;
|
|
179
179
|
min-height: 0;
|
|
180
180
|
padding: 0 4px 2px 12px;
|
|
@@ -209,7 +209,7 @@
|
|
|
209
209
|
display: flex;
|
|
210
210
|
flex: 0 1 auto;
|
|
211
211
|
flex-direction: column;
|
|
212
|
-
justify-content: flex-
|
|
212
|
+
justify-content: flex-start;
|
|
213
213
|
gap: 5px;
|
|
214
214
|
min-height: 0;
|
|
215
215
|
overflow: hidden;
|
|
@@ -300,20 +300,11 @@
|
|
|
300
300
|
opacity: 0.58;
|
|
301
301
|
}
|
|
302
302
|
|
|
303
|
-
.agent-block.working .agent-output:not(.structured-preview) {
|
|
304
|
-
animation: blink 0.5s infinite;
|
|
305
|
-
}
|
|
306
|
-
|
|
307
303
|
@keyframes crt-chat-signal {
|
|
308
304
|
0%, 100% { opacity: 0.45; transform: scale(0.82); }
|
|
309
305
|
50% { opacity: 1; transform: scale(1); }
|
|
310
306
|
}
|
|
311
307
|
|
|
312
|
-
@keyframes blink {
|
|
313
|
-
0%, 100% { opacity: 1; }
|
|
314
|
-
50% { opacity: 0.8; }
|
|
315
|
-
}
|
|
316
|
-
|
|
317
308
|
.sidebar {
|
|
318
309
|
width: 200px;
|
|
319
310
|
background: #2a2a2a;
|