@yvhitxcel/opencode-remote 0.16.3 → 0.17.0

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.
@@ -13,7 +13,23 @@ const CONFIG_FILE = join(CONFIG_DIR, '.env');
13
13
 
14
14
  const threadModels = new Map();
15
15
  const recentModels = [];
16
+ let rawDebugEnabled = false;
17
+ let thinkVisibleEnabled = false;
16
18
 
19
+ export function setRawDebug(enabled) {
20
+ rawDebugEnabled = enabled;
21
+ console.log(`[rawDebug] ${enabled ? 'ON' : 'OFF'}`);
22
+ }
23
+ export function isRawDebug() {
24
+ return rawDebugEnabled || process.env.DEBUG_RAW === '1';
25
+ }
26
+ export function setThinkVisible(enabled) {
27
+ thinkVisibleEnabled = enabled;
28
+ console.log(`[think] ${enabled ? 'ON' : 'OFF'}`);
29
+ }
30
+ export function isThinkVisible() {
31
+ return thinkVisibleEnabled;
32
+ }
17
33
  export function setThreadModel(threadId, modelStr) {
18
34
  if (!modelStr || !modelStr.includes('/')) {
19
35
  threadModels.delete(threadId);
@@ -209,7 +225,6 @@ let opencodeInstance = null;
209
225
  let opencodeServer = null;
210
226
  let lastStdoutTime = 0;
211
227
  let lastStdoutLine = '';
212
- let lastReportedStatus = '';
213
228
  const PORTS_TO_TRY = [4096, 4097, 4098];
214
229
 
215
230
  // TCP-level port probe: true = occupied, false = free
@@ -225,7 +240,7 @@ function probeTCP(port, timeoutMs = 2000) {
225
240
  }
226
241
 
227
242
  async function tryConnectPort(port, timeoutMs = 5000) {
228
- const { createOpencodeClient } = await import('@opencode-ai/sdk');
243
+ const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
229
244
  const client = createOpencodeClient({ baseUrl: `http://localhost:${port}` });
230
245
  const result = await Promise.race([
231
246
  client.session.list(),
@@ -290,7 +305,7 @@ export async function initOpenCode() {
290
305
  opencodeServer.on('exit', (code) => console.log(`[opencode] exited with code ${code}`));
291
306
 
292
307
  // Wait for server to be ready
293
- const { createOpencodeClient } = await import('@opencode-ai/sdk');
308
+ const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
294
309
  for (let i = 0; i < 15; i++) {
295
310
  await new Promise(r => setTimeout(r, 1000));
296
311
  try {
@@ -353,7 +368,7 @@ export async function createSession(_threadId, title = `Remote control session`)
353
368
  const opencode = await initOpenCode();
354
369
  try {
355
370
  const createResult = await opencode.client.session.create({
356
- body: { title },
371
+ title,
357
372
  });
358
373
  if (createResult.error) {
359
374
  console.error('Failed to create session:', createResult.error);
@@ -364,7 +379,7 @@ export async function createSession(_threadId, title = `Remote control session`)
364
379
  let shareUrl;
365
380
  if (process.env.SHARE_SESSIONS === 'true') {
366
381
  const shareResult = await opencode.client.session.share({
367
- path: { id: sessionId }
382
+ sessionID: sessionId,
368
383
  });
369
384
  if (!shareResult.error && shareResult.data?.share?.url) {
370
385
  shareUrl = shareResult.data.share.url;
@@ -385,13 +400,12 @@ export async function createSession(_threadId, title = `Remote control session`)
385
400
  }
386
401
  // Send message - use promptAsync then poll for response
387
402
  export async function sendMessage(session, message, callbacks, threadId) {
388
- const TIMEOUT_MS = 5 * 60 * 1000; // 5 minute timeout
389
- const POLL_INTERVAL = 2000; // 2 seconds between polls
390
-
403
+ const TIMEOUT_MS = 5 * 60 * 1000;
404
+
391
405
  try {
392
406
  // Verify session is valid first
393
407
  try {
394
- const sessionCheck = await session.client.session.get({ path: { id: session.sessionId } });
408
+ const sessionCheck = await session.client.session.get({ sessionID: session.sessionId });
395
409
  if (sessionCheck.error) {
396
410
  console.error('[sendMessage] Session error:', sessionCheck.error);
397
411
  return '❌ 会话无效,请发送 /restart 重启';
@@ -400,19 +414,8 @@ export async function sendMessage(session, message, callbacks, threadId) {
400
414
  console.error('[sendMessage] Session check failed:', e.message);
401
415
  return '❌ 会话连接失败,请发送 /restart 重启';
402
416
  }
403
-
404
- // Get last message ID and count before sending
405
- let lastMsgId = null;
406
- let msgCountBefore = 0;
407
- try {
408
- const msgsBefore = await session.client.session.messages({ path: { id: session.sessionId } });
409
- if (msgsBefore.data?.length > 0) {
410
- lastMsgId = msgsBefore.data[msgsBefore.data.length - 1].info?.id;
411
- msgCountBefore = msgsBefore.data.length;
412
- }
413
- } catch { /* ignore */ }
414
-
415
- // Send message using promptAsync (non-blocking)
417
+
418
+ // Build prompt body
416
419
  const promptBody = {
417
420
  parts: [{ type: 'text', text: message }]
418
421
  };
@@ -428,124 +431,101 @@ export async function sendMessage(session, message, callbacks, threadId) {
428
431
  modelID: session.model.modelID,
429
432
  };
430
433
  }
431
- const sendResult = await session.client.session.promptAsync({
432
- path: { id: session.sessionId },
433
- body: promptBody,
434
- });
435
434
 
436
- // Poll for new response - keep going as long as new content keeps arriving
437
- const startTime = Date.now();
438
- let responseText = '';
439
- let hasToolActivity = false;
440
- let idleSince = 0; // 最后一次收到新内容的时间戳
441
- let lastStatus = '';
435
+ // Stream the response via session.prompt (POST /session/{sessionID}/message)
436
+ const abortController = new AbortController();
437
+ const timeoutId = setTimeout(() => abortController.abort(), TIMEOUT_MS);
442
438
 
443
- while (Date.now() - startTime < TIMEOUT_MS) {
444
- await new Promise(r => setTimeout(r, POLL_INTERVAL));
439
+ try {
440
+ const response = await session.client.session.prompt({
441
+ sessionID: session.sessionId,
442
+ parts: promptBody.parts,
443
+ ...(promptBody.model ? { model: promptBody.model } : {}),
444
+ }, {
445
+ parseAs: 'stream',
446
+ signal: abortController.signal,
447
+ });
445
448
 
446
- try {
447
- const msgsResult = await session.client.session.messages({
448
- path: { id: session.sessionId }
449
- });
449
+ if (response.error) {
450
+ return `❌ 发送失败: ${response.error}`;
451
+ }
450
452
 
451
- if (msgsResult.error) { console.error('[sendMessage] Messages error:', msgsResult.error); break; }
452
- if (!msgsResult.data?.length) continue;
453
+ const stream = response.data;
454
+ if (!stream) {
455
+ return '❌ 未收到响应流';
456
+ }
453
457
 
454
- const messages = msgsResult.data;
458
+ const reader = stream.getReader();
459
+ const decoder = new TextDecoder();
460
+ let rawJson = '';
461
+ let responseText = '';
455
462
 
456
- // 工具活动
457
- for (let i = msgCountBefore; i < messages.length; i++) {
458
- const msg = messages[i];
459
- if (msg.parts) for (const part of msg.parts) {
460
- if (part.type === 'tool_use' || part.type === 'tool_result') {
461
- hasToolActivity = true;
462
- callbacks?.onEvent?.({ type: 'tool.call', properties: { name: part.name || part.tool_name || 'unknown', input: part.input || {} } });
463
- break;
464
- }
465
- }
466
- if (hasToolActivity) break;
467
- }
463
+ while (true) {
464
+ const { done, value } = await reader.read();
465
+ if (done) break;
466
+ const chunk = decoder.decode(value, { stream: true });
467
+ if (!chunk) continue;
468
+ rawJson += chunk;
469
+ }
468
470
 
469
- // 收集所有新的 assistant 回复(累加,不丢内容)
470
- if (lastMsgId) {
471
- const idx = messages.findIndex(m => m.info?.id === lastMsgId);
472
- const startIdx = idx >= 0 ? idx + 1 : 0;
473
- const newParts = [];
474
- for (let i = startIdx; i < messages.length; i++) {
475
- const msg = messages[i];
476
- if (msg.info?.role === 'assistant' && msg.parts) {
477
- for (const p of msg.parts) {
478
- if (p.type === 'text' && p.text) newParts.push(p.text);
471
+ // Parse the full response JSON
472
+ if (isRawDebug()) console.log('[RAW]', rawJson);
473
+ try {
474
+ const parsed = JSON.parse(rawJson);
475
+ const t = parsed.info?.tokens || {};
476
+ const time = parsed.info?.time || {};
477
+ const elapsed = time.completed && time.created ? `${(time.completed - time.created) / 1000}s` : '?';
478
+ const cacheRead = t.cache?.read || 0;
479
+ const cacheWrite = t.cache?.write || 0;
480
+ const cacheRate = cacheRead + cacheWrite > 0 ? `${(cacheRead / (cacheRead + cacheWrite) * 100).toFixed(0)}%` : '-';
481
+ console.log(`[RESPONSE] ${parsed.info?.providerID}/${parsed.info?.modelID} │ ${elapsed} │ tokens=${t.total || '?'} (in=${t.input} out=${t.output} rsn=${t.reasoning}) │ cache ${cacheRate} │ finish=${parsed.info?.finish || '?'}`);
482
+ const meta = { modelID: parsed.info?.modelID, providerID: parsed.info?.providerID, tokens: t, parts: parsed.parts };
483
+ callbacks?.onResponseMeta?.(meta);
484
+ if (parsed.parts) {
485
+ for (const part of parsed.parts) {
486
+ if (part.type === 'text' && part.text) {
487
+ responseText += part.text;
488
+ callbacks?.onNewContent?.(part.text);
489
+ callbacks?.onTextDelta?.(part.text);
490
+ }
491
+ if (part.type === 'reasoning' && part.text) {
492
+ const cleaned = part.text.replace(/\n/g, ' ').trim();
493
+ console.log(`[REASONING] ${cleaned.slice(0, 300)}`);
494
+ if (thinkVisibleEnabled) {
495
+ responseText += `\n🤔 思考: ${cleaned}\n━━━━━━━━━━━━━━━━━━\n`;
496
+ callbacks?.onNewContent?.(`\n🤔 思考: ${cleaned}\n━━━━━━━━━━━━━━━━━━\n`);
479
497
  }
480
498
  }
481
499
  }
482
- const fullText = newParts.join('\n');
483
- if (fullText && fullText !== responseText) {
484
- const delta = fullText.slice(responseText.length);
485
- responseText = fullText;
486
- callbacks?.onTextDelta?.(delta);
487
- callbacks?.onNewContent?.(delta);
488
- idleSince = Date.now();
489
- continue;
490
- }
491
- }
492
-
493
- // 检查 AI 是否还在忙(thinking/pending_tool 说明还没干完)
494
- const latestStatus = msgsResult.data?.length ? msgsResult.data[msgsResult.data.length - 1]?.info?.status : '';
495
- if (latestStatus === 'thinking' || latestStatus === 'pending_tool') {
496
- idleSince = Date.now();
497
500
  }
498
- if (latestStatus) lastStatus = latestStatus;
499
- if (latestStatus && latestStatus !== lastReportedStatus) {
500
- lastReportedStatus = latestStatus;
501
- console.log(`[AI状态] ${latestStatus}`);
502
- }
503
-
504
- // 有回复后:等 30 秒无新内容且 AI 不忙才退出
505
- if (responseText && Date.now() - idleSince > 30000) {
506
- break;
501
+ if (!responseText && parsed.info?.finish) {
502
+ responseText = '[empty response]';
507
503
  }
508
504
  } catch (e) {
509
- console.warn('Poll error:', e.message);
505
+ console.error('[sendMessage] Failed to parse response:', e.message);
506
+ console.log('[RAW]', rawJson.slice(0, 1000));
507
+ responseText = rawJson;
510
508
  }
509
+
510
+ callbacks?.onStatusChange?.({ type: 'idle' });
511
+ return responseText;
512
+
513
+ } finally {
514
+ clearTimeout(timeoutId);
511
515
  }
512
-
513
- if (!responseText) {
514
- console.warn(' Timeout waiting for response, status:', lastStatus);
515
- // Try one more time with a fresh message query
516
- try {
517
- const finalMsgs = await session.client.session.messages({ path: { id: session.sessionId }, query: { limit: 50 } });
518
- if (finalMsgs.data?.length) {
519
- for (let i = finalMsgs.data.length - 1; i >= 0; i--) {
520
- const msg = finalMsgs.data[i];
521
- if (msg.info?.role === 'assistant' && msg.parts) {
522
- const textParts = msg.parts.filter(p => p.type === 'text' && p.text).map(p => p.text);
523
- if (textParts.length > 0) {
524
- responseText = textParts.join('\n');
525
- break;
526
- }
527
- }
528
- }
529
- }
530
- } catch { /* ignore */ }
531
-
532
- if (!responseText) {
533
- return '⏰ 请求超时,请重试';
534
- }
516
+ } catch (error) {
517
+ if (error.name === 'AbortError') {
518
+ console.warn('[sendMessage] 5min timeout, aborting stream');
519
+ return '⏰ 请求超时,请重试';
535
520
  }
536
-
537
- callbacks?.onStatusChange?.({ type: 'idle', hasToolActivity });
538
- return responseText;
539
- }
540
- catch (error) {
541
- console.error('Error sending message:', error);
521
+ console.error('[sendMessage] Error:', error);
542
522
  return `❌ Error: ${error instanceof Error ? error.message : 'Unknown error'}`;
543
523
  }
544
524
  }
545
525
  export async function getSession(session) {
546
526
  try {
547
527
  const result = await session.client.session.get({
548
- path: { id: session.sessionId }
528
+ sessionID: session.sessionId
549
529
  });
550
530
  if (result.error) {
551
531
  return null;
@@ -559,7 +539,7 @@ export async function getSession(session) {
559
539
  export async function shareSession(session) {
560
540
  try {
561
541
  const result = await session.client.session.share({
562
- path: { id: session.sessionId }
542
+ sessionID: session.sessionId
563
543
  });
564
544
  if (result.error || !result.data?.share?.url) {
565
545
  return null;
@@ -585,7 +565,7 @@ export async function checkConnection() {
585
565
  export async function abortSession(session) {
586
566
  try {
587
567
  await session.client.session.abort({
588
- path: { id: session.sessionId }
568
+ sessionID: session.sessionId
589
569
  });
590
570
  console.log(`🛑 Aborted session: ${session.sessionId}`);
591
571
  return true;
@@ -598,7 +578,7 @@ export async function abortSession(session) {
598
578
  export async function getSessionMessages(session, limit = 20) {
599
579
  try {
600
580
  const result = await session.client.session.messages({
601
- path: { id: session.sessionId }
581
+ sessionID: session.sessionId
602
582
  });
603
583
  if (result.error) {
604
584
  return null;
@@ -614,7 +594,7 @@ export async function resumeSession(sessionId, title = 'Resumed session') {
614
594
  try {
615
595
  const opencode = await initOpenCode();
616
596
  if (!opencode) return null;
617
- const getResult = await opencode.client.session.get({ path: { id: sessionId } });
597
+ const getResult = await opencode.client.session.get({ sessionID: sessionId });
618
598
  if (getResult.error) {
619
599
  console.warn(`Session ${sessionId} not found`);
620
600
  return null;
@@ -639,10 +619,9 @@ export async function listOpenCodeSessions() {
639
619
  return sessions.map(s => ({
640
620
  id: s.id,
641
621
  title: s.title || 'Untitled',
642
- status: s.status?.type || 'unknown',
643
622
  directory: s.directory || '',
644
- createdAt: s.created_at || 0,
645
- lastActivity: s.updated_at || 0,
623
+ createdAt: s.created_at || s.time?.created || 0,
624
+ lastActivity: s.updated_at || s.time?.updated || 0,
646
625
  }));
647
626
  }
648
627
  catch (error) {
@@ -652,7 +631,7 @@ export async function listOpenCodeSessions() {
652
631
  }
653
632
  export async function listOpenCodeSessionsFromServer(baseUrl) {
654
633
  try {
655
- const { createOpencodeClient } = await import('@opencode-ai/sdk');
634
+ const { createOpencodeClient } = await import('@opencode-ai/sdk/v2');
656
635
  const client = createOpencodeClient({
657
636
  baseUrl: baseUrl || 'http://localhost:4096',
658
637
  });
@@ -664,10 +643,9 @@ export async function listOpenCodeSessionsFromServer(baseUrl) {
664
643
  return sessions.map(s => ({
665
644
  id: s.id,
666
645
  title: s.title || 'Untitled',
667
- status: s.status?.type || 'unknown',
668
646
  directory: s.directory || '',
669
- createdAt: s.created_at || 0,
670
- lastActivity: s.updated_at || 0,
647
+ createdAt: s.created_at || s.time?.created || 0,
648
+ lastActivity: s.updated_at || s.time?.updated || 0,
671
649
  }));
672
650
  }
673
651
  catch (error) {
@@ -680,7 +658,7 @@ export async function createOpenCodeSession(title = 'New session') {
680
658
  const opencode = await initOpenCode();
681
659
  if (!opencode) return null;
682
660
  const result = await opencode.client.session.create({
683
- body: { title }
661
+ title
684
662
  });
685
663
  if (result.error) {
686
664
  return null;
@@ -704,7 +682,7 @@ export async function deleteOpenCodeSession(sessionId) {
704
682
  const opencode = await initOpenCode();
705
683
  if (!opencode) return false;
706
684
  const result = await opencode.client.session.delete({
707
- path: { id: sessionId }
685
+ sessionID: sessionId
708
686
  });
709
687
  if (result.error) {
710
688
  return false;
@@ -719,9 +697,9 @@ export async function deleteOpenCodeSession(sessionId) {
719
697
  }
720
698
  export async function renameOpenCodeSession(session, title) {
721
699
  try {
722
- const result = await session.client.session.patch({
723
- path: { id: session.sessionId },
724
- body: { title }
700
+ const result = await session.client.session.update({
701
+ sessionID: session.sessionId,
702
+ title,
725
703
  });
726
704
  if (result.error) {
727
705
  return false;
@@ -739,9 +717,9 @@ export async function forkSession(sessionId, messageID, directory) {
739
717
  const opencode = await initOpenCode();
740
718
  if (!opencode) return null;
741
719
  const result = await opencode.client.session.fork({
742
- path: { id: sessionId },
743
- body: { messageID },
744
- query: directory ? { directory } : {}
720
+ sessionID: sessionId,
721
+ messageID,
722
+ ...(directory ? { directory } : {}),
745
723
  });
746
724
  if (result.error) {
747
725
  console.warn(`Fork failed: ${result.error}`);
@@ -766,8 +744,9 @@ export async function revertSessionMessage(sessionId, messageID, partID) {
766
744
  const opencode = await initOpenCode();
767
745
  if (!opencode) return false;
768
746
  const result = await opencode.client.session.revert({
769
- path: { id: sessionId },
770
- body: { messageID, partID }
747
+ sessionID: sessionId,
748
+ messageID,
749
+ partID,
771
750
  });
772
751
  if (result.error) {
773
752
  console.warn(`Revert failed: ${result.error}`);
@@ -786,7 +765,7 @@ export async function unrevertSession(sessionId) {
786
765
  const opencode = await initOpenCode();
787
766
  if (!opencode) return false;
788
767
  const result = await opencode.client.session.unrevert({
789
- path: { id: sessionId }
768
+ sessionID: sessionId
790
769
  });
791
770
  if (result.error) {
792
771
  console.warn(`Unrevert failed: ${result.error}`);
@@ -805,9 +784,12 @@ export async function listProviders() {
805
784
  try {
806
785
  const opencode = await initOpenCode();
807
786
  if (!opencode) return null;
808
- const result = await opencode.client.provider.list();
809
- if (result.error || !result.data?.all) return null;
810
- return result.data.all;
787
+ // Config2.providers() GET /config/providers (same as v1)
788
+ const result = await opencode.client.config.providers();
789
+ if (result.error) return null;
790
+ // v2 returns { providers: [...] }, v1 returns { all: [...] }
791
+ const data = result.data?.providers || result.data?.all || result.data || [];
792
+ return Array.isArray(data) ? data : null;
811
793
  } catch (error) {
812
794
  console.error('Failed to list providers:', error.message);
813
795
  return null;
@@ -819,7 +801,7 @@ export async function updateGlobalModel(modelStr) {
819
801
  const opencode = await initOpenCode();
820
802
  if (!opencode) return false;
821
803
  const result = await opencode.client.config.update({
822
- body: { model: modelStr },
804
+ config: { model: modelStr },
823
805
  });
824
806
  if (result.error) {
825
807
  console.error('Failed to update model:', result.error);
@@ -24,9 +24,13 @@ export class ClaudeCodeAgentAdapter {
24
24
 
25
25
  async sendPrompt(_sessionId, prompt, history, options = {}) {
26
26
  const projectDir = options.projectDir;
27
- const contextualPrompt = this.buildContextualPrompt(prompt, history);
27
+ let cleanPrompt = prompt;
28
+ if (prompt.startsWith('-c')) {
29
+ cleanPrompt = prompt.slice(2).trim();
30
+ }
31
+ const contextualPrompt = this.buildContextualPrompt(cleanPrompt, history);
28
32
 
29
- const args = ['--print', contextualPrompt];
33
+ const args = ['--print', '-c', contextualPrompt];
30
34
 
31
35
  return this.callClaude(args, projectDir);
32
36
  }
@@ -23,10 +23,14 @@ export class OpenCodeAgentAdapter {
23
23
  }
24
24
 
25
25
  async sendPrompt(_sessionId, prompt, history, options = {}) {
26
- const contextualPrompt = this.buildContextualPrompt(prompt, history);
26
+ let cleanPrompt = prompt;
27
+ if (prompt.startsWith('-c')) {
28
+ cleanPrompt = prompt.slice(2).trim();
29
+ }
30
+ const contextualPrompt = this.buildContextualPrompt(cleanPrompt, history);
27
31
  return this.callOpenCode(contextualPrompt);
28
32
  }
29
-
33
+
30
34
  buildContextualPrompt(prompt, history) {
31
35
  if (!history || history.length === 0) return prompt;
32
36
  const historyText = history
@@ -34,7 +38,7 @@ export class OpenCodeAgentAdapter {
34
38
  .join('\n\n');
35
39
  return `Previous conversation:\n${historyText}\n\nCurrent request: ${prompt}`;
36
40
  }
37
-
41
+
38
42
  extractErrorMessage(stdout, stderr) {
39
43
  const lines = [...stdout.trim().split('\n'), ...stderr.trim().split('\n')]
40
44
  .map(l => l.trim()).filter(Boolean)
@@ -45,10 +49,10 @@ export class OpenCodeAgentAdapter {
45
49
  .find(l => /Error|error|ERROR|^\d{3}/.test(l));
46
50
  return first || null;
47
51
  }
48
-
52
+
49
53
  callOpenCode(prompt) {
50
54
  return new Promise((resolve) => {
51
- const proc = spawn('opencode', ['run', '--format', 'json', prompt], {
55
+ const proc = spawn('opencode', ['run', '--format', 'json', '-c', prompt], {
52
56
  stdio: ['ignore', 'pipe', 'pipe'],
53
57
  shell: true,
54
58
  });
@@ -38,12 +38,9 @@ export class TelegramAdapter {
38
38
  async sendCommandMenu(threadId, title) {
39
39
  if (!this.bot) return;
40
40
  const groups = [
41
- ['🟢 常用', ['/help', '/status', '/start', '/reset']],
42
- ['🔄 任务', ['/loop', '/refresh', '/restart']],
43
- ['🤖 AI', ['/model', '/agents', '/oc', '/cc']],
44
- ['🧠 专家', ['/tutorial', '/z', '/diagnose']],
45
- ['📂 会话', ['/sessions', '/delsessions', '/copy', '/revert']],
46
- ['⬆️ 文件', ['/upload', '/delete']],
41
+ ['/help', '/start', '/reset', '/diagnose'],
42
+ ['/restart', '/model', '/oc', '/cc'],
43
+ ['/cx', '/copilot'],
47
44
  ];
48
45
  const keyboard = [];
49
46
  for (const [, cmds] of groups) {
@@ -1,8 +1,8 @@
1
1
  import { registry } from '../core/registry.js';
2
- import { sessionManager } from '../core/session.js';
3
2
  import { initOpenCode, createSession, sendMessage as sendToOpenCode, checkConnection } from '../opencode/client.js';
4
3
  import { parseMessage, routeMessage } from '../core/router.js';
5
4
  import { telegramAdapter } from './adapter.js';
5
+ import { splitMessage } from '../utils/message-split.js';
6
6
 
7
7
  export async function startBot() {
8
8
  const { loadConfig } = await import('../core/config.js');
@@ -17,7 +17,6 @@ export async function startBot() {
17
17
  process.exit(1);
18
18
  }
19
19
 
20
- await sessionManager.start();
21
20
  await registry.loadBuiltInPlugins();
22
21
  await telegramAdapter.start(config);
23
22
 
@@ -68,10 +67,6 @@ export async function startBot() {
68
67
  if (parsed.type === 'command' && parsed.command === 'reset') {
69
68
  openCodeSessions.delete(message.threadId);
70
69
  opencodeSessionId = null;
71
- try {
72
- const session = await sessionManager.getExistingSession(platform, channelId, message.threadId);
73
- if (session) await sessionManager.resetConversation(platform, channelId, message.threadId);
74
- } catch (e) { console.warn('[Telegram] Reset error:', e.message); }
75
70
  }
76
71
 
77
72
  if (parsed.type === 'default') {
@@ -53,7 +53,14 @@ async function apiFetch(params) {
53
53
  }
54
54
  catch (err) {
55
55
  clearTimeout(t);
56
- console.error(`[apiFetch] ${params.label} error:`, err.message);
56
+ const details = {
57
+ url: url.toString(),
58
+ label: params.label,
59
+ message: err.message,
60
+ cause: err.cause ? (err.cause.message || String(err.cause)) : undefined,
61
+ code: err.code,
62
+ };
63
+ console.error(`[apiFetch] ${params.label} error:`, JSON.stringify(details));
57
64
  throw err;
58
65
  }
59
66
  }
@@ -61,7 +68,7 @@ async function apiFetch(params) {
61
68
  // Login APIs
62
69
  // ---------------------------------------------------------------------------
63
70
  /**
64
- * Fetch QR code for login
71
+ * Fetch QR code for login (admin auth)
65
72
  */
66
73
  export async function fetchQRCode(baseUrl = DEFAULT_BASE_URL, botType = '3') {
67
74
  const base = ensureTrailingSlash(baseUrl);