@pixelbyte-software/pixcode 1.53.6 → 1.53.8
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/dist/assets/{index-DP7jnMyc.js → index-BTj30VBN.js} +1 -1
- package/dist/index.html +1 -1
- package/dist-server/server/claude-sdk.js +17 -15
- package/dist-server/server/claude-sdk.js.map +1 -1
- package/dist-server/server/cursor-cli.js +14 -12
- package/dist-server/server/cursor-cli.js.map +1 -1
- package/dist-server/server/gemini-cli.js +14 -12
- package/dist-server/server/gemini-cli.js.map +1 -1
- package/dist-server/server/index.js +38 -7
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/openai-codex.js +25 -22
- package/dist-server/server/openai-codex.js.map +1 -1
- package/dist-server/server/opencode-cli.js +14 -12
- package/dist-server/server/opencode-cli.js.map +1 -1
- package/dist-server/server/qwen-code-cli.js +14 -12
- package/dist-server/server/qwen-code-cli.js.map +1 -1
- package/dist-server/server/routes/agent.js +11 -4
- package/dist-server/server/routes/agent.js.map +1 -1
- package/dist-server/server/services/telegram/control-center.js +44 -2
- package/dist-server/server/services/telegram/control-center.js.map +1 -1
- package/dist-server/server/services/telegram/translations.js +8 -0
- package/dist-server/server/services/telegram/translations.js.map +1 -1
- package/package.json +1 -1
- package/server/claude-sdk.js +3 -3
- package/server/cursor-cli.js +3 -3
- package/server/gemini-cli.js +3 -3
- package/server/index.js +39 -5
- package/server/openai-codex.js +5 -4
- package/server/opencode-cli.js +3 -3
- package/server/qwen-code-cli.js +3 -3
- package/server/routes/agent.js +11 -4
- package/server/services/telegram/control-center.js +53 -2
- package/server/services/telegram/translations.js +8 -0
package/server/gemini-cli.js
CHANGED
|
@@ -65,7 +65,7 @@ async function ensureGeminiSettingsJson() {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
async function spawnGemini(command, options = {}, ws) {
|
|
68
|
-
const { sessionId, projectPath, cwd, toolsSettings, permissionMode, images, sessionSummary } = options;
|
|
68
|
+
const { sessionId, projectPath, cwd, toolsSettings, permissionMode, images, sessionSummary, suppressNotifications = false } = options;
|
|
69
69
|
let capturedSessionId = sessionId; // Track session ID throughout the process
|
|
70
70
|
let sessionCreatedSent = false; // Track if we've already sent session-created event
|
|
71
71
|
let assistantBlocks = []; // Accumulate the full response blocks including tools
|
|
@@ -265,7 +265,7 @@ async function spawnGemini(command, options = {}, ws) {
|
|
|
265
265
|
|
|
266
266
|
const finalSessionId = capturedSessionId || sessionId || processKey;
|
|
267
267
|
if (code === 0 && !error) {
|
|
268
|
-
notifyRunStopped({
|
|
268
|
+
if (!suppressNotifications) notifyRunStopped({
|
|
269
269
|
userId: ws?.userId || null,
|
|
270
270
|
provider: 'gemini',
|
|
271
271
|
sessionId: finalSessionId,
|
|
@@ -275,7 +275,7 @@ async function spawnGemini(command, options = {}, ws) {
|
|
|
275
275
|
return;
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
-
notifyRunFailed({
|
|
278
|
+
if (!suppressNotifications) notifyRunFailed({
|
|
279
279
|
userId: ws?.userId || null,
|
|
280
280
|
provider: 'gemini',
|
|
281
281
|
sessionId: finalSessionId,
|
package/server/index.js
CHANGED
|
@@ -1012,6 +1012,9 @@ const ptySessionsMap = new Map();
|
|
|
1012
1012
|
const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
1013
1013
|
const COMPLETED_PTY_SESSION_TTL = 5 * 60 * 1000;
|
|
1014
1014
|
const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
|
|
1015
|
+
const SHELL_OUTPUT_FLUSH_MAX_CHARS = 128 * 1024;
|
|
1016
|
+
const SHELL_WS_BACKPRESSURE_LIMIT = 8 * 1024 * 1024;
|
|
1017
|
+
const PTY_SESSION_BUFFER_MAX_BYTES = Number.parseInt(process.env.PIXCODE_PTY_BUFFER_MAX_BYTES || '', 10) || (2 * 1024 * 1024);
|
|
1015
1018
|
const SHELL_CLI_PROVIDERS = new Set(['claude', 'codex', 'cursor', 'gemini', 'qwen', 'opencode']);
|
|
1016
1019
|
import { stripAnsiSequences, normalizeDetectedUrl, extractUrlsFromText, shouldAutoOpenUrlFromOutput } from './utils/url-detection.js';
|
|
1017
1020
|
|
|
@@ -1156,12 +1159,23 @@ function resolveProviderTerminalState(session, provider, output) {
|
|
|
1156
1159
|
|
|
1157
1160
|
function appendPtySessionBuffer(session, data) {
|
|
1158
1161
|
if (!session) return;
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1162
|
+
let value = String(data || '');
|
|
1163
|
+
if (!value) return;
|
|
1164
|
+
|
|
1165
|
+
let valueBytes = Buffer.byteLength(value, 'utf8');
|
|
1166
|
+
if (valueBytes > PTY_SESSION_BUFFER_MAX_BYTES) {
|
|
1167
|
+
value = value.slice(-PTY_SESSION_BUFFER_MAX_BYTES);
|
|
1168
|
+
valueBytes = Buffer.byteLength(value, 'utf8');
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
session.buffer.push(value);
|
|
1172
|
+
session.bufferBytes = (session.bufferBytes || 0) + valueBytes;
|
|
1173
|
+
|
|
1174
|
+
while (session.buffer.length > 0 && session.bufferBytes > PTY_SESSION_BUFFER_MAX_BYTES) {
|
|
1175
|
+
const removed = session.buffer.shift();
|
|
1176
|
+
session.bufferBytes -= Buffer.byteLength(String(removed || ''), 'utf8');
|
|
1164
1177
|
}
|
|
1178
|
+
if (session.bufferBytes < 0) session.bufferBytes = 0;
|
|
1165
1179
|
}
|
|
1166
1180
|
|
|
1167
1181
|
function normalizeTerminalStartupInput(input) {
|
|
@@ -3424,7 +3438,17 @@ function handleShellConnection(ws, request) {
|
|
|
3424
3438
|
appendPtySessionBuffer(session, rawData);
|
|
3425
3439
|
|
|
3426
3440
|
if (session.ws && session.ws.readyState === WebSocket.OPEN) {
|
|
3441
|
+
if (session.ws.bufferedAmount > SHELL_WS_BACKPRESSURE_LIMIT) {
|
|
3442
|
+
session.droppedOutputBytes = (session.droppedOutputBytes || 0) + Buffer.byteLength(rawData, 'utf8');
|
|
3443
|
+
return;
|
|
3444
|
+
}
|
|
3445
|
+
|
|
3446
|
+
const droppedOutputBytes = session.droppedOutputBytes || 0;
|
|
3447
|
+
session.droppedOutputBytes = 0;
|
|
3427
3448
|
let outputData = rawData;
|
|
3449
|
+
if (droppedOutputBytes > 0) {
|
|
3450
|
+
outputData = `\r\n\x1b[33m[Pixcode] ${droppedOutputBytes} bytes of terminal output were skipped because the browser connection was backpressured.\x1b[0m\r\n${outputData}`;
|
|
3451
|
+
}
|
|
3428
3452
|
|
|
3429
3453
|
const cleanChunk = stripAnsiSequences(rawData);
|
|
3430
3454
|
urlDetectionBuffer = `${urlDetectionBuffer}${cleanChunk}`.slice(-SHELL_URL_PARSE_BUFFER_LIMIT);
|
|
@@ -3786,6 +3810,8 @@ function handleShellConnection(ws, request) {
|
|
|
3786
3810
|
pty: shellProcess,
|
|
3787
3811
|
ws: ws,
|
|
3788
3812
|
buffer: [],
|
|
3813
|
+
bufferBytes: 0,
|
|
3814
|
+
droppedOutputBytes: 0,
|
|
3789
3815
|
timeoutId: null,
|
|
3790
3816
|
userId: ownerUserId,
|
|
3791
3817
|
projectPath,
|
|
@@ -3812,6 +3838,14 @@ function handleShellConnection(ws, request) {
|
|
|
3812
3838
|
// keystroke-sized chunk.
|
|
3813
3839
|
shellProcess.onData((data) => {
|
|
3814
3840
|
outputBuffer += data;
|
|
3841
|
+
if (outputBuffer.length >= SHELL_OUTPUT_FLUSH_MAX_CHARS) {
|
|
3842
|
+
if (outputFlushTimer) {
|
|
3843
|
+
clearTimeout(outputFlushTimer);
|
|
3844
|
+
outputFlushTimer = null;
|
|
3845
|
+
}
|
|
3846
|
+
flushOutputBuffer();
|
|
3847
|
+
return;
|
|
3848
|
+
}
|
|
3815
3849
|
if (!outputFlushTimer) {
|
|
3816
3850
|
outputFlushTimer = setTimeout(flushOutputBuffer, 8);
|
|
3817
3851
|
}
|
package/server/openai-codex.js
CHANGED
|
@@ -236,7 +236,8 @@ export async function queryCodex(command, options = {}, ws) {
|
|
|
236
236
|
cwd,
|
|
237
237
|
projectPath,
|
|
238
238
|
model,
|
|
239
|
-
permissionMode = 'default'
|
|
239
|
+
permissionMode = 'default',
|
|
240
|
+
suppressNotifications = false
|
|
240
241
|
} = options;
|
|
241
242
|
|
|
242
243
|
const workingDirectory = cwd || projectPath || process.cwd();
|
|
@@ -313,7 +314,7 @@ export async function queryCodex(command, options = {}, ws) {
|
|
|
313
314
|
|
|
314
315
|
if (event.type === 'turn.failed' && !terminalFailure) {
|
|
315
316
|
terminalFailure = event.error || new Error('Turn failed');
|
|
316
|
-
notifyRunFailed({
|
|
317
|
+
if (!suppressNotifications) notifyRunFailed({
|
|
317
318
|
userId: ws?.userId || null,
|
|
318
319
|
provider: 'codex',
|
|
319
320
|
sessionId: currentSessionId,
|
|
@@ -332,7 +333,7 @@ export async function queryCodex(command, options = {}, ws) {
|
|
|
332
333
|
// Send completion event
|
|
333
334
|
if (!terminalFailure) {
|
|
334
335
|
sendMessage(ws, createNormalizedMessage({ kind: 'complete', actualSessionId: thread.id, sessionId: currentSessionId, provider: 'codex' }));
|
|
335
|
-
notifyRunStopped({
|
|
336
|
+
if (!suppressNotifications) notifyRunStopped({
|
|
336
337
|
userId: ws?.userId || null,
|
|
337
338
|
provider: 'codex',
|
|
338
339
|
sessionId: currentSessionId,
|
|
@@ -359,7 +360,7 @@ export async function queryCodex(command, options = {}, ws) {
|
|
|
359
360
|
|
|
360
361
|
sendMessage(ws, createNormalizedMessage({ kind: 'error', content: errorContent, sessionId: currentSessionId, provider: 'codex' }));
|
|
361
362
|
if (!terminalFailure) {
|
|
362
|
-
notifyRunFailed({
|
|
363
|
+
if (!suppressNotifications) notifyRunFailed({
|
|
363
364
|
userId: ws?.userId || null,
|
|
364
365
|
provider: 'codex',
|
|
365
366
|
sessionId: currentSessionId,
|
package/server/opencode-cli.js
CHANGED
|
@@ -79,7 +79,7 @@ function buildOpencodePermissionConfig(settings) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
async function spawnOpencode(command, options = {}, ws) {
|
|
82
|
-
const { sessionId, projectPath, cwd, toolsSettings, permissionMode, images, sessionSummary, agent: agentOverride } = options;
|
|
82
|
+
const { sessionId, projectPath, cwd, toolsSettings, permissionMode, images, sessionSummary, agent: agentOverride, suppressNotifications = false } = options;
|
|
83
83
|
let capturedSessionId = sessionId;
|
|
84
84
|
let sessionCreatedSent = false;
|
|
85
85
|
let assistantBlocks = [];
|
|
@@ -211,7 +211,7 @@ async function spawnOpencode(command, options = {}, ws) {
|
|
|
211
211
|
|
|
212
212
|
const finalSessionId = capturedSessionId || sessionId || processKey;
|
|
213
213
|
if (code === 0 && !error) {
|
|
214
|
-
notifyRunStopped({
|
|
214
|
+
if (!suppressNotifications) notifyRunStopped({
|
|
215
215
|
userId: ws?.userId || null,
|
|
216
216
|
provider: 'opencode',
|
|
217
217
|
sessionId: finalSessionId,
|
|
@@ -221,7 +221,7 @@ async function spawnOpencode(command, options = {}, ws) {
|
|
|
221
221
|
return;
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
notifyRunFailed({
|
|
224
|
+
if (!suppressNotifications) notifyRunFailed({
|
|
225
225
|
userId: ws?.userId || null,
|
|
226
226
|
provider: 'opencode',
|
|
227
227
|
sessionId: finalSessionId,
|
package/server/qwen-code-cli.js
CHANGED
|
@@ -34,7 +34,7 @@ function readCliIdleTimeoutMs() {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
async function spawnQwen(command, options = {}, ws) {
|
|
37
|
-
const { sessionId, projectPath, cwd, toolsSettings, permissionMode, images, sessionSummary } = options;
|
|
37
|
+
const { sessionId, projectPath, cwd, toolsSettings, permissionMode, images, sessionSummary, suppressNotifications = false } = options;
|
|
38
38
|
let capturedSessionId = sessionId;
|
|
39
39
|
let sessionCreatedSent = false;
|
|
40
40
|
let assistantBlocks = [];
|
|
@@ -154,7 +154,7 @@ async function spawnQwen(command, options = {}, ws) {
|
|
|
154
154
|
|
|
155
155
|
const finalSessionId = capturedSessionId || sessionId || processKey;
|
|
156
156
|
if (code === 0 && !error) {
|
|
157
|
-
notifyRunStopped({
|
|
157
|
+
if (!suppressNotifications) notifyRunStopped({
|
|
158
158
|
userId: ws?.userId || null,
|
|
159
159
|
provider: 'qwen',
|
|
160
160
|
sessionId: finalSessionId,
|
|
@@ -164,7 +164,7 @@ async function spawnQwen(command, options = {}, ws) {
|
|
|
164
164
|
return;
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
notifyRunFailed({
|
|
167
|
+
if (!suppressNotifications) notifyRunFailed({
|
|
168
168
|
userId: ws?.userId || null,
|
|
169
169
|
provider: 'qwen',
|
|
170
170
|
sessionId: finalSessionId,
|
package/server/routes/agent.js
CHANGED
|
@@ -968,6 +968,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
968
968
|
const permissionMode = ['default', 'acceptEdits', 'bypassPermissions', 'plan', 'auto_edit', 'yolo'].includes(requestedPermissionMode)
|
|
969
969
|
? requestedPermissionMode
|
|
970
970
|
: null;
|
|
971
|
+
const suppressNotifications = req.body.suppressNotifications === true || req.body.suppressNotifications === 'true';
|
|
971
972
|
|
|
972
973
|
// Parse stream and cleanup as booleans (handle string "true"/"false" from curl)
|
|
973
974
|
const stream = req.body.stream === undefined ? true : (req.body.stream === true || req.body.stream === 'true');
|
|
@@ -1079,7 +1080,8 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1079
1080
|
cwd: finalProjectPath,
|
|
1080
1081
|
sessionId: sessionId || null,
|
|
1081
1082
|
model: model,
|
|
1082
|
-
permissionMode: permissionMode || 'bypassPermissions' // Bypass all permissions for API calls unless explicitly restricted
|
|
1083
|
+
permissionMode: permissionMode || 'bypassPermissions', // Bypass all permissions for API calls unless explicitly restricted
|
|
1084
|
+
suppressNotifications,
|
|
1083
1085
|
}, writer);
|
|
1084
1086
|
|
|
1085
1087
|
} else if (provider === 'cursor') {
|
|
@@ -1090,7 +1092,8 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1090
1092
|
cwd: finalProjectPath,
|
|
1091
1093
|
sessionId: sessionId || null,
|
|
1092
1094
|
model: model || undefined,
|
|
1093
|
-
skipPermissions: permissionMode ? permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits' || permissionMode === 'yolo' : true
|
|
1095
|
+
skipPermissions: permissionMode ? permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits' || permissionMode === 'yolo' : true,
|
|
1096
|
+
suppressNotifications,
|
|
1094
1097
|
}, writer);
|
|
1095
1098
|
} else if (provider === 'codex') {
|
|
1096
1099
|
console.log('🤖 Starting Codex SDK session');
|
|
@@ -1100,7 +1103,8 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1100
1103
|
cwd: finalProjectPath,
|
|
1101
1104
|
sessionId: sessionId || null,
|
|
1102
1105
|
model: model || getDefaultProviderModel('codex'),
|
|
1103
|
-
permissionMode: permissionMode || 'bypassPermissions'
|
|
1106
|
+
permissionMode: permissionMode || 'bypassPermissions',
|
|
1107
|
+
suppressNotifications,
|
|
1104
1108
|
}, writer);
|
|
1105
1109
|
} else if (provider === 'gemini') {
|
|
1106
1110
|
console.log('✨ Starting Gemini CLI session');
|
|
@@ -1111,7 +1115,8 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1111
1115
|
sessionId: sessionId || null,
|
|
1112
1116
|
model: model,
|
|
1113
1117
|
permissionMode: permissionMode || undefined,
|
|
1114
|
-
skipPermissions: permissionMode ? permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits' || permissionMode === 'yolo' : true
|
|
1118
|
+
skipPermissions: permissionMode ? permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits' || permissionMode === 'yolo' : true,
|
|
1119
|
+
suppressNotifications,
|
|
1115
1120
|
}, writer);
|
|
1116
1121
|
} else if (provider === 'qwen') {
|
|
1117
1122
|
console.log('🐉 Starting Qwen Code CLI session');
|
|
@@ -1123,6 +1128,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1123
1128
|
model: model,
|
|
1124
1129
|
permissionMode: permissionMode || undefined,
|
|
1125
1130
|
skipPermissions: permissionMode ? permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits' || permissionMode === 'yolo' : true,
|
|
1131
|
+
suppressNotifications,
|
|
1126
1132
|
}, writer);
|
|
1127
1133
|
} else if (provider === 'opencode') {
|
|
1128
1134
|
console.log('🅾️ Starting OpenCode CLI session');
|
|
@@ -1138,6 +1144,7 @@ router.post('/', validateExternalApiKey, async (req, res) => {
|
|
|
1138
1144
|
denyPatterns: [],
|
|
1139
1145
|
skipPermissions: permissionMode ? permissionMode === 'bypassPermissions' || permissionMode === 'acceptEdits' || permissionMode === 'yolo' : true,
|
|
1140
1146
|
},
|
|
1147
|
+
suppressNotifications,
|
|
1141
1148
|
}, writer);
|
|
1142
1149
|
}
|
|
1143
1150
|
|
|
@@ -27,6 +27,8 @@ const TERMINAL_RUN_STATES = new Set(['completed', 'failed', 'canceled']);
|
|
|
27
27
|
const CALLBACK_TTL_MS = 10 * 60 * 1000;
|
|
28
28
|
const MAX_CALLBACK_ACTIONS = 1000;
|
|
29
29
|
const MAX_TELEGRAM_TEXT = 3600;
|
|
30
|
+
const MAX_ACTIVITY_OUTPUT_CHARS = 48_000;
|
|
31
|
+
const MAX_SSE_BUFFER_CHARS = 256_000;
|
|
30
32
|
const ACTIVITY_EDIT_THROTTLE_MS = 1200;
|
|
31
33
|
const ACTIVITY_HEARTBEAT_MS = 8000;
|
|
32
34
|
const INTENT_ROUTER_TIMEOUT_MS = 45_000;
|
|
@@ -85,6 +87,14 @@ function truncate(text, max = MAX_TELEGRAM_TEXT) {
|
|
|
85
87
|
return value.length > max ? `${value.slice(0, max - 20)}\n\n…truncated…` : value;
|
|
86
88
|
}
|
|
87
89
|
|
|
90
|
+
function appendBoundedText(current, chunk, maxChars = MAX_ACTIVITY_OUTPUT_CHARS) {
|
|
91
|
+
const nextChunk = String(chunk || '');
|
|
92
|
+
if (!nextChunk) return { text: current || '', truncated: false };
|
|
93
|
+
const combined = `${current || ''}${nextChunk}`;
|
|
94
|
+
if (combined.length <= maxChars) return { text: combined, truncated: false };
|
|
95
|
+
return { text: combined.slice(-maxChars), truncated: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
88
98
|
function languageFor(link) {
|
|
89
99
|
return SUPPORTED_LANGUAGES.includes(link?.language) ? link.language : 'en';
|
|
90
100
|
}
|
|
@@ -313,6 +323,9 @@ async function localAgentStream(userId, body, onEvent) {
|
|
|
313
323
|
const { done, value } = await reader.read();
|
|
314
324
|
if (done) break;
|
|
315
325
|
buffer += decoder.decode(value, { stream: true });
|
|
326
|
+
if (buffer.length > MAX_SSE_BUFFER_CHARS) {
|
|
327
|
+
buffer = buffer.slice(-MAX_SSE_BUFFER_CHARS);
|
|
328
|
+
}
|
|
316
329
|
let boundary = buffer.indexOf('\n\n');
|
|
317
330
|
while (boundary !== -1) {
|
|
318
331
|
const block = buffer.slice(0, boundary);
|
|
@@ -360,6 +373,7 @@ function createActivityState({ lang, type = 'agent', provider, project, prompt,
|
|
|
360
373
|
workflowId: null,
|
|
361
374
|
events: [],
|
|
362
375
|
output: '',
|
|
376
|
+
outputTruncated: false,
|
|
363
377
|
error: null,
|
|
364
378
|
};
|
|
365
379
|
}
|
|
@@ -378,8 +392,43 @@ function activityTitle(activity) {
|
|
|
378
392
|
return t(activity.lang, 'control.activity.agentTitle');
|
|
379
393
|
}
|
|
380
394
|
|
|
395
|
+
function trimTelegramOutput(text, max, suffix = '') {
|
|
396
|
+
const value = String(text || '').trim();
|
|
397
|
+
const ending = String(suffix || '').trim();
|
|
398
|
+
if (value.length <= max) return value;
|
|
399
|
+
const room = Math.max(300, max - ending.length - 4);
|
|
400
|
+
return `${value.slice(0, room).trim()}\n\n${ending}`;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function appendActivityOutput(activity, chunk) {
|
|
404
|
+
if (!activity) return;
|
|
405
|
+
const next = appendBoundedText(activity.output, chunk);
|
|
406
|
+
activity.output = next.text;
|
|
407
|
+
activity.outputTruncated = Boolean(activity.outputTruncated || next.truncated);
|
|
408
|
+
}
|
|
409
|
+
|
|
381
410
|
function renderActivity(activity, { finalText = null } = {}) {
|
|
382
411
|
const output = finalText || activity.output;
|
|
412
|
+
if (activity.type === 'agent' && output && !activity.error) {
|
|
413
|
+
if (activity.status === 'done') {
|
|
414
|
+
return trimTelegramOutput(
|
|
415
|
+
output,
|
|
416
|
+
3400,
|
|
417
|
+
activity.outputTruncated
|
|
418
|
+
? t(activity.lang, 'control.activity.outputHistoryTrimmed')
|
|
419
|
+
: t(activity.lang, 'control.activity.outputTooLong'),
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const footer = `⏳ ${t(activity.lang, 'control.activity.liveFooter', { elapsed: formatElapsed(activity.startedAt) })}`;
|
|
424
|
+
const body = trimTelegramOutput(
|
|
425
|
+
output,
|
|
426
|
+
3200,
|
|
427
|
+
t(activity.lang, 'control.activity.outputShortened'),
|
|
428
|
+
);
|
|
429
|
+
return truncate(`${body}\n\n${footer}`, 3400);
|
|
430
|
+
}
|
|
431
|
+
|
|
383
432
|
const lines = [
|
|
384
433
|
`${activity.status === 'failed' ? '❌' : activity.status === 'done' ? '✅' : activity.status === 'running' ? '🔧' : '⏳'} ${activityTitle(activity)}`,
|
|
385
434
|
'',
|
|
@@ -813,13 +862,13 @@ function applyAgentStreamEvent(activity, event) {
|
|
|
813
862
|
if (event.kind === 'stream_delta' || event.kind === 'text') {
|
|
814
863
|
activity.status = 'running';
|
|
815
864
|
activity.phase = t(activity.lang, 'control.activity.responding');
|
|
816
|
-
activity
|
|
865
|
+
appendActivityOutput(activity, extractTextFromEvent(event));
|
|
817
866
|
return;
|
|
818
867
|
}
|
|
819
868
|
if (event.type === 'claude-response' && event.data?.type === 'assistant') {
|
|
820
869
|
activity.status = 'running';
|
|
821
870
|
activity.phase = t(activity.lang, 'control.activity.responding');
|
|
822
|
-
activity
|
|
871
|
+
appendActivityOutput(activity, extractTextFromEvent(event));
|
|
823
872
|
return;
|
|
824
873
|
}
|
|
825
874
|
if (event.kind === 'tool_use') {
|
|
@@ -924,6 +973,7 @@ async function runAgent({ bot, chatId, link, prompt, activity = null }) {
|
|
|
924
973
|
message: buildTelegramAgentPrompt(prompt, state),
|
|
925
974
|
cleanup: false,
|
|
926
975
|
permissionMode: 'default',
|
|
976
|
+
suppressNotifications: true,
|
|
927
977
|
}, async (event) => {
|
|
928
978
|
applyAgentStreamEvent(active, event);
|
|
929
979
|
await editTelegramActivity({ bot, chatId, activity: active });
|
|
@@ -1136,6 +1186,7 @@ async function resolveTelegramAiIntent({ bot, chatId, link, text, activity }) {
|
|
|
1136
1186
|
cleanup: false,
|
|
1137
1187
|
stream: false,
|
|
1138
1188
|
permissionMode: 'plan',
|
|
1189
|
+
suppressNotifications: true,
|
|
1139
1190
|
},
|
|
1140
1191
|
});
|
|
1141
1192
|
const assistantText = extractAssistantText(response);
|
|
@@ -138,6 +138,10 @@ const EN = {
|
|
|
138
138
|
'control.activity.toolDone': 'Tool completed',
|
|
139
139
|
'control.activity.toolFailed': 'Tool failed',
|
|
140
140
|
'control.activity.tokens': 'Tokens',
|
|
141
|
+
'control.activity.liveFooter': 'Response is still streaming · {{elapsed}}',
|
|
142
|
+
'control.activity.outputShortened': 'Telegram output was shortened while the run continues.',
|
|
143
|
+
'control.activity.outputHistoryTrimmed': 'Earlier response output was trimmed to protect memory. Open the Pixcode session for the full output.',
|
|
144
|
+
'control.activity.outputTooLong': 'Long response shortened for Telegram. Open the session in Pixcode for the full output.',
|
|
141
145
|
'notification.taskDone': '✅ {{title}} — task completed.',
|
|
142
146
|
'notification.taskFailed': '⚠️ {{title}} — task failed: {{error}}',
|
|
143
147
|
'notification.actionRequired': '❗ {{title}} — action required.',
|
|
@@ -276,6 +280,10 @@ const TR = {
|
|
|
276
280
|
'control.activity.toolDone': 'Tool tamamlandı',
|
|
277
281
|
'control.activity.toolFailed': 'Tool başarısız',
|
|
278
282
|
'control.activity.tokens': 'Token',
|
|
283
|
+
'control.activity.liveFooter': 'Yanıt devam ediyor · {{elapsed}}',
|
|
284
|
+
'control.activity.outputShortened': 'Telegram çıktısı çalışma devam ederken kısaltıldı.',
|
|
285
|
+
'control.activity.outputHistoryTrimmed': 'Yanıtın önceki bölümü bellek koruması için kısaltıldı. Tam çıktı için Pixcode oturumunu aç.',
|
|
286
|
+
'control.activity.outputTooLong': 'Uzun yanıt Telegram için kısaltıldı. Tam çıktı için Pixcode oturumunu aç.',
|
|
279
287
|
'notification.taskDone': '✅ {{title}} — görev tamamlandı.',
|
|
280
288
|
'notification.taskFailed': '⚠️ {{title}} — görev başarısız: {{error}}',
|
|
281
289
|
'notification.actionRequired': '❗ {{title}} — işlem gerekli.',
|