farming-code 2.2.19 → 2.2.22
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 +5 -5
- package/README.zh_cn.md +5 -5
- package/THIRD_PARTY_NOTICES.md +5 -8
- package/backend/acp/client-services.js +37 -5
- package/backend/acp/patch-decisions.js +19 -3
- package/backend/acp-runtime.js +1201 -198
- package/backend/acp-transcript.js +66 -0
- package/backend/agent-lifecycle-journal.js +133 -0
- package/backend/agent-manager.js +2715 -354
- package/backend/agent-order.js +8 -4
- package/backend/agent-runtime-binding.js +1 -0
- package/backend/bundle-update-helper.js +5 -1
- package/backend/config-manager.js +16 -0
- package/backend/control-api.js +146 -20
- package/backend/farming-app-cli.js +335 -10
- package/backend/farming-session-store.js +283 -36
- package/backend/json-cli-runtime.js +120 -6
- package/backend/npm-update-helper.js +97 -18
- package/backend/provider-adapters.js +18 -6
- package/backend/review-session-service.js +31 -17
- package/backend/server.js +337 -103
- package/backend/storage-layout.js +10 -0
- package/backend/terminal-screen-worker-pool.js +15 -6
- package/backend/theme-manager.js +23 -5
- package/backend/update-service.js +239 -27
- package/backend/usage-monitor.js +11 -0
- package/backend/workspace-file-router.js +9 -3
- package/backend/workspace-file-service.js +292 -62
- package/dist/acp/codex-acp-1.1.4.js +32 -1
- package/dist/assets/App-CmZdNavW.js +210 -0
- package/dist/assets/{FileEditorMarkdownPreview-R0jwwLME.js → FileEditorMarkdownPreview-KbnE8VC8.js} +1 -1
- package/dist/assets/FileEditorPane-5_HUejjd.js +2 -0
- package/dist/assets/IconGlyphs-C2WFcoJS.js +1 -0
- package/dist/assets/ProjectFilesSection-C1RAJWOE.js +12 -0
- package/dist/assets/{ReviewPage-DBKNOWsJ.js → ReviewPage-L1vh1Ggs.js} +3 -3
- package/dist/assets/code-dark-BKtvf0Xc.css +1 -0
- package/dist/assets/file-icons-CHWt5nSy.js +1 -0
- package/dist/assets/{index-DQdtPfCk.js → index-RvUiMpGP.js} +2 -2
- package/dist/assets/main-XMXgyJJt.css +1 -0
- package/dist/assets/workspace-view-state-BqB4HM7r.js +7 -0
- package/dist/index.html +1 -1
- package/frontend/skins/crt/app.js +365 -107
- package/frontend/skins/crt/index.html +5 -10
- package/frontend/skins/crt/styles/billing.css +6 -1
- package/package.json +9 -3
- package/dist/assets/App-CbXYBccJ.js +0 -210
- package/dist/assets/FileEditorPane-CJs6kfZO.js +0 -2
- package/dist/assets/IconGlyphs-CZzDr5o2.js +0 -1
- package/dist/assets/ProjectFilesSection-DWCV07lA.js +0 -12
- package/dist/assets/code-dark-qIBiLBWV.css +0 -1
- package/dist/assets/file-icons-DpbrD5ZP.js +0 -1
- package/dist/assets/main-yzZEfgft.css +0 -1
- package/dist/assets/workspace-view-state-BpPINu84.js +0 -7
|
@@ -55,6 +55,9 @@ let billingRequestSequence = 0;
|
|
|
55
55
|
let billingAbortController = null;
|
|
56
56
|
let billingRefreshTimer = null;
|
|
57
57
|
let billingLiveDayRefreshTimer = null;
|
|
58
|
+
let crtTokenRateRefreshTimer = null;
|
|
59
|
+
let crtTokenRateFirstLoadTimer = null;
|
|
60
|
+
let crtTokenRateAbortController = null;
|
|
58
61
|
let billingCanvasFrame = null;
|
|
59
62
|
let billingMode = 'days';
|
|
60
63
|
let billingSelectedDate = '';
|
|
@@ -86,6 +89,7 @@ const crtStructuredPreviewTimers = new Map();
|
|
|
86
89
|
let sessionRuntime = null;
|
|
87
90
|
let structuredSessionPoller = null;
|
|
88
91
|
let structuredSessionLoading = false;
|
|
92
|
+
let structuredSessionGeneration = 0;
|
|
89
93
|
let structuredSessionRenderedAt = '';
|
|
90
94
|
let structuredSessionSnapshot = null;
|
|
91
95
|
let structuredSessionControlsLoading = false;
|
|
@@ -104,6 +108,8 @@ let runtimeSwitchPending = false;
|
|
|
104
108
|
let pendingRuntimeSwitchAgentId = '';
|
|
105
109
|
let runtimeSwitchRequestSequence = 0;
|
|
106
110
|
let crtTerminalReplication = null;
|
|
111
|
+
const globalSettingsSaveRevisions = new Map();
|
|
112
|
+
let globalSettingsSaveTail = Promise.resolve();
|
|
107
113
|
const terminalPreviewSnapshots = new Map();
|
|
108
114
|
const crtBrandPulseTimers = new Map();
|
|
109
115
|
const SESSION_LINK_LIMIT = 6;
|
|
@@ -116,13 +122,16 @@ const CRT_AGENT_GRID_GAP = 15;
|
|
|
116
122
|
const CRT_AGENT_GRID_PADDING = 20;
|
|
117
123
|
const CRT_SEARCH_DEBOUNCE_MS = 180;
|
|
118
124
|
const CRT_SEARCH_RESULT_LIMIT = 100;
|
|
119
|
-
const CRT_BILLING_REFRESH_MS =
|
|
125
|
+
const CRT_BILLING_REFRESH_MS = 15_000;
|
|
120
126
|
const CRT_BILLING_LIVE_DAY_REFRESH_MS = 5_000;
|
|
127
|
+
const CRT_TOKEN_RATE_REFRESH_MS = 60_000;
|
|
128
|
+
const CRT_TOKEN_RATE_FIRST_LOAD_MS = 1_500;
|
|
121
129
|
const CRT_BILLING_DAY_DETAIL_CACHE_MS = 30_000;
|
|
122
130
|
const CRT_BILLING_TOTAL_ANIMATION_MS = 900;
|
|
123
131
|
const CRT_BILLING_DAY_DETAIL_RETRY_MS = 750;
|
|
124
132
|
const CRT_BILLING_DAY_DETAIL_MAX_RETRIES = 4;
|
|
125
133
|
const CRT_TERMINAL_CHECKPOINT_REQUEST_TIMEOUT_MS = 5_000;
|
|
134
|
+
const CRT_SETTINGS_REQUEST_TIMEOUT_MS = 15_000;
|
|
126
135
|
const CRT_TERMINAL_RESIZE_SETTLE_MS = 250;
|
|
127
136
|
const CRT_TERMINAL_MIN_COLS = 40;
|
|
128
137
|
const CRT_TERMINAL_MIN_ROWS = 10;
|
|
@@ -844,8 +853,14 @@ function fallbackCopyText(text) {
|
|
|
844
853
|
return copied;
|
|
845
854
|
}
|
|
846
855
|
|
|
847
|
-
|
|
848
|
-
if (!
|
|
856
|
+
function isCurrentCrtSession(agentId, sessionToken = null) {
|
|
857
|
+
if (!agentId || focusedAgentId !== agentId) return false;
|
|
858
|
+
const runtime = getSessionRuntime();
|
|
859
|
+
return sessionToken === null || !runtime || runtime.isCurrentSession(agentId, sessionToken);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
async function pasteTerminalText(text, agentId = focusedAgentId, sessionToken = null) {
|
|
863
|
+
if (!text || !isCurrentCrtSession(agentId, sessionToken)) {
|
|
849
864
|
return false;
|
|
850
865
|
}
|
|
851
866
|
|
|
@@ -872,9 +887,13 @@ async function pasteFromClipboard() {
|
|
|
872
887
|
return false;
|
|
873
888
|
}
|
|
874
889
|
|
|
890
|
+
const agentId = focusedAgentId;
|
|
891
|
+
const runtime = getSessionRuntime();
|
|
892
|
+
const sessionToken = runtime ? runtime.getSessionToken() : null;
|
|
893
|
+
if (!agentId) return false;
|
|
875
894
|
try {
|
|
876
895
|
const text = await navigator.clipboard.readText();
|
|
877
|
-
return pasteTerminalText(text);
|
|
896
|
+
return pasteTerminalText(text, agentId, sessionToken);
|
|
878
897
|
} catch (error) {
|
|
879
898
|
console.warn('Clipboard API paste failed:', error);
|
|
880
899
|
return false;
|
|
@@ -2787,9 +2806,19 @@ async function loadThemes() {
|
|
|
2787
2806
|
}
|
|
2788
2807
|
}
|
|
2789
2808
|
|
|
2809
|
+
async function fetchCrtSettingsRequest(url, init) {
|
|
2810
|
+
const controller = new window.AbortController();
|
|
2811
|
+
const timeout = setTimeout(() => controller.abort(), CRT_SETTINGS_REQUEST_TIMEOUT_MS);
|
|
2812
|
+
try {
|
|
2813
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2814
|
+
} finally {
|
|
2815
|
+
clearTimeout(timeout);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
|
|
2790
2819
|
async function loadGlobalSettings() {
|
|
2791
2820
|
try {
|
|
2792
|
-
const response = await
|
|
2821
|
+
const response = await fetchCrtSettingsRequest(farmingApiPath('/settings'));
|
|
2793
2822
|
const data = await response.json();
|
|
2794
2823
|
globalSettings = {
|
|
2795
2824
|
...globalSettings,
|
|
@@ -2808,31 +2837,48 @@ async function loadGlobalSettings() {
|
|
|
2808
2837
|
}
|
|
2809
2838
|
}
|
|
2810
2839
|
|
|
2811
|
-
async function saveGlobalSettings() {
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2840
|
+
async function saveGlobalSettings(settingsPatch) {
|
|
2841
|
+
const patch = settingsPatch && typeof settingsPatch === 'object'
|
|
2842
|
+
? { ...settingsPatch }
|
|
2843
|
+
: {};
|
|
2844
|
+
const keys = Object.keys(patch);
|
|
2845
|
+
if (keys.length === 0) return;
|
|
2846
|
+
const revisions = new Map(keys.map((key) => {
|
|
2847
|
+
const revision = (globalSettingsSaveRevisions.get(key) || 0) + 1;
|
|
2848
|
+
globalSettingsSaveRevisions.set(key, revision);
|
|
2849
|
+
return [key, revision];
|
|
2850
|
+
}));
|
|
2851
|
+
const save = globalSettingsSaveTail.catch(() => {}).then(async () => {
|
|
2852
|
+
try {
|
|
2853
|
+
const response = await fetchCrtSettingsRequest(farmingApiPath('/settings'), {
|
|
2854
|
+
method: 'POST',
|
|
2855
|
+
headers: {
|
|
2856
|
+
'Content-Type': 'application/json'
|
|
2857
|
+
},
|
|
2858
|
+
body: JSON.stringify(patch)
|
|
2859
|
+
});
|
|
2821
2860
|
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2861
|
+
const data = await response.json();
|
|
2862
|
+
if (data.success) {
|
|
2863
|
+
const savedSettings = data.settings || {};
|
|
2864
|
+
keys.forEach((key) => {
|
|
2865
|
+
if (globalSettingsSaveRevisions.get(key) !== revisions.get(key)) return;
|
|
2866
|
+
globalSettings[key] = Object.prototype.hasOwnProperty.call(savedSettings, key)
|
|
2867
|
+
? savedSettings[key]
|
|
2868
|
+
: patch[key];
|
|
2869
|
+
});
|
|
2870
|
+
if (globalSettings.instanceName) {
|
|
2871
|
+
document.title = `${globalSettings.instanceName} · Farming CRT`;
|
|
2872
|
+
}
|
|
2873
|
+
syncWorkspaceSettings();
|
|
2830
2874
|
}
|
|
2831
|
-
|
|
2875
|
+
} catch (error) {
|
|
2876
|
+
console.error('Failed to save global settings:', error);
|
|
2877
|
+
await loadGlobalSettings();
|
|
2832
2878
|
}
|
|
2833
|
-
}
|
|
2834
|
-
|
|
2835
|
-
|
|
2879
|
+
});
|
|
2880
|
+
globalSettingsSaveTail = save;
|
|
2881
|
+
return save;
|
|
2836
2882
|
}
|
|
2837
2883
|
|
|
2838
2884
|
async function setTheme(themeId) {
|
|
@@ -2936,7 +2982,7 @@ function scheduleCrtTerminalFontSizeSave() {
|
|
|
2936
2982
|
if (crtTerminalFontSizeSaveTimer) clearTimeout(crtTerminalFontSizeSaveTimer);
|
|
2937
2983
|
crtTerminalFontSizeSaveTimer = setTimeout(() => {
|
|
2938
2984
|
crtTerminalFontSizeSaveTimer = null;
|
|
2939
|
-
void saveGlobalSettings();
|
|
2985
|
+
void saveGlobalSettings({ crtTerminalFontSize: globalSettings.crtTerminalFontSize });
|
|
2940
2986
|
}, 150);
|
|
2941
2987
|
}
|
|
2942
2988
|
|
|
@@ -2954,7 +3000,7 @@ function initDisplaySettings() {
|
|
|
2954
3000
|
crtToggle.onchange = async () => {
|
|
2955
3001
|
globalSettings.crtSkinEffectsEnabled = crtToggle.checked;
|
|
2956
3002
|
applyCRTEffects(crtToggle.checked);
|
|
2957
|
-
await saveGlobalSettings();
|
|
3003
|
+
await saveGlobalSettings({ crtSkinEffectsEnabled: crtToggle.checked });
|
|
2958
3004
|
};
|
|
2959
3005
|
}
|
|
2960
3006
|
} else {
|
|
@@ -2969,7 +3015,7 @@ function initDisplaySettings() {
|
|
|
2969
3015
|
dynamicHeatToggle.onchange = async () => {
|
|
2970
3016
|
globalSettings.crtDynamicHeatEnabled = dynamicHeatToggle.checked;
|
|
2971
3017
|
renderState();
|
|
2972
|
-
await saveGlobalSettings();
|
|
3018
|
+
await saveGlobalSettings({ crtDynamicHeatEnabled: dynamicHeatToggle.checked });
|
|
2973
3019
|
};
|
|
2974
3020
|
}
|
|
2975
3021
|
|
|
@@ -2989,7 +3035,9 @@ function initSessionEngineSettings() {
|
|
|
2989
3035
|
skipPermissionCheckToggle.checked = globalSettings.dangerouslySkipAgentPermissionsByDefault === true;
|
|
2990
3036
|
skipPermissionCheckToggle.onchange = async () => {
|
|
2991
3037
|
globalSettings.dangerouslySkipAgentPermissionsByDefault = skipPermissionCheckToggle.checked;
|
|
2992
|
-
await saveGlobalSettings(
|
|
3038
|
+
await saveGlobalSettings({
|
|
3039
|
+
dangerouslySkipAgentPermissionsByDefault: skipPermissionCheckToggle.checked,
|
|
3040
|
+
});
|
|
2993
3041
|
};
|
|
2994
3042
|
}
|
|
2995
3043
|
|
|
@@ -3159,6 +3207,7 @@ function setCrtMainView(view) {
|
|
|
3159
3207
|
if (historySidebarItem) historySidebarItem.classList.toggle('active', crtMainView === 'history');
|
|
3160
3208
|
if (searchSidebarItem) searchSidebarItem.classList.toggle('active', crtMainView === 'search');
|
|
3161
3209
|
if (billingSidebarItem) billingSidebarItem.classList.toggle('active', crtMainView === 'billing');
|
|
3210
|
+
if (previousView !== 'agents' && crtMainView === 'agents') checkMainAgentStatus();
|
|
3162
3211
|
}
|
|
3163
3212
|
|
|
3164
3213
|
function getCrtSearchResults() {
|
|
@@ -4006,6 +4055,30 @@ function scrollCrtBillingSelectedDayIntoView() {
|
|
|
4006
4055
|
else if (right > scroll.scrollLeft + scroll.clientWidth) scroll.scrollLeft = right - scroll.clientWidth;
|
|
4007
4056
|
}
|
|
4008
4057
|
|
|
4058
|
+
function crtBillingDayArrowTargetIndex(points, index, key) {
|
|
4059
|
+
if (!Array.isArray(points) || index < 0 || index >= points.length) return -1;
|
|
4060
|
+
const firstDate = parseCrtBillingDate(points[0] && points[0].date);
|
|
4061
|
+
const leadingDays = firstDate ? (firstDate.getDay() + 6) % 7 : 0;
|
|
4062
|
+
const position = leadingDays + index;
|
|
4063
|
+
const weekdayRow = position % 7;
|
|
4064
|
+
let targetPosition = position;
|
|
4065
|
+
if (key === 'ArrowUp') {
|
|
4066
|
+
if (weekdayRow === 0) return -1;
|
|
4067
|
+
targetPosition -= 1;
|
|
4068
|
+
} else if (key === 'ArrowDown') {
|
|
4069
|
+
if (weekdayRow === 6) return -1;
|
|
4070
|
+
targetPosition += 1;
|
|
4071
|
+
} else if (key === 'ArrowLeft') {
|
|
4072
|
+
targetPosition -= 7;
|
|
4073
|
+
} else if (key === 'ArrowRight') {
|
|
4074
|
+
targetPosition += 7;
|
|
4075
|
+
} else {
|
|
4076
|
+
return -1;
|
|
4077
|
+
}
|
|
4078
|
+
const targetIndex = targetPosition - leadingDays;
|
|
4079
|
+
return targetIndex >= 0 && targetIndex < points.length ? targetIndex : -1;
|
|
4080
|
+
}
|
|
4081
|
+
|
|
4009
4082
|
function selectCrtBillingDayByArrow(key) {
|
|
4010
4083
|
if (crtMainView !== 'billing' || billingMode !== 'days') return false;
|
|
4011
4084
|
const points = billingSummary && billingSummary.daily && Array.isArray(billingSummary.daily.points)
|
|
@@ -4014,8 +4087,8 @@ function selectCrtBillingDayByArrow(key) {
|
|
|
4014
4087
|
if (points.length === 0) return false;
|
|
4015
4088
|
let index = points.findIndex(point => point.date === billingSelectedDate);
|
|
4016
4089
|
if (index < 0) index = points.length - 1;
|
|
4017
|
-
const
|
|
4018
|
-
|
|
4090
|
+
const nextIndex = crtBillingDayArrowTargetIndex(points, index, key);
|
|
4091
|
+
if (nextIndex < 0) return false;
|
|
4019
4092
|
return selectCrtBillingDay(points[nextIndex].date, { focus: true });
|
|
4020
4093
|
}
|
|
4021
4094
|
|
|
@@ -4156,11 +4229,35 @@ function setCrtBillingMode(mode) {
|
|
|
4156
4229
|
}
|
|
4157
4230
|
const status = document.getElementById('billing-status');
|
|
4158
4231
|
if (status && billingSummary && !billingLoading && !billingError) {
|
|
4159
|
-
status.textContent =
|
|
4232
|
+
status.textContent = crtBillingStatusText();
|
|
4160
4233
|
}
|
|
4161
4234
|
if (billingMode === 'live') window.requestAnimationFrame(() => drawCrtBillingScope());
|
|
4162
4235
|
}
|
|
4163
4236
|
|
|
4237
|
+
function selectCrtBillingMode(mode) {
|
|
4238
|
+
setCrtBillingMode(mode);
|
|
4239
|
+
const selectedTab = document.getElementById(
|
|
4240
|
+
billingMode === 'live' ? 'billing-live-tab' : 'billing-days-tab',
|
|
4241
|
+
);
|
|
4242
|
+
if (selectedTab) setCrtNavigationSelection(selectedTab);
|
|
4243
|
+
if (billingMode === 'live' && !billingLoading) void loadCrtBilling();
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
function crtBillingTimeline(summary = billingSummary) {
|
|
4247
|
+
return summary && summary.liveTimeline || summary && summary.timeline || null;
|
|
4248
|
+
}
|
|
4249
|
+
|
|
4250
|
+
function crtBillingStatusText(summary = billingSummary) {
|
|
4251
|
+
if (billingMode === 'days') return 'HISTORY READY';
|
|
4252
|
+
const sampledAt = Number(summary && summary.sampledAt);
|
|
4253
|
+
if (!Number.isFinite(sampledAt) || sampledAt <= 0) return 'LIVE';
|
|
4254
|
+
const sampled = new Date(sampledAt);
|
|
4255
|
+
const time = [sampled.getHours(), sampled.getMinutes(), sampled.getSeconds()]
|
|
4256
|
+
.map(value => String(value).padStart(2, '0'))
|
|
4257
|
+
.join(':');
|
|
4258
|
+
return `LIVE ${time}`;
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4164
4261
|
function formatCrtBillingWindow(windowMinutes) {
|
|
4165
4262
|
const minutes = Number(windowMinutes);
|
|
4166
4263
|
if (!Number.isFinite(minutes) || minutes <= 0) return 'WINDOW';
|
|
@@ -4214,6 +4311,13 @@ function crtBillingCurrentRate(summary = billingSummary) {
|
|
|
4214
4311
|
return hasRate ? total : null;
|
|
4215
4312
|
}
|
|
4216
4313
|
|
|
4314
|
+
function renderCrtTopBarTokenRate(summary = billingSummary) {
|
|
4315
|
+
const tokensPerMinute = document.getElementById('tokens-per-minute');
|
|
4316
|
+
if (tokensPerMinute) {
|
|
4317
|
+
tokensPerMinute.textContent = formatCrtTokenRate(crtBillingCurrentRate(summary));
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
|
|
4217
4321
|
function appendCrtBillingMessage(container, text, isError = false) {
|
|
4218
4322
|
const message = document.createElement('div');
|
|
4219
4323
|
message.className = `billing-message${isError ? ' is-error' : ''}`;
|
|
@@ -4314,7 +4418,7 @@ function drawCrtBillingScope(summary = billingSummary) {
|
|
|
4314
4418
|
context.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
4315
4419
|
context.clearRect(0, 0, rect.width, rect.height);
|
|
4316
4420
|
|
|
4317
|
-
const timeline = summary
|
|
4421
|
+
const timeline = crtBillingTimeline(summary);
|
|
4318
4422
|
const points = timeline && Array.isArray(timeline.points) ? timeline.points : [];
|
|
4319
4423
|
const values = points.map(point => Math.max(0, Number(point.tokensPerMinute) || 0));
|
|
4320
4424
|
const providerNames = points.length > 0 ? Object.keys(points[0].providers || {}) : [];
|
|
@@ -4392,7 +4496,7 @@ function renderCrtBilling() {
|
|
|
4392
4496
|
const status = document.getElementById('billing-status');
|
|
4393
4497
|
const refresh = document.getElementById('billing-refresh');
|
|
4394
4498
|
const empty = document.getElementById('billing-scope-empty');
|
|
4395
|
-
const timeline =
|
|
4499
|
+
const timeline = crtBillingTimeline();
|
|
4396
4500
|
const hasSignal = Boolean(timeline && Number(timeline.totalTokens) > 0);
|
|
4397
4501
|
if (status) {
|
|
4398
4502
|
status.classList.toggle('is-busy', billingLoading);
|
|
@@ -4402,7 +4506,7 @@ function renderCrtBilling() {
|
|
|
4402
4506
|
: billingLoading
|
|
4403
4507
|
? (billingSummary ? 'REFRESHING' : 'SCANNING LOGS')
|
|
4404
4508
|
: billingSummary
|
|
4405
|
-
? (
|
|
4509
|
+
? crtBillingStatusText()
|
|
4406
4510
|
: 'STANDBY';
|
|
4407
4511
|
}
|
|
4408
4512
|
if (refresh) refresh.disabled = billingLoading;
|
|
@@ -4449,13 +4553,18 @@ async function loadCrtBilling({ fresh = false } = {}) {
|
|
|
4449
4553
|
billingRequestSequence += 1;
|
|
4450
4554
|
const requestSequence = billingRequestSequence;
|
|
4451
4555
|
if (billingAbortController) billingAbortController.abort();
|
|
4556
|
+
if (crtTokenRateAbortController) {
|
|
4557
|
+
crtTokenRateAbortController.abort();
|
|
4558
|
+
crtTokenRateAbortController = null;
|
|
4559
|
+
}
|
|
4452
4560
|
const controller = new window.AbortController();
|
|
4453
4561
|
billingAbortController = controller;
|
|
4454
4562
|
billingLoading = true;
|
|
4455
4563
|
billingError = '';
|
|
4456
4564
|
renderCrtBilling();
|
|
4457
4565
|
try {
|
|
4458
|
-
const
|
|
4566
|
+
const query = fresh ? '?fresh=1' : billingMode === 'live' ? '?live=1' : '';
|
|
4567
|
+
const response = await fetch(farmingApiPath(`/usage${query}`), {
|
|
4459
4568
|
signal: controller.signal,
|
|
4460
4569
|
cache: 'no-store',
|
|
4461
4570
|
});
|
|
@@ -4463,6 +4572,7 @@ async function loadCrtBilling({ fresh = false } = {}) {
|
|
|
4463
4572
|
if (!response.ok || !data || !data.usage) throw new Error(data && data.error ? data.error : `Usage request failed (${response.status})`);
|
|
4464
4573
|
if (requestSequence !== billingRequestSequence) return;
|
|
4465
4574
|
billingSummary = data.usage;
|
|
4575
|
+
renderCrtTopBarTokenRate();
|
|
4466
4576
|
} catch (error) {
|
|
4467
4577
|
if (controller.signal.aborted || requestSequence !== billingRequestSequence) return;
|
|
4468
4578
|
billingError = error instanceof Error ? error.message : 'Failed to load token telemetry';
|
|
@@ -4481,6 +4591,58 @@ async function loadCrtBilling({ fresh = false } = {}) {
|
|
|
4481
4591
|
}
|
|
4482
4592
|
}
|
|
4483
4593
|
|
|
4594
|
+
async function loadCrtTopBarTokenRate() {
|
|
4595
|
+
if (billingLoading || crtTokenRateAbortController) return;
|
|
4596
|
+
const controller = new window.AbortController();
|
|
4597
|
+
crtTokenRateAbortController = controller;
|
|
4598
|
+
try {
|
|
4599
|
+
const response = await fetch(farmingApiPath('/usage'), {
|
|
4600
|
+
signal: controller.signal,
|
|
4601
|
+
cache: 'no-store',
|
|
4602
|
+
});
|
|
4603
|
+
const data = await response.json().catch(() => null);
|
|
4604
|
+
if (controller.signal.aborted) return;
|
|
4605
|
+
if (!response.ok || !data || !data.usage) {
|
|
4606
|
+
throw new Error(data && data.error ? data.error : `Usage request failed (${response.status})`);
|
|
4607
|
+
}
|
|
4608
|
+
renderCrtTopBarTokenRate(data.usage);
|
|
4609
|
+
} catch {
|
|
4610
|
+
if (!controller.signal.aborted) renderCrtTopBarTokenRate(null);
|
|
4611
|
+
} finally {
|
|
4612
|
+
if (crtTokenRateAbortController === controller) crtTokenRateAbortController = null;
|
|
4613
|
+
}
|
|
4614
|
+
}
|
|
4615
|
+
|
|
4616
|
+
function stopCrtTopBarTokenRateRefresh({ abort = false } = {}) {
|
|
4617
|
+
if (crtTokenRateFirstLoadTimer !== null) {
|
|
4618
|
+
clearTimeout(crtTokenRateFirstLoadTimer);
|
|
4619
|
+
crtTokenRateFirstLoadTimer = null;
|
|
4620
|
+
}
|
|
4621
|
+
if (crtTokenRateRefreshTimer !== null) {
|
|
4622
|
+
clearInterval(crtTokenRateRefreshTimer);
|
|
4623
|
+
crtTokenRateRefreshTimer = null;
|
|
4624
|
+
}
|
|
4625
|
+
if (abort && crtTokenRateAbortController) {
|
|
4626
|
+
crtTokenRateAbortController.abort();
|
|
4627
|
+
crtTokenRateAbortController = null;
|
|
4628
|
+
}
|
|
4629
|
+
}
|
|
4630
|
+
|
|
4631
|
+
function startCrtTopBarTokenRateRefresh({ immediate = false } = {}) {
|
|
4632
|
+
stopCrtTopBarTokenRateRefresh();
|
|
4633
|
+
if (immediate) {
|
|
4634
|
+
void loadCrtTopBarTokenRate();
|
|
4635
|
+
} else {
|
|
4636
|
+
crtTokenRateFirstLoadTimer = setTimeout(() => {
|
|
4637
|
+
crtTokenRateFirstLoadTimer = null;
|
|
4638
|
+
if (document.visibilityState !== 'hidden') void loadCrtTopBarTokenRate();
|
|
4639
|
+
}, CRT_TOKEN_RATE_FIRST_LOAD_MS);
|
|
4640
|
+
}
|
|
4641
|
+
crtTokenRateRefreshTimer = setInterval(() => {
|
|
4642
|
+
if (document.visibilityState !== 'hidden') void loadCrtTopBarTokenRate();
|
|
4643
|
+
}, CRT_TOKEN_RATE_REFRESH_MS);
|
|
4644
|
+
}
|
|
4645
|
+
|
|
4484
4646
|
function stopCrtBillingRefresh({ abort = false } = {}) {
|
|
4485
4647
|
if (billingRefreshTimer !== null) {
|
|
4486
4648
|
clearInterval(billingRefreshTimer);
|
|
@@ -5239,11 +5401,16 @@ async function startCrtAgent(agent, workspaceToUse, asMainAgent) {
|
|
|
5239
5401
|
}
|
|
5240
5402
|
if (JSON.stringify(getWorkspaceHistory()) !== previousHistory) {
|
|
5241
5403
|
refreshWorkspaceMemoryUI();
|
|
5242
|
-
|
|
5404
|
+
syncWorkspaceSettings();
|
|
5405
|
+
await saveGlobalSettings({
|
|
5406
|
+
workspace: globalSettings.workspace,
|
|
5407
|
+
workspaceHistory: globalSettings.workspaceHistory,
|
|
5408
|
+
});
|
|
5243
5409
|
}
|
|
5244
5410
|
|
|
5245
5411
|
ws.send(JSON.stringify({
|
|
5246
5412
|
type: 'start-agent',
|
|
5413
|
+
requestId: globalThis.crypto?.randomUUID?.(),
|
|
5247
5414
|
command: agent.name,
|
|
5248
5415
|
workspace: workspaceToUse,
|
|
5249
5416
|
asMain: asMainAgent,
|
|
@@ -5485,7 +5652,7 @@ function connect() {
|
|
|
5485
5652
|
else scheduleCrtPreviewCardRender(agent);
|
|
5486
5653
|
}
|
|
5487
5654
|
} else if (data.type === 'system-stats') {
|
|
5488
|
-
updateSystemStats(data.stats, data.uptime
|
|
5655
|
+
updateSystemStats(data.stats, data.uptime);
|
|
5489
5656
|
} else if (data.type === 'error') {
|
|
5490
5657
|
waitingForAgent = false;
|
|
5491
5658
|
if (workspaceDirectoryPrompt?.kind === 'creating') {
|
|
@@ -5532,6 +5699,7 @@ function connect() {
|
|
|
5532
5699
|
function suspendCrtPageConnection() {
|
|
5533
5700
|
document.body.classList.add('page-hidden');
|
|
5534
5701
|
stopCrtBillingRefresh({ abort: true });
|
|
5702
|
+
stopCrtTopBarTokenRateRefresh({ abort: true });
|
|
5535
5703
|
if (wsReconnectTimer) {
|
|
5536
5704
|
clearTimeout(wsReconnectTimer);
|
|
5537
5705
|
wsReconnectTimer = null;
|
|
@@ -5545,6 +5713,7 @@ function resumeCrtPageConnection() {
|
|
|
5545
5713
|
if (document.visibilityState === 'hidden') return;
|
|
5546
5714
|
document.body.classList.remove('page-hidden');
|
|
5547
5715
|
connect();
|
|
5716
|
+
startCrtTopBarTokenRateRefresh({ immediate: true });
|
|
5548
5717
|
if (crtMainView === 'billing') {
|
|
5549
5718
|
startCrtBillingRefresh();
|
|
5550
5719
|
void loadCrtBilling();
|
|
@@ -5554,15 +5723,33 @@ function resumeCrtPageConnection() {
|
|
|
5554
5723
|
function checkMainAgentStatus() {
|
|
5555
5724
|
if (!state) return;
|
|
5556
5725
|
|
|
5557
|
-
const
|
|
5558
|
-
|
|
5559
|
-
:
|
|
5560
|
-
|
|
5561
|
-
|
|
5726
|
+
const inputDialog = document.getElementById('input-dialog');
|
|
5727
|
+
const action = getCrtMainAgentDialogAction({
|
|
5728
|
+
currentState: state,
|
|
5729
|
+
mainView: crtMainView,
|
|
5730
|
+
dialogActive: Boolean(inputDialog && inputDialog.classList.contains('active')),
|
|
5731
|
+
pendingMainAgent: pendingMainAgentLaunch,
|
|
5732
|
+
});
|
|
5733
|
+
if (action === 'hide') {
|
|
5734
|
+
hideInputDialog();
|
|
5735
|
+
} else if (action === 'show') {
|
|
5562
5736
|
showInputDialog();
|
|
5563
5737
|
}
|
|
5564
5738
|
}
|
|
5565
5739
|
|
|
5740
|
+
function getCrtMainAgentDialogAction({
|
|
5741
|
+
currentState,
|
|
5742
|
+
mainView = 'agents',
|
|
5743
|
+
dialogActive = false,
|
|
5744
|
+
pendingMainAgent = false,
|
|
5745
|
+
} = {}) {
|
|
5746
|
+
if (!needsMainAgent(currentState)) {
|
|
5747
|
+
return dialogActive && pendingMainAgent ? 'hide' : 'none';
|
|
5748
|
+
}
|
|
5749
|
+
if (mainView !== 'agents' || dialogActive) return 'none';
|
|
5750
|
+
return 'show';
|
|
5751
|
+
}
|
|
5752
|
+
|
|
5566
5753
|
function formatSystemClock(timestamp, timeZone) {
|
|
5567
5754
|
if (!Number.isFinite(timestamp)) return '--';
|
|
5568
5755
|
try {
|
|
@@ -5582,21 +5769,22 @@ function formatSystemClock(timestamp, timeZone) {
|
|
|
5582
5769
|
}
|
|
5583
5770
|
|
|
5584
5771
|
function formatCrtTokenRate(value) {
|
|
5772
|
+
if (value === null || value === undefined || value === '') return '--';
|
|
5585
5773
|
const rate = Number(value);
|
|
5586
5774
|
if (!Number.isFinite(rate) || rate < 0) return '--';
|
|
5587
5775
|
const rounded = rate < 10 ? Math.round(rate * 10) / 10 : Math.round(rate);
|
|
5588
5776
|
if (rounded >= 1_000_000) {
|
|
5589
5777
|
const compact = rounded / 1_000_000;
|
|
5590
|
-
return
|
|
5778
|
+
return `${compact >= 10 ? Math.round(compact) : Math.round(compact * 10) / 10}M`;
|
|
5591
5779
|
}
|
|
5592
5780
|
if (rounded >= 1_000) {
|
|
5593
5781
|
const compact = rounded / 1_000;
|
|
5594
|
-
return
|
|
5782
|
+
return `${compact >= 10 ? Math.round(compact) : Math.round(compact * 10) / 10}K`;
|
|
5595
5783
|
}
|
|
5596
|
-
return
|
|
5784
|
+
return String(rounded);
|
|
5597
5785
|
}
|
|
5598
5786
|
|
|
5599
|
-
function updateSystemStats(stats, uptime
|
|
5787
|
+
function updateSystemStats(stats, uptime) {
|
|
5600
5788
|
if (stats.cpu !== undefined) {
|
|
5601
5789
|
document.getElementById('cpu-usage').textContent = stats.cpu;
|
|
5602
5790
|
}
|
|
@@ -5613,11 +5801,6 @@ function updateSystemStats(stats, uptime, usageRate) {
|
|
|
5613
5801
|
document.getElementById('system-time').textContent = formatSystemClock(stats.timestamp, stats.timeZone);
|
|
5614
5802
|
}
|
|
5615
5803
|
|
|
5616
|
-
const tokensPerMinute = document.getElementById('tokens-per-minute');
|
|
5617
|
-
if (tokensPerMinute) {
|
|
5618
|
-
tokensPerMinute.textContent = formatCrtTokenRate(usageRate && usageRate.estimatedTokensPerMinute);
|
|
5619
|
-
}
|
|
5620
|
-
|
|
5621
5804
|
if (uptime !== undefined) {
|
|
5622
5805
|
const hours = Math.floor(uptime / 3600);
|
|
5623
5806
|
const minutes = Math.floor((uptime % 3600) / 60);
|
|
@@ -6168,7 +6351,8 @@ function flushStructuredComposerFollowUp(agent) {
|
|
|
6168
6351
|
structuredComposerPendingFollowUps.set(agent.id, queue);
|
|
6169
6352
|
return;
|
|
6170
6353
|
}
|
|
6171
|
-
|
|
6354
|
+
const generation = structuredSessionGeneration;
|
|
6355
|
+
setTimeout(() => void refreshStructuredSession(agent.id, true, generation), 160);
|
|
6172
6356
|
}
|
|
6173
6357
|
|
|
6174
6358
|
function structuredContextUsage(session) {
|
|
@@ -6243,6 +6427,14 @@ function structuredConfigValueLabel(option) {
|
|
|
6243
6427
|
return selected && selected.name ? selected.name : String(option.currentValue || '');
|
|
6244
6428
|
}
|
|
6245
6429
|
|
|
6430
|
+
function isCurrentStructuredSession(agentId, generation) {
|
|
6431
|
+
return Boolean(
|
|
6432
|
+
agentId
|
|
6433
|
+
&& agentId === focusedAgentId
|
|
6434
|
+
&& generation === structuredSessionGeneration
|
|
6435
|
+
);
|
|
6436
|
+
}
|
|
6437
|
+
|
|
6246
6438
|
function resetStructuredSessionControls() {
|
|
6247
6439
|
structuredSessionSnapshot = null;
|
|
6248
6440
|
structuredSessionControlsLoading = false;
|
|
@@ -6367,19 +6559,28 @@ function backStructuredComposerMenu() {
|
|
|
6367
6559
|
closeStructuredComposerMenu();
|
|
6368
6560
|
}
|
|
6369
6561
|
|
|
6370
|
-
async function patchStructuredAcpSession(patch) {
|
|
6371
|
-
if (!
|
|
6562
|
+
async function patchStructuredAcpSession(agentId, generation, patch) {
|
|
6563
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6372
6564
|
const openerId = structuredComposerMenuOpenerId;
|
|
6373
|
-
|
|
6374
|
-
|
|
6375
|
-
|
|
6376
|
-
|
|
6377
|
-
|
|
6378
|
-
|
|
6565
|
+
let response;
|
|
6566
|
+
let body;
|
|
6567
|
+
try {
|
|
6568
|
+
response = await fetch(farmingApiPath(`/agents/${encodeURIComponent(agentId)}/acp-session`), {
|
|
6569
|
+
method: 'PATCH',
|
|
6570
|
+
headers: { 'Content-Type': 'application/json' },
|
|
6571
|
+
body: JSON.stringify(patch)
|
|
6572
|
+
});
|
|
6573
|
+
body = await response.json().catch(() => null);
|
|
6574
|
+
} catch (error) {
|
|
6575
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6576
|
+
throw error;
|
|
6577
|
+
}
|
|
6578
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6379
6579
|
if (!response.ok) throw new Error(body && body.error ? body.error : `Failed to update ACP session (${response.status})`);
|
|
6380
6580
|
structuredComposerMenu = '';
|
|
6381
6581
|
structuredComposerConfigId = '';
|
|
6382
|
-
await refreshStructuredSessionControls(
|
|
6582
|
+
await refreshStructuredSessionControls(agentId, true, generation);
|
|
6583
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6383
6584
|
const opener = openerId ? document.getElementById(openerId) : null;
|
|
6384
6585
|
if (opener && !opener.hidden && !opener.disabled) opener.focus();
|
|
6385
6586
|
}
|
|
@@ -6407,6 +6608,8 @@ function renderStructuredComposerMenu() {
|
|
|
6407
6608
|
const retainedFocus = captureStructuredComposerMenuFocus(menu);
|
|
6408
6609
|
menu.replaceChildren();
|
|
6409
6610
|
const session = structuredSessionSnapshot;
|
|
6611
|
+
const agentId = focusedAgentId;
|
|
6612
|
+
const generation = structuredSessionGeneration;
|
|
6410
6613
|
if (!structuredComposerMenu || !session) {
|
|
6411
6614
|
menu.hidden = true;
|
|
6412
6615
|
return;
|
|
@@ -6448,7 +6651,7 @@ function renderStructuredComposerMenu() {
|
|
|
6448
6651
|
const current = session.currentModeId || (session.modes && session.modes.currentModeId) || '';
|
|
6449
6652
|
modes.forEach((mode) => {
|
|
6450
6653
|
items.appendChild(structuredMenuButton(mode.name || mode.id, mode.description || '', mode.id === current, () => {
|
|
6451
|
-
void patchStructuredAcpSession({ modeId: mode.id }).catch(showStructuredComposerError);
|
|
6654
|
+
void patchStructuredAcpSession(agentId, generation, { modeId: mode.id }).catch(showStructuredComposerError);
|
|
6452
6655
|
}, `mode:${mode.id}`));
|
|
6453
6656
|
});
|
|
6454
6657
|
} else if (!selectedConfig) {
|
|
@@ -6468,13 +6671,13 @@ function renderStructuredComposerMenu() {
|
|
|
6468
6671
|
} else if (selectedConfig.type === 'boolean') {
|
|
6469
6672
|
[false, true].forEach((value) => {
|
|
6470
6673
|
items.appendChild(structuredMenuButton(value ? 'ON' : 'OFF', selectedConfig.description || '', selectedConfig.currentValue === value, () => {
|
|
6471
|
-
void patchStructuredAcpSession({ configId: selectedConfig.id, value }).catch(showStructuredComposerError);
|
|
6674
|
+
void patchStructuredAcpSession(agentId, generation, { configId: selectedConfig.id, value }).catch(showStructuredComposerError);
|
|
6472
6675
|
}, `boolean:${value}`));
|
|
6473
6676
|
});
|
|
6474
6677
|
} else {
|
|
6475
6678
|
structuredSelectOptions(selectedConfig).forEach((candidate) => {
|
|
6476
6679
|
items.appendChild(structuredMenuButton(candidate.name || candidate.value, candidate.description || '', candidate.value === selectedConfig.currentValue, () => {
|
|
6477
|
-
void patchStructuredAcpSession({ configId: selectedConfig.id, value: candidate.value }).catch(showStructuredComposerError);
|
|
6680
|
+
void patchStructuredAcpSession(agentId, generation, { configId: selectedConfig.id, value: candidate.value }).catch(showStructuredComposerError);
|
|
6478
6681
|
}, `option:${candidate.value}`));
|
|
6479
6682
|
});
|
|
6480
6683
|
}
|
|
@@ -6509,8 +6712,12 @@ function renderStructuredSessionControls() {
|
|
|
6509
6712
|
renderStructuredComposerMenu();
|
|
6510
6713
|
}
|
|
6511
6714
|
|
|
6512
|
-
async function refreshStructuredSessionControls(
|
|
6513
|
-
|
|
6715
|
+
async function refreshStructuredSessionControls(
|
|
6716
|
+
agentId = focusedAgentId,
|
|
6717
|
+
force = false,
|
|
6718
|
+
generation = structuredSessionGeneration
|
|
6719
|
+
) {
|
|
6720
|
+
if (!isCurrentStructuredSession(agentId, generation) || structuredSessionControlsLoading) return;
|
|
6514
6721
|
const agent = state && state.agents.find((candidate) => candidate.id === agentId);
|
|
6515
6722
|
if (!agent || agent.runtimeBinding?.kind !== 'acp') {
|
|
6516
6723
|
resetStructuredSessionControls();
|
|
@@ -6525,6 +6732,7 @@ async function refreshStructuredSessionControls(agentId = focusedAgentId, force
|
|
|
6525
6732
|
try {
|
|
6526
6733
|
const response = await fetch(farmingApiPath(`/agents/${encodeURIComponent(agentId)}/acp-session`));
|
|
6527
6734
|
const body = await response.json().catch(() => null);
|
|
6735
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6528
6736
|
if (!response.ok || !body || !body.session) {
|
|
6529
6737
|
throw new Error(body && body.error ? body.error : `Failed to read ACP session (${response.status})`);
|
|
6530
6738
|
}
|
|
@@ -6532,9 +6740,12 @@ async function refreshStructuredSessionControls(agentId = focusedAgentId, force
|
|
|
6532
6740
|
structuredSessionControlsRevision = revision || String(body.session.updatedAt || Date.now());
|
|
6533
6741
|
renderStructuredSessionControls();
|
|
6534
6742
|
} catch (error) {
|
|
6743
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6535
6744
|
showStructuredComposerError(error);
|
|
6536
6745
|
} finally {
|
|
6537
|
-
|
|
6746
|
+
if (isCurrentStructuredSession(agentId, generation)) {
|
|
6747
|
+
structuredSessionControlsLoading = false;
|
|
6748
|
+
}
|
|
6538
6749
|
}
|
|
6539
6750
|
}
|
|
6540
6751
|
|
|
@@ -6606,7 +6817,7 @@ function setStructuredComposerActive(active) {
|
|
|
6606
6817
|
}
|
|
6607
6818
|
}
|
|
6608
6819
|
|
|
6609
|
-
function updateStructuredComposerState(agent) {
|
|
6820
|
+
function updateStructuredComposerState(agent, { refreshControls = true } = {}) {
|
|
6610
6821
|
const input = document.getElementById('crt-structured-input');
|
|
6611
6822
|
const sendButton = document.getElementById('crt-structured-send');
|
|
6612
6823
|
const statusNode = document.getElementById('crt-structured-composer-status');
|
|
@@ -6632,7 +6843,9 @@ function updateStructuredComposerState(agent) {
|
|
|
6632
6843
|
statusNode.textContent = error || `${kind} ${String(runtimeStatus || 'idle').toUpperCase()}`;
|
|
6633
6844
|
statusNode.classList.toggle('error', Boolean(error));
|
|
6634
6845
|
renderStructuredPermissions(agent);
|
|
6635
|
-
if (agent && agent.runtimeBinding?.kind === 'acp')
|
|
6846
|
+
if (refreshControls && agent && agent.runtimeBinding?.kind === 'acp') {
|
|
6847
|
+
void refreshStructuredSessionControls(agent.id);
|
|
6848
|
+
}
|
|
6636
6849
|
}
|
|
6637
6850
|
|
|
6638
6851
|
function structuredAttachmentId(file) {
|
|
@@ -6791,6 +7004,8 @@ function renderStructuredPermissions(agent) {
|
|
|
6791
7004
|
if (!container) return;
|
|
6792
7005
|
container.replaceChildren();
|
|
6793
7006
|
if (!agent || agent.runtimeBinding?.kind !== 'acp') return;
|
|
7007
|
+
const agentId = agent.id;
|
|
7008
|
+
const generation = structuredSessionGeneration;
|
|
6794
7009
|
const runtime = agent.runtimeBinding;
|
|
6795
7010
|
const requests = Array.isArray(runtime.pendingPermissions) && runtime.pendingPermissions.length
|
|
6796
7011
|
? runtime.pendingPermissions
|
|
@@ -6809,27 +7024,47 @@ function renderStructuredPermissions(agent) {
|
|
|
6809
7024
|
const button = document.createElement('button');
|
|
6810
7025
|
button.type = 'button';
|
|
6811
7026
|
button.textContent = `[${option.name}]`;
|
|
6812
|
-
button.onclick = () => void respondToStructuredPermission(
|
|
7027
|
+
button.onclick = () => void respondToStructuredPermission(
|
|
7028
|
+
agentId,
|
|
7029
|
+
generation,
|
|
7030
|
+
request.requestId,
|
|
7031
|
+
option.optionId,
|
|
7032
|
+
false,
|
|
7033
|
+
);
|
|
6813
7034
|
actions.appendChild(button);
|
|
6814
7035
|
});
|
|
6815
7036
|
const decline = document.createElement('button');
|
|
6816
7037
|
decline.type = 'button';
|
|
6817
7038
|
decline.textContent = '[DECLINE]';
|
|
6818
|
-
decline.onclick = () => void respondToStructuredPermission(
|
|
7039
|
+
decline.onclick = () => void respondToStructuredPermission(
|
|
7040
|
+
agentId,
|
|
7041
|
+
generation,
|
|
7042
|
+
request.requestId,
|
|
7043
|
+
undefined,
|
|
7044
|
+
true,
|
|
7045
|
+
);
|
|
6819
7046
|
actions.appendChild(decline);
|
|
6820
7047
|
panel.append(title, description, actions);
|
|
6821
7048
|
container.appendChild(panel);
|
|
6822
7049
|
});
|
|
6823
7050
|
}
|
|
6824
7051
|
|
|
6825
|
-
async function respondToStructuredPermission(requestId, optionId, cancelled) {
|
|
6826
|
-
if (!
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
7052
|
+
async function respondToStructuredPermission(agentId, generation, requestId, optionId, cancelled) {
|
|
7053
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
7054
|
+
let response;
|
|
7055
|
+
let body;
|
|
7056
|
+
try {
|
|
7057
|
+
response = await fetch(farmingApiPath(`/agents/${encodeURIComponent(agentId)}/acp-permission`), {
|
|
7058
|
+
method: 'POST',
|
|
7059
|
+
headers: { 'Content-Type': 'application/json' },
|
|
7060
|
+
body: JSON.stringify({ requestId, optionId, cancelled: cancelled === true })
|
|
7061
|
+
});
|
|
7062
|
+
body = await response.json().catch(() => null);
|
|
7063
|
+
} catch (error) {
|
|
7064
|
+
if (isCurrentStructuredSession(agentId, generation)) showStructuredComposerError(error);
|
|
7065
|
+
return;
|
|
7066
|
+
}
|
|
7067
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
6833
7068
|
if (!response.ok) {
|
|
6834
7069
|
showStructuredComposerError(new Error(body && body.error ? body.error : `Permission response failed (${response.status})`));
|
|
6835
7070
|
}
|
|
@@ -7069,13 +7304,13 @@ async function refreshCrtStructuredPreview(agent) {
|
|
|
7069
7304
|
}
|
|
7070
7305
|
}
|
|
7071
7306
|
|
|
7072
|
-
function renderStructuredTranscript(transcript, force = false) {
|
|
7307
|
+
function renderStructuredTranscript(transcript, force = false, agentId = focusedAgentId) {
|
|
7073
7308
|
const container = document.getElementById('terminal-output');
|
|
7074
|
-
if (!container) return;
|
|
7309
|
+
if (!container || agentId !== focusedAgentId) return;
|
|
7075
7310
|
const updatedAt = String(transcript && transcript.updatedAt || '');
|
|
7076
7311
|
if (!force && updatedAt && updatedAt === structuredSessionRenderedAt) return;
|
|
7077
7312
|
const nearBottom = container.scrollHeight - container.scrollTop - container.clientHeight < 80;
|
|
7078
|
-
if (!nearBottom
|
|
7313
|
+
if (!nearBottom) saveCrtStructuredReadingAnchor(agentId);
|
|
7079
7314
|
const turns = structuredTranscriptTurns(transcript);
|
|
7080
7315
|
|
|
7081
7316
|
const markdownRenderer = typeof window !== 'undefined' ? window.FarmingCrtMarkdownRenderer : null;
|
|
@@ -7099,15 +7334,21 @@ function renderStructuredTranscript(transcript, force = false) {
|
|
|
7099
7334
|
return;
|
|
7100
7335
|
}
|
|
7101
7336
|
structuredSessionRenderedAt = updatedAt;
|
|
7102
|
-
if (!
|
|
7337
|
+
if (!restoreCrtStructuredReadingAnchor(agentId)) {
|
|
7103
7338
|
if (force || nearBottom) container.scrollTop = container.scrollHeight;
|
|
7104
7339
|
}
|
|
7105
|
-
container.onscroll = () =>
|
|
7340
|
+
container.onscroll = () => {
|
|
7341
|
+
if (focusedAgentId === agentId) saveCrtStructuredReadingAnchor(agentId);
|
|
7342
|
+
};
|
|
7106
7343
|
requestAnimationFrame(updateStructuredTranscriptScrollState);
|
|
7107
7344
|
}
|
|
7108
7345
|
|
|
7109
|
-
async function refreshStructuredSession(
|
|
7110
|
-
|
|
7346
|
+
async function refreshStructuredSession(
|
|
7347
|
+
agentId = focusedAgentId,
|
|
7348
|
+
force = false,
|
|
7349
|
+
generation = structuredSessionGeneration
|
|
7350
|
+
) {
|
|
7351
|
+
if (!isCurrentStructuredSession(agentId, generation) || structuredSessionLoading) return;
|
|
7111
7352
|
const agent = state && state.agents.find((candidate) => candidate.id === agentId);
|
|
7112
7353
|
if (!agent || !isStructuredRuntimeAgent(agent)) return;
|
|
7113
7354
|
structuredSessionLoading = true;
|
|
@@ -7115,12 +7356,14 @@ async function refreshStructuredSession(agentId = focusedAgentId, force = false)
|
|
|
7115
7356
|
const endpoint = structuredTranscriptEndpoint(agent);
|
|
7116
7357
|
const response = await fetch(farmingApiPath(`/agents/${encodeURIComponent(agentId)}/${endpoint}?maxTurns=80`));
|
|
7117
7358
|
const body = await response.json().catch(() => null);
|
|
7359
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
7118
7360
|
if (!response.ok || !body || !body.transcript) {
|
|
7119
7361
|
throw new Error(body && body.error ? body.error : `Failed to read conversation (${response.status})`);
|
|
7120
7362
|
}
|
|
7121
|
-
renderStructuredTranscript(body.transcript, force);
|
|
7363
|
+
renderStructuredTranscript(body.transcript, force, agentId);
|
|
7122
7364
|
updateStructuredComposerState(agent);
|
|
7123
7365
|
} catch (error) {
|
|
7366
|
+
if (!isCurrentStructuredSession(agentId, generation)) return;
|
|
7124
7367
|
const container = document.getElementById('terminal-output');
|
|
7125
7368
|
if (container && !container.querySelector('.crt-structured-transcript')) {
|
|
7126
7369
|
const message = document.createElement('div');
|
|
@@ -7130,23 +7373,28 @@ async function refreshStructuredSession(agentId = focusedAgentId, force = false)
|
|
|
7130
7373
|
}
|
|
7131
7374
|
updateStructuredComposerState(agent);
|
|
7132
7375
|
} finally {
|
|
7133
|
-
|
|
7376
|
+
if (isCurrentStructuredSession(agentId, generation)) {
|
|
7377
|
+
structuredSessionLoading = false;
|
|
7378
|
+
}
|
|
7134
7379
|
}
|
|
7135
7380
|
}
|
|
7136
7381
|
|
|
7137
|
-
function stopStructuredSessionPolling() {
|
|
7382
|
+
function stopStructuredSessionPolling(revokeOwnership = true) {
|
|
7138
7383
|
if (structuredSessionPoller) {
|
|
7139
7384
|
clearInterval(structuredSessionPoller);
|
|
7140
7385
|
structuredSessionPoller = null;
|
|
7141
7386
|
}
|
|
7142
7387
|
structuredSessionLoading = false;
|
|
7143
7388
|
structuredSessionRenderedAt = '';
|
|
7389
|
+
if (revokeOwnership) structuredSessionGeneration += 1;
|
|
7144
7390
|
}
|
|
7145
7391
|
|
|
7146
|
-
function startStructuredSessionPolling(agentId) {
|
|
7147
|
-
stopStructuredSessionPolling();
|
|
7392
|
+
function startStructuredSessionPolling(agentId, generation) {
|
|
7393
|
+
stopStructuredSessionPolling(false);
|
|
7148
7394
|
structuredSessionPoller = setInterval(() => {
|
|
7149
|
-
if (
|
|
7395
|
+
if (isCurrentStructuredSession(agentId, generation)) {
|
|
7396
|
+
void refreshStructuredSession(agentId, false, generation);
|
|
7397
|
+
}
|
|
7150
7398
|
}, 1000);
|
|
7151
7399
|
}
|
|
7152
7400
|
|
|
@@ -7317,9 +7565,11 @@ function setupStructuredSessionComposer() {
|
|
|
7317
7565
|
const agent = state && state.agents.find((candidate) => candidate.id === focusedAgentId);
|
|
7318
7566
|
const action = structuredComposerAction(agent, input.value);
|
|
7319
7567
|
if (!agent || action === 'disabled') return;
|
|
7568
|
+
const agentId = agent.id;
|
|
7569
|
+
const generation = structuredSessionGeneration;
|
|
7320
7570
|
if (action === 'interrupt') {
|
|
7321
7571
|
structuredComposerRestoreFocusAfterInterrupt = true;
|
|
7322
|
-
const interrupted = getSessionClient()?.interruptAgent(
|
|
7572
|
+
const interrupted = getSessionClient()?.interruptAgent(agentId);
|
|
7323
7573
|
if (!interrupted) {
|
|
7324
7574
|
structuredComposerRestoreFocusAfterInterrupt = false;
|
|
7325
7575
|
showStructuredComposerError(new Error('Connection unavailable'));
|
|
@@ -7333,7 +7583,7 @@ function setupStructuredSessionComposer() {
|
|
|
7333
7583
|
if (!message && promptAttachments.length === 0) return;
|
|
7334
7584
|
const statusNode = document.getElementById('crt-structured-composer-status');
|
|
7335
7585
|
const completeSubmission = () => {
|
|
7336
|
-
addStructuredComposerHistory(
|
|
7586
|
+
addStructuredComposerHistory(agentId, draft);
|
|
7337
7587
|
input.value = '';
|
|
7338
7588
|
structuredComposerAttachments = [];
|
|
7339
7589
|
renderStructuredComposerAttachments();
|
|
@@ -7343,11 +7593,11 @@ function setupStructuredSessionComposer() {
|
|
|
7343
7593
|
statusNode.classList.remove('error');
|
|
7344
7594
|
}
|
|
7345
7595
|
updateStructuredComposerState(agent);
|
|
7346
|
-
setTimeout(() => void refreshStructuredSession(
|
|
7596
|
+
setTimeout(() => void refreshStructuredSession(agentId, true, generation), 160);
|
|
7347
7597
|
};
|
|
7348
7598
|
const sent = action === 'send' && structuredRuntimeStatus(agent) !== 'idle'
|
|
7349
|
-
? (queueStructuredComposerFollowUp(
|
|
7350
|
-
: getSessionClient()?.sendComposerMessage(
|
|
7599
|
+
? (queueStructuredComposerFollowUp(agentId, message, promptAttachments), true)
|
|
7600
|
+
: getSessionClient()?.sendComposerMessage(agentId, message, promptAttachments);
|
|
7351
7601
|
if (!sent) {
|
|
7352
7602
|
if (statusNode) {
|
|
7353
7603
|
statusNode.textContent = 'Connection unavailable';
|
|
@@ -7363,14 +7613,17 @@ async function openStructuredSession(agent, sessionToken) {
|
|
|
7363
7613
|
const runtime = getSessionRuntime();
|
|
7364
7614
|
const container = document.getElementById('terminal-output');
|
|
7365
7615
|
if (!container) return;
|
|
7616
|
+
const generation = ++structuredSessionGeneration;
|
|
7366
7617
|
container.classList.add('crt-structured-session');
|
|
7367
7618
|
setStructuredComposerActive(true);
|
|
7368
7619
|
setupStructuredSessionComposer();
|
|
7369
|
-
updateStructuredComposerState(agent);
|
|
7370
|
-
await refreshStructuredSessionControls(agent.id, true);
|
|
7371
|
-
|
|
7620
|
+
updateStructuredComposerState(agent, { refreshControls: false });
|
|
7621
|
+
await refreshStructuredSessionControls(agent.id, true, generation);
|
|
7622
|
+
if (!isCurrentStructuredSession(agent.id, generation)) return;
|
|
7623
|
+
await refreshStructuredSession(agent.id, true, generation);
|
|
7624
|
+
if (!isCurrentStructuredSession(agent.id, generation)) return;
|
|
7372
7625
|
if (runtime && !runtime.isCurrentSession(agent.id, sessionToken)) return;
|
|
7373
|
-
startStructuredSessionPolling(agent.id);
|
|
7626
|
+
startStructuredSessionPolling(agent.id, generation);
|
|
7374
7627
|
const input = document.getElementById('crt-structured-input');
|
|
7375
7628
|
if (input) {
|
|
7376
7629
|
resizeStructuredComposerInput(input);
|
|
@@ -7952,13 +8205,14 @@ if (typeof document !== 'undefined') {
|
|
|
7952
8205
|
|
|
7953
8206
|
if (
|
|
7954
8207
|
crtMainView === 'billing'
|
|
8208
|
+
&& billingMode === 'days'
|
|
7955
8209
|
&& !sessionActive
|
|
7956
8210
|
&& !e.ctrlKey
|
|
7957
8211
|
&& !e.metaKey
|
|
7958
8212
|
&& !e.altKey
|
|
7959
8213
|
&& navigationArrow
|
|
7960
|
-
&& selectCrtBillingDayByArrow(e.key)
|
|
7961
8214
|
) {
|
|
8215
|
+
selectCrtBillingDayByArrow(e.key);
|
|
7962
8216
|
e.preventDefault();
|
|
7963
8217
|
return;
|
|
7964
8218
|
}
|
|
@@ -8152,13 +8406,13 @@ if (typeof document !== 'undefined') {
|
|
|
8152
8406
|
}
|
|
8153
8407
|
|
|
8154
8408
|
if ((e.key === 'd' || e.key === 'D') && crtMainView === 'billing') {
|
|
8155
|
-
|
|
8409
|
+
selectCrtBillingMode('days');
|
|
8156
8410
|
e.preventDefault();
|
|
8157
8411
|
return;
|
|
8158
8412
|
}
|
|
8159
8413
|
|
|
8160
8414
|
if ((e.key === 'l' || e.key === 'L') && crtMainView === 'billing') {
|
|
8161
|
-
|
|
8415
|
+
selectCrtBillingMode('live');
|
|
8162
8416
|
e.preventDefault();
|
|
8163
8417
|
return;
|
|
8164
8418
|
}
|
|
@@ -8329,7 +8583,9 @@ if (typeof module !== 'undefined' && module.exports) {
|
|
|
8329
8583
|
crtAgentSessionKey,
|
|
8330
8584
|
crtResumedSessionFromSource,
|
|
8331
8585
|
crtDashboardStateSignature,
|
|
8586
|
+
crtBillingDayArrowTargetIndex,
|
|
8332
8587
|
crtBillingTimelineLabels,
|
|
8588
|
+
crtBillingTimeline,
|
|
8333
8589
|
crtBillingCurrentRate,
|
|
8334
8590
|
findDefaultNewAgentIndex,
|
|
8335
8591
|
findDirectionalNavigationIndex,
|
|
@@ -8354,6 +8610,7 @@ if (typeof module !== 'undefined' && module.exports) {
|
|
|
8354
8610
|
getCrtAgentVerticalPageTarget,
|
|
8355
8611
|
getCrtLiveAgents,
|
|
8356
8612
|
getCrtRegularAgents,
|
|
8613
|
+
getCrtMainAgentDialogAction,
|
|
8357
8614
|
getCrtAgentRemovalFallback,
|
|
8358
8615
|
getAgentDisplayText,
|
|
8359
8616
|
getCrtPreviewCellStyle,
|
|
@@ -8398,5 +8655,6 @@ if (typeof module !== 'undefined' && module.exports) {
|
|
|
8398
8655
|
});
|
|
8399
8656
|
window.addEventListener('pagehide', suspendCrtPageConnection);
|
|
8400
8657
|
window.addEventListener('pageshow', resumeCrtPageConnection);
|
|
8658
|
+
startCrtTopBarTokenRateRefresh();
|
|
8401
8659
|
connect();
|
|
8402
8660
|
}
|