farming-code 2.2.14 → 2.2.17
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 +3 -3
- package/README.zh_cn.md +3 -3
- package/THIRD_PARTY_NOTICES.md +17 -1
- package/backend/acp/packaged-codex-acp.js +13 -0
- package/backend/acp-runtime.js +413 -24
- package/backend/agent-manager.js +572 -557
- package/backend/agent-provider-session.js +15 -1
- package/backend/agent-runtime-binding.js +9 -55
- package/backend/chat-runtime.js +28 -0
- package/backend/codex-rollout-follower.js +83 -0
- package/backend/codex-session-archive.js +12 -1
- package/backend/codex-session-history.js +94 -0
- package/backend/codex-transcript.js +2 -1
- package/backend/config-manager.js +25 -17
- package/backend/control-api.js +3 -1
- package/backend/farming-app-cli.js +21 -0
- package/backend/farming-session-store.js +2 -1
- package/backend/native-pty-host-client.js +59 -2
- package/backend/npm-update-helper.js +65 -7
- package/backend/provider-adapters.js +123 -38
- package/backend/provider-session-service.js +138 -34
- package/backend/server.js +94 -105
- package/backend/storage-layout.js +5 -16
- package/backend/update-service.js +94 -5
- package/backend/usage-history-client.js +305 -0
- package/backend/usage-history-scanner.generated.js +2250 -0
- package/backend/usage-history-scanner.ts +2718 -0
- package/backend/usage-history-smoke.js +64 -0
- package/backend/usage-history-worker.js +34 -0
- package/backend/usage-monitor.js +188 -588
- package/backend/vendor/usage-parsers/CC_STATISTICS_LICENSE +21 -0
- package/backend/vendor/usage-parsers/CODEXBAR_LICENSE +21 -0
- package/backend/vendor/usage-parsers/VENDOR.json +45 -0
- package/dist/acp/LICENSE.codex-acp +190 -0
- package/dist/acp/codex-acp-1.1.4.js +30718 -0
- package/dist/assets/App-6yWdwzj8.js +210 -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-5esj0bRu.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-BA-iH2Kg.js → index-C8ZFKye1.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-DSO3i_Gs.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 +203 -85
- package/frontend/skins/crt/index.html +2 -11
- package/frontend/skins/crt/styles/effects.css +2 -34
- package/package.json +30 -12
- package/shared/backend-connection-status.d.ts +25 -0
- package/shared/backend-connection-status.js +40 -0
- 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-Bumi772v.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-C8ZFKye1.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) {
|
|
@@ -3841,11 +3856,17 @@ function renderCrtBillingSelectedDay({ preferSummary = false } = {}) {
|
|
|
3841
3856
|
updateCrtBillingExactMetric('billing-day-cache-read', displayPoint && displayPoint.cacheReadTokens, { date: selectedDate, live: isToday });
|
|
3842
3857
|
updateCrtBillingExactMetric('billing-day-cache-write', displayPoint && displayPoint.cacheWriteTokens, { date: selectedDate, live: isToday });
|
|
3843
3858
|
if (providers) {
|
|
3844
|
-
const providerTotals = displayProviders
|
|
3845
|
-
|
|
3859
|
+
const providerTotals = displayProviders
|
|
3860
|
+
? Object.entries(displayProviders).filter(([, usage]) => (
|
|
3861
|
+
Math.max(0, Number(usage && usage.totalTokens) || 0) > 0
|
|
3862
|
+
))
|
|
3863
|
+
: [];
|
|
3864
|
+
providers.textContent = providerTotals.length > 0
|
|
3865
|
+
? `${providerTotals.length} ${providerTotals.length === 1 ? 'SOURCE' : 'SOURCES'}`
|
|
3866
|
+
: '--';
|
|
3867
|
+
providers.title = providerTotals
|
|
3846
3868
|
.map(([provider, usage]) => `${provider.toUpperCase()} ${formatCrtExactUsageValue(usage && usage.totalTokens)}`)
|
|
3847
3869
|
.join(' · ') || '--';
|
|
3848
|
-
providers.title = providers.textContent;
|
|
3849
3870
|
}
|
|
3850
3871
|
if (stateLabel) {
|
|
3851
3872
|
const notes = isToday ? ['LIVE 5S', 'PARTIAL DAY', 'INCL CACHE'] : ['COMPLETE DAY', 'INCL CACHE'];
|
|
@@ -5297,6 +5318,7 @@ function connect() {
|
|
|
5297
5318
|
socket.onmessage = (event) => {
|
|
5298
5319
|
if (ws !== socket) return;
|
|
5299
5320
|
const data = JSON.parse(event.data);
|
|
5321
|
+
if (getSessionClient()?.handleServerMessage(data)) return;
|
|
5300
5322
|
if (data.type === 'protocol-hello') {
|
|
5301
5323
|
if (data.protocolVersion !== CRT_PROTOCOL_VERSION) {
|
|
5302
5324
|
socket.close(4002, `Unsupported Farming protocol version ${data.protocolVersion}`);
|
|
@@ -5315,6 +5337,18 @@ function connect() {
|
|
|
5315
5337
|
terminalPreviewSnapshots.forEach((_snapshot, agentId) => {
|
|
5316
5338
|
if (!activeAgentIds.has(agentId)) terminalPreviewSnapshots.delete(agentId);
|
|
5317
5339
|
});
|
|
5340
|
+
if (
|
|
5341
|
+
focusedAgentId
|
|
5342
|
+
&& !activeAgentIds.has(focusedAgentId)
|
|
5343
|
+
&& !runtimeSwitchPending
|
|
5344
|
+
&& !pendingRuntimeSwitchAgentId
|
|
5345
|
+
) {
|
|
5346
|
+
// Another viewer may kill or archive the Agent while this CRT session
|
|
5347
|
+
// is open. Runtime replacement has its own bounded handoff and may
|
|
5348
|
+
// briefly remove the old id, so do not mistake that legal transition
|
|
5349
|
+
// for a dead session.
|
|
5350
|
+
closeSession();
|
|
5351
|
+
}
|
|
5318
5352
|
state.agents.forEach((agent) => {
|
|
5319
5353
|
if (terminalPreviewSnapshots.has(agent.id)) {
|
|
5320
5354
|
agent.previewSnapshot = terminalPreviewSnapshots.get(agent.id);
|
|
@@ -5359,21 +5393,23 @@ function connect() {
|
|
|
5359
5393
|
} else if (data.type === 'session-preview') {
|
|
5360
5394
|
const preview = data.preview;
|
|
5361
5395
|
if (preview && preview.agentId) {
|
|
5362
|
-
|
|
5396
|
+
if (preview.previewSnapshot && typeof preview.previewSnapshot === 'object') {
|
|
5397
|
+
terminalPreviewSnapshots.set(preview.agentId, preview.previewSnapshot);
|
|
5398
|
+
}
|
|
5363
5399
|
const agent = state && state.agents.find((candidate) => candidate.id === preview.agentId);
|
|
5364
5400
|
if (agent) {
|
|
5365
|
-
const previousSnapshot = agent.previewSnapshot;
|
|
5366
|
-
const previousText = getAgentDisplayText(agent);
|
|
5367
5401
|
const previewChanged = typeof preview.previewText === 'string'
|
|
5368
5402
|
&& preview.previewText !== agent.previewText;
|
|
5369
5403
|
agent.previewText = preview.previewText || agent.previewText;
|
|
5370
5404
|
agent.previewCols = preview.cols || agent.previewCols;
|
|
5371
5405
|
agent.previewRows = preview.rows || agent.previewRows;
|
|
5372
|
-
|
|
5406
|
+
// A null preview is not a completed new frame. Retain the last
|
|
5407
|
+
// authoritative snapshot until a replacement is ready.
|
|
5408
|
+
agent.previewSnapshot = preview.previewSnapshot || agent.previewSnapshot || null;
|
|
5373
5409
|
if (preview.terminalStatus) agent.terminalStatus = preview.terminalStatus;
|
|
5374
5410
|
if (preview.runtimeObservation) agent.runtimeObservation = preview.runtimeObservation;
|
|
5375
5411
|
if (isCrtSessionOpen()) dashboardRenderDeferred = true;
|
|
5376
|
-
else scheduleCrtPreviewCardRender(agent,
|
|
5412
|
+
else scheduleCrtPreviewCardRender(agent, previewChanged);
|
|
5377
5413
|
}
|
|
5378
5414
|
}
|
|
5379
5415
|
} else if (data.type === 'session-output') {
|
|
@@ -5404,7 +5440,8 @@ function connect() {
|
|
|
5404
5440
|
const agent = activity && state && state.agents.find((candidate) => candidate.id === activity.agentId);
|
|
5405
5441
|
if (agent) {
|
|
5406
5442
|
Object.assign(agent, activity);
|
|
5407
|
-
|
|
5443
|
+
if (isCrtSessionOpen()) dashboardRenderDeferred = true;
|
|
5444
|
+
else scheduleCrtPreviewCardRender(agent);
|
|
5408
5445
|
}
|
|
5409
5446
|
} else if (data.type === 'system-stats') {
|
|
5410
5447
|
updateSystemStats(data.stats, data.uptime, data.usageRate);
|
|
@@ -5420,6 +5457,7 @@ function connect() {
|
|
|
5420
5457
|
socket.onclose = () => {
|
|
5421
5458
|
if (ws !== socket) return;
|
|
5422
5459
|
ws = null;
|
|
5460
|
+
getSessionClient()?.rejectPendingComposerMessages();
|
|
5423
5461
|
console.log('Disconnected from server');
|
|
5424
5462
|
if (crtTerminalReplication) {
|
|
5425
5463
|
clearPendingCrtTerminalFitResize(crtTerminalReplication);
|
|
@@ -5842,7 +5880,6 @@ function structuredRuntimeKind(agent) {
|
|
|
5842
5880
|
const kind = agent && agent.runtimeBinding && agent.runtimeBinding.kind;
|
|
5843
5881
|
if (kind === 'acp') return 'ACP';
|
|
5844
5882
|
if (kind === 'json') return 'JSON';
|
|
5845
|
-
if (kind === 'app-server') return 'APP SERVER';
|
|
5846
5883
|
return '';
|
|
5847
5884
|
}
|
|
5848
5885
|
|
|
@@ -5868,6 +5905,19 @@ function canSwitchCrtAgentRuntime(agent) {
|
|
|
5868
5905
|
);
|
|
5869
5906
|
}
|
|
5870
5907
|
|
|
5908
|
+
function crtRuntimeSwitchUnavailableReason(agent) {
|
|
5909
|
+
if (!agent || !agent.providerCapabilities?.runtimeSwitch || !agent.providerCapabilities?.supportsChat) {
|
|
5910
|
+
return '';
|
|
5911
|
+
}
|
|
5912
|
+
if (agent.providerSessionTemporary === true && agent.terminalInputReceived === true) {
|
|
5913
|
+
return 'MSG unavailable: this temporary Terminal session already has input and cannot be resumed safely.';
|
|
5914
|
+
}
|
|
5915
|
+
if (!String(agent.providerSessionId || '').trim()) {
|
|
5916
|
+
return 'MSG unavailable: this Agent has no resumable provider session yet.';
|
|
5917
|
+
}
|
|
5918
|
+
return '';
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5871
5921
|
function isCrtRuntimeSwitchShortcut(event) {
|
|
5872
5922
|
return Boolean(
|
|
5873
5923
|
event
|
|
@@ -5931,11 +5981,27 @@ function updateCrtRuntimeSwitchControl(agent) {
|
|
|
5931
5981
|
const terminalButton = document.getElementById('crt-runtime-terminal');
|
|
5932
5982
|
if (!control || !chatButton || !terminalButton) return;
|
|
5933
5983
|
const supported = canSwitchCrtAgentRuntime(agent);
|
|
5984
|
+
const unavailableReason = crtRuntimeSwitchUnavailableReason(agent);
|
|
5934
5985
|
const view = crtRuntimeView(agent);
|
|
5935
|
-
control
|
|
5986
|
+
// Keep a disabled control visible for a provider that supports Chat when
|
|
5987
|
+
// this particular session cannot be resumed. Hiding it made a live
|
|
5988
|
+
// Terminal look like a product capability was missing, while silently
|
|
5989
|
+
// enabling it would risk replacing a temporary session and losing input.
|
|
5990
|
+
control.hidden = !supported && !unavailableReason;
|
|
5936
5991
|
control.setAttribute('aria-busy', runtimeSwitchPending ? 'true' : 'false');
|
|
5937
5992
|
chatButton.disabled = !supported || runtimeSwitchPending;
|
|
5938
5993
|
terminalButton.disabled = !supported || runtimeSwitchPending;
|
|
5994
|
+
if (unavailableReason) {
|
|
5995
|
+
control.title = unavailableReason;
|
|
5996
|
+
control.setAttribute('aria-label', unavailableReason);
|
|
5997
|
+
chatButton.title = unavailableReason;
|
|
5998
|
+
terminalButton.title = unavailableReason;
|
|
5999
|
+
} else {
|
|
6000
|
+
control.removeAttribute('title');
|
|
6001
|
+
control.removeAttribute('aria-label');
|
|
6002
|
+
chatButton.title = 'Chat view [Alt+M]';
|
|
6003
|
+
terminalButton.title = 'Terminal view [Alt+M]';
|
|
6004
|
+
}
|
|
5939
6005
|
chatButton.classList.toggle('active', view === 'chat');
|
|
5940
6006
|
terminalButton.classList.toggle('active', view === 'terminal');
|
|
5941
6007
|
chatButton.setAttribute('aria-pressed', view === 'chat' ? 'true' : 'false');
|
|
@@ -5973,7 +6039,7 @@ async function switchCrtSessionRuntimeMode(mode) {
|
|
|
5973
6039
|
if (runtimeSwitchPending || !focusedAgentId || !state) return;
|
|
5974
6040
|
const agent = state.agents.find((candidate) => candidate.id === focusedAgentId);
|
|
5975
6041
|
if (!canSwitchCrtAgentRuntime(agent)) return;
|
|
5976
|
-
const targetMode = mode === 'terminal' ? 'terminal' : '
|
|
6042
|
+
const targetMode = mode === 'terminal' ? 'terminal' : 'chat';
|
|
5977
6043
|
if (crtRuntimeView(agent) === (targetMode === 'terminal' ? 'terminal' : 'chat')) return;
|
|
5978
6044
|
|
|
5979
6045
|
const requestSequence = ++runtimeSwitchRequestSequence;
|
|
@@ -6006,14 +6072,17 @@ async function switchCrtSessionRuntimeMode(mode) {
|
|
|
6006
6072
|
function toggleCrtSessionRuntimeMode() {
|
|
6007
6073
|
if (!focusedAgentId || !state) return;
|
|
6008
6074
|
const agent = state.agents.find((candidate) => candidate.id === focusedAgentId);
|
|
6009
|
-
if (!canSwitchCrtAgentRuntime(agent))
|
|
6010
|
-
|
|
6075
|
+
if (!canSwitchCrtAgentRuntime(agent)) {
|
|
6076
|
+
const reason = crtRuntimeSwitchUnavailableReason(agent);
|
|
6077
|
+
if (reason) setCrtRuntimeSwitchStatus(reason, true);
|
|
6078
|
+
return;
|
|
6079
|
+
}
|
|
6080
|
+
void switchCrtSessionRuntimeMode(crtRuntimeView(agent) === 'chat' ? 'terminal' : 'chat');
|
|
6011
6081
|
}
|
|
6012
6082
|
|
|
6013
6083
|
function structuredTranscriptEndpoint(agent) {
|
|
6014
6084
|
if (agent.runtimeBinding.kind === 'acp') return 'acp-transcript';
|
|
6015
|
-
|
|
6016
|
-
return 'codex-app-server-transcript';
|
|
6085
|
+
return 'json-cli-transcript';
|
|
6017
6086
|
}
|
|
6018
6087
|
|
|
6019
6088
|
function structuredRuntimeStatus(agent) {
|
|
@@ -6033,7 +6102,6 @@ function structuredComposerAction(agent, draft = '') {
|
|
|
6033
6102
|
const working = ['working', 'waiting-for-permission'].includes(status);
|
|
6034
6103
|
if (working) {
|
|
6035
6104
|
if (agent.runtimeBinding?.kind === 'acp' && String(draft || '').trim()) return 'send';
|
|
6036
|
-
if (agent.runtimeBinding?.kind === 'app-server' && String(draft || '').trim()) return 'steer';
|
|
6037
6105
|
return 'interrupt';
|
|
6038
6106
|
}
|
|
6039
6107
|
if (['starting', 'interrupting'].includes(status)) return 'disabled';
|
|
@@ -6042,9 +6110,9 @@ function structuredComposerAction(agent, draft = '') {
|
|
|
6042
6110
|
: 'disabled';
|
|
6043
6111
|
}
|
|
6044
6112
|
|
|
6045
|
-
function queueStructuredComposerFollowUp(agentId, message) {
|
|
6113
|
+
function queueStructuredComposerFollowUp(agentId, message, attachments = []) {
|
|
6046
6114
|
const queue = structuredComposerPendingFollowUps.get(agentId) || [];
|
|
6047
|
-
queue.push(message);
|
|
6115
|
+
queue.push({ message, attachments });
|
|
6048
6116
|
structuredComposerPendingFollowUps.set(agentId, queue);
|
|
6049
6117
|
}
|
|
6050
6118
|
|
|
@@ -6052,10 +6120,10 @@ function flushStructuredComposerFollowUp(agent) {
|
|
|
6052
6120
|
if (!agent || structuredRuntimeStatus(agent) !== 'idle') return;
|
|
6053
6121
|
const queue = structuredComposerPendingFollowUps.get(agent.id);
|
|
6054
6122
|
if (!queue || queue.length === 0) return;
|
|
6055
|
-
const
|
|
6123
|
+
const next = queue.shift();
|
|
6056
6124
|
if (queue.length === 0) structuredComposerPendingFollowUps.delete(agent.id);
|
|
6057
|
-
if (!getSessionClient()?.sendComposerMessage(agent.id, message)) {
|
|
6058
|
-
queue.unshift(
|
|
6125
|
+
if (!getSessionClient()?.sendComposerMessage(agent.id, next.message, next.attachments)) {
|
|
6126
|
+
queue.unshift(next);
|
|
6059
6127
|
structuredComposerPendingFollowUps.set(agent.id, queue);
|
|
6060
6128
|
return;
|
|
6061
6129
|
}
|
|
@@ -6481,9 +6549,7 @@ function updateStructuredComposerState(agent) {
|
|
|
6481
6549
|
}
|
|
6482
6550
|
sendButton.disabled = action === 'disabled';
|
|
6483
6551
|
sendButton.dataset.action = action;
|
|
6484
|
-
sendButton.textContent = action === 'interrupt'
|
|
6485
|
-
? 'BREAK [ESC]'
|
|
6486
|
-
: action === 'steer' ? 'STEER [ENTER]' : 'SEND [ENTER]';
|
|
6552
|
+
sendButton.textContent = action === 'interrupt' ? 'BREAK [ESC]' : 'SEND [ENTER]';
|
|
6487
6553
|
input.placeholder = error
|
|
6488
6554
|
? 'Session unavailable'
|
|
6489
6555
|
: (busy ? 'Agent is changing state...' : 'Type a message...');
|
|
@@ -6542,6 +6608,7 @@ function readStructuredTextFile(file) {
|
|
|
6542
6608
|
async function prepareStructuredAttachment(file) {
|
|
6543
6609
|
const attachment = {
|
|
6544
6610
|
id: structuredAttachmentId(file),
|
|
6611
|
+
kind: String(file.type || '').startsWith('audio/') ? 'audio' : 'image',
|
|
6545
6612
|
name: file.name || 'attachment',
|
|
6546
6613
|
status: 'uploading',
|
|
6547
6614
|
messageBlock: ''
|
|
@@ -6549,15 +6616,21 @@ async function prepareStructuredAttachment(file) {
|
|
|
6549
6616
|
structuredComposerAttachments.push(attachment);
|
|
6550
6617
|
renderStructuredComposerAttachments();
|
|
6551
6618
|
try {
|
|
6552
|
-
if (String(file.type || '').startsWith('image/')) {
|
|
6553
|
-
const
|
|
6619
|
+
if (String(file.type || '').startsWith('image/') || String(file.type || '').startsWith('audio/')) {
|
|
6620
|
+
const image = attachment.kind === 'image';
|
|
6621
|
+
const response = await fetch(farmingApiPath(image ? '/attachments/image' : '/attachments/audio'), {
|
|
6554
6622
|
method: 'POST',
|
|
6555
|
-
headers: { 'Content-Type': file.type || 'image/png' },
|
|
6623
|
+
headers: { 'Content-Type': file.type || (image ? 'image/png' : 'audio/wav') },
|
|
6556
6624
|
body: file
|
|
6557
6625
|
});
|
|
6558
6626
|
const body = await response.json().catch(() => null);
|
|
6559
|
-
if (!response.ok || !body || !body.path) throw new Error(
|
|
6560
|
-
attachment.
|
|
6627
|
+
if (!response.ok || !body || !body.path) throw new Error(`${image ? 'Image' : 'Audio'} upload failed (${response.status})`);
|
|
6628
|
+
attachment.path = body.path;
|
|
6629
|
+
attachment.type = body.type || file.type || 'image/png';
|
|
6630
|
+
attachment.size = Number(body.size) || file.size || 0;
|
|
6631
|
+
attachment.messageBlock = image
|
|
6632
|
+
? `Attached image: ${body.name || attachment.name}\n\nImage path: ${body.path}`
|
|
6633
|
+
: `Attached audio: ${body.name || attachment.name}\n\nAudio path: ${body.path}`;
|
|
6561
6634
|
} else {
|
|
6562
6635
|
const content = await readStructuredTextFile(file);
|
|
6563
6636
|
const limit = 50000;
|
|
@@ -6581,11 +6654,25 @@ function addStructuredAttachmentFiles(files) {
|
|
|
6581
6654
|
|
|
6582
6655
|
function structuredComposerMessage(draft) {
|
|
6583
6656
|
return structuredComposerAttachments
|
|
6584
|
-
.filter((attachment) => attachment.status === 'ready' && attachment.messageBlock)
|
|
6657
|
+
.filter((attachment) => attachment.status === 'ready' && attachment.messageBlock && !attachment.path)
|
|
6585
6658
|
.reduce((message, attachment) => structuredAttachmentBlock(message, attachment.messageBlock), String(draft || ''))
|
|
6586
6659
|
.trim();
|
|
6587
6660
|
}
|
|
6588
6661
|
|
|
6662
|
+
function structuredComposerPromptAttachments() {
|
|
6663
|
+
return structuredComposerAttachments.flatMap((attachment) => (
|
|
6664
|
+
attachment.status === 'ready' && attachment.path
|
|
6665
|
+
? [{
|
|
6666
|
+
kind: attachment.kind,
|
|
6667
|
+
path: attachment.path,
|
|
6668
|
+
name: attachment.name,
|
|
6669
|
+
type: attachment.type,
|
|
6670
|
+
size: attachment.size,
|
|
6671
|
+
}]
|
|
6672
|
+
: []
|
|
6673
|
+
));
|
|
6674
|
+
}
|
|
6675
|
+
|
|
6589
6676
|
function structuredComposerHistoryFor(agentId) {
|
|
6590
6677
|
if (!agentId) return [];
|
|
6591
6678
|
return structuredComposerHistory.get(agentId) || [];
|
|
@@ -7013,11 +7100,11 @@ function setupStructuredSessionComposer() {
|
|
|
7013
7100
|
updateStructuredComposerState(agent);
|
|
7014
7101
|
});
|
|
7015
7102
|
input.addEventListener('paste', (event) => {
|
|
7016
|
-
const
|
|
7017
|
-
.filter((file) => String(file.type || '')
|
|
7018
|
-
if (!
|
|
7103
|
+
const mediaFiles = Array.from(event.clipboardData && event.clipboardData.files || [])
|
|
7104
|
+
.filter((file) => /^(image|audio)\//.test(String(file.type || '')));
|
|
7105
|
+
if (!mediaFiles.length) return;
|
|
7019
7106
|
event.preventDefault();
|
|
7020
|
-
addStructuredAttachmentFiles(
|
|
7107
|
+
addStructuredAttachmentFiles(mediaFiles);
|
|
7021
7108
|
});
|
|
7022
7109
|
input.addEventListener('compositionend', () => {
|
|
7023
7110
|
structuredComposerCompositionEndAt = Date.now();
|
|
@@ -7166,11 +7253,25 @@ function setupStructuredSessionComposer() {
|
|
|
7166
7253
|
}
|
|
7167
7254
|
const draft = input.value;
|
|
7168
7255
|
const message = structuredComposerMessage(draft);
|
|
7169
|
-
|
|
7170
|
-
|
|
7171
|
-
? (queueStructuredComposerFollowUp(focusedAgentId, message), true)
|
|
7172
|
-
: getSessionClient()?.sendComposerMessage(focusedAgentId, message);
|
|
7256
|
+
const promptAttachments = structuredComposerPromptAttachments();
|
|
7257
|
+
if (!message && promptAttachments.length === 0) return;
|
|
7173
7258
|
const statusNode = document.getElementById('crt-structured-composer-status');
|
|
7259
|
+
const completeSubmission = () => {
|
|
7260
|
+
addStructuredComposerHistory(focusedAgentId, draft);
|
|
7261
|
+
input.value = '';
|
|
7262
|
+
structuredComposerAttachments = [];
|
|
7263
|
+
renderStructuredComposerAttachments();
|
|
7264
|
+
resizeStructuredComposerInput(input);
|
|
7265
|
+
if (statusNode) {
|
|
7266
|
+
statusNode.textContent = 'SENDING...';
|
|
7267
|
+
statusNode.classList.remove('error');
|
|
7268
|
+
}
|
|
7269
|
+
updateStructuredComposerState(agent);
|
|
7270
|
+
setTimeout(() => void refreshStructuredSession(focusedAgentId, true), 160);
|
|
7271
|
+
};
|
|
7272
|
+
const sent = action === 'send' && structuredRuntimeStatus(agent) !== 'idle'
|
|
7273
|
+
? (queueStructuredComposerFollowUp(focusedAgentId, message, promptAttachments), true)
|
|
7274
|
+
: getSessionClient()?.sendComposerMessage(focusedAgentId, message, promptAttachments);
|
|
7174
7275
|
if (!sent) {
|
|
7175
7276
|
if (statusNode) {
|
|
7176
7277
|
statusNode.textContent = 'Connection unavailable';
|
|
@@ -7178,17 +7279,7 @@ function setupStructuredSessionComposer() {
|
|
|
7178
7279
|
}
|
|
7179
7280
|
return;
|
|
7180
7281
|
}
|
|
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);
|
|
7282
|
+
completeSubmission();
|
|
7192
7283
|
});
|
|
7193
7284
|
}
|
|
7194
7285
|
|
|
@@ -7536,7 +7627,11 @@ function clearPendingCrtTerminalFitResize(replication) {
|
|
|
7536
7627
|
replication.pendingFitResize = null;
|
|
7537
7628
|
}
|
|
7538
7629
|
|
|
7539
|
-
function commitCrtTerminalResize(
|
|
7630
|
+
function commitCrtTerminalResize(
|
|
7631
|
+
agentId,
|
|
7632
|
+
normalizedDimensions,
|
|
7633
|
+
{ allowDuringRecovery = false } = {}
|
|
7634
|
+
) {
|
|
7540
7635
|
const replication = crtTerminalReplication;
|
|
7541
7636
|
if (
|
|
7542
7637
|
!agentId ||
|
|
@@ -7544,7 +7639,7 @@ function commitCrtTerminalResize(agentId, normalizedDimensions) {
|
|
|
7544
7639
|
!replication ||
|
|
7545
7640
|
replication.disposed ||
|
|
7546
7641
|
replication.agentId !== agentId ||
|
|
7547
|
-
replication.replayState.recovering ||
|
|
7642
|
+
(!allowDuringRecovery && replication.replayState.recovering) ||
|
|
7548
7643
|
(
|
|
7549
7644
|
normalizedDimensions.cols === replication.lastResizeCols &&
|
|
7550
7645
|
normalizedDimensions.rows === replication.lastResizeRows
|
|
@@ -7586,7 +7681,11 @@ function scheduleCrtTerminalFitResize(replication, agentId, normalizedDimensions
|
|
|
7586
7681
|
}, CRT_TERMINAL_RESIZE_SETTLE_MS);
|
|
7587
7682
|
}
|
|
7588
7683
|
|
|
7589
|
-
function sendSessionResize(
|
|
7684
|
+
function sendSessionResize(
|
|
7685
|
+
agentId = focusedAgentId,
|
|
7686
|
+
requestedDimensions = null,
|
|
7687
|
+
{ forceDuringRecovery = false } = {}
|
|
7688
|
+
) {
|
|
7590
7689
|
const replication = crtTerminalReplication;
|
|
7591
7690
|
if (
|
|
7592
7691
|
!agentId ||
|
|
@@ -7594,7 +7693,7 @@ function sendSessionResize(agentId = focusedAgentId, requestedDimensions = null)
|
|
|
7594
7693
|
!fitAddon ||
|
|
7595
7694
|
!replication ||
|
|
7596
7695
|
replication.agentId !== agentId ||
|
|
7597
|
-
replication.replayState.recovering
|
|
7696
|
+
(!forceDuringRecovery && replication.replayState.recovering)
|
|
7598
7697
|
) return;
|
|
7599
7698
|
const dimensions = requestedDimensions || (
|
|
7600
7699
|
typeof fitAddon.proposeDimensions === 'function'
|
|
@@ -7618,6 +7717,25 @@ function sendSessionResize(agentId = focusedAgentId, requestedDimensions = null)
|
|
|
7618
7717
|
normalizedDimensions.rows === replication.lastResizeRows
|
|
7619
7718
|
) {
|
|
7620
7719
|
clearPendingCrtTerminalFitResize(replication);
|
|
7720
|
+
// A checkpoint can temporarily restore the previous viewer's geometry
|
|
7721
|
+
// after this surface's resize was already delivered. Reapply the known
|
|
7722
|
+
// local geometry without sending a duplicate resize to the backend.
|
|
7723
|
+
if (
|
|
7724
|
+
terminal.cols !== normalizedDimensions.cols ||
|
|
7725
|
+
terminal.rows !== normalizedDimensions.rows
|
|
7726
|
+
) {
|
|
7727
|
+
replication.applyingLocalResize = true;
|
|
7728
|
+
try {
|
|
7729
|
+
terminal.resize(normalizedDimensions.cols, normalizedDimensions.rows);
|
|
7730
|
+
} finally {
|
|
7731
|
+
replication.applyingLocalResize = false;
|
|
7732
|
+
}
|
|
7733
|
+
}
|
|
7734
|
+
return;
|
|
7735
|
+
}
|
|
7736
|
+
if (forceDuringRecovery) {
|
|
7737
|
+
clearPendingCrtTerminalFitResize(replication);
|
|
7738
|
+
commitCrtTerminalResize(agentId, normalizedDimensions, { allowDuringRecovery: true });
|
|
7621
7739
|
return;
|
|
7622
7740
|
}
|
|
7623
7741
|
scheduleCrtTerminalFitResize(replication, agentId, normalizedDimensions);
|