@yeaft/webchat-agent 0.1.160 → 0.1.162

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/claude.js CHANGED
@@ -5,17 +5,22 @@ import { sendConversationList, sendOutput, sendError, handleAskUserQuestion } fr
5
5
  /**
6
6
  * Determine maxContextTokens and autoCompactThreshold from model name.
7
7
  * Returns defaults suitable for the model's context window size.
8
+ *
9
+ * NOTE (2026-03): Opus 4.6 / Sonnet 4 have 200k context windows.
10
+ * Claude Code handles its own compaction internally, so we set the
11
+ * default threshold to 200k (effectively never triggers our custom compact).
12
+ * The thresholds are kept as parameters in case we need to re-enable later.
8
13
  */
9
14
  export function getModelContextConfig(modelName) {
10
- if (!modelName) return { maxContext: 128000, compactThreshold: 110000 };
15
+ if (!modelName) return { maxContext: 200000, compactThreshold: 200000 };
11
16
  const name = modelName.toLowerCase();
12
17
  // Explicit 1M context indicators
13
18
  if (name.includes('1m') || name.includes('1000k')) {
14
19
  return { maxContext: 1000000, compactThreshold: 256000 };
15
20
  }
16
- // Default: 128kCopilot API models (Sonnet 4, Opus 4, Claude 3.5 etc.)
17
- // report 200k context but actual usable window is 128k
18
- return { maxContext: 128000, compactThreshold: 110000 };
21
+ // Default: 200kOpus 4.6 / Sonnet 4 context window.
22
+ // Claude Code manages its own compaction; we no longer need custom compact logic.
23
+ return { maxContext: 200000, compactThreshold: 200000 };
19
24
  }
20
25
 
21
26
  /**
@@ -484,39 +489,41 @@ async function processClaudeOutput(conversationId, claudeQuery, state) {
484
489
  } else if (resultHandled) {
485
490
  // Turn 已正常完成,进程退出产生的 error 不发送给用户
486
491
  console.warn(`[SDK] Ignoring post-result error for ${conversationId}: ${error.message}`);
487
- } else if (isPromptTokenOverflow(error.message) && state.claudeSessionId && !state._compactRetried) {
488
- // 兜底:prompt token 溢出 自动 compact + 重试(而非暴露 raw API error 给用户)
489
- console.warn(`[SDK] Prompt token overflow for ${conversationId}, auto-compact + retry`);
490
- const savedSessionId = state.claudeSessionId;
491
- const savedLastMsg = state._lastUserMessage;
492
-
493
- ctx.sendToServer({
494
- type: 'compact_status',
495
- conversationId,
496
- status: 'compacting',
497
- message: 'Context too long, auto-compacting and retrying...'
498
- });
499
-
500
- // 重启 SDK(startClaudeQuery 会先 abort 当前 state,使 finally 中 isStale=true)
501
- try {
502
- const newState = await startClaudeQuery(conversationId, state.workDir, savedSessionId);
503
- newState._compactRetried = true; // 防止无限重试
504
- newState.turnActive = true;
505
- newState.turnResultReceived = false;
506
-
507
- // compact,再重试原始消息(如果有的话)
508
- if (savedLastMsg) {
509
- newState._pendingUserMessage = savedLastMsg;
510
- }
511
- newState.inputStream.enqueue({
512
- type: 'user',
513
- message: { role: 'user', content: '/compact' }
514
- });
515
- sendConversationList();
516
- } catch (retryError) {
517
- console.error(`[SDK] Compact-retry failed for ${conversationId}:`, retryError.message);
518
- sendError(conversationId, `Context too long. Auto-compact failed: ${retryError.message}`);
519
- }
492
+ // DISABLED (2026-03): Opus 4.6 has 200k context. Claude Code handles its own compaction.
493
+ // Keeping code for reference; re-enable if we ever need custom overflow recovery.
494
+ // } else if (isPromptTokenOverflow(error.message) && state.claudeSessionId && !state._compactRetried) {
495
+ // // 兜底:prompt token 溢出 → 自动 compact + 重试(而非暴露 raw API error 给用户)
496
+ // console.warn(`[SDK] Prompt token overflow for ${conversationId}, auto-compact + retry`);
497
+ // const savedSessionId = state.claudeSessionId;
498
+ // const savedLastMsg = state._lastUserMessage;
499
+ //
500
+ // ctx.sendToServer({
501
+ // type: 'compact_status',
502
+ // conversationId,
503
+ // status: 'compacting',
504
+ // message: 'Context too long, auto-compacting and retrying...'
505
+ // });
506
+ //
507
+ // // 重启 SDK(startClaudeQuery 会先 abort 当前 state,使 finally 中 isStale=true)
508
+ // try {
509
+ // const newState = await startClaudeQuery(conversationId, state.workDir, savedSessionId);
510
+ // newState._compactRetried = true; // 防止无限重试
511
+ // newState.turnActive = true;
512
+ // newState.turnResultReceived = false;
513
+ //
514
+ // // compact,再重试原始消息(如果有的话)
515
+ // if (savedLastMsg) {
516
+ // newState._pendingUserMessage = savedLastMsg;
517
+ // }
518
+ // newState.inputStream.enqueue({
519
+ // type: 'user',
520
+ // message: { role: 'user', content: '/compact' }
521
+ // });
522
+ // sendConversationList();
523
+ // } catch (retryError) {
524
+ // console.error(`[SDK] Compact-retry failed for ${conversationId}:`, retryError.message);
525
+ // sendError(conversationId, `Context too long. Auto-compact failed: ${retryError.message}`);
526
+ // }
520
527
  } else {
521
528
  console.error(`[SDK] Error for ${conversationId}:`, error.message);
522
529
  sendError(conversationId, error.message);
package/conversation.js CHANGED
@@ -443,35 +443,37 @@ export async function handleUserInput(msg) {
443
443
 
444
444
  console.log(`[${conversationId}] Sending: ${prompt.substring(0, 100)}...`);
445
445
 
446
+ // DISABLED (2026-03): Opus 4.6 has 200k context. Claude Code handles its own compaction.
447
+ // Keeping code for reference; re-enable if we ever need custom pre-send compact.
446
448
  // ★ Pre-send compact check: estimate total tokens and compact before sending if needed
447
- const autoCompactThreshold = state.autoCompactThreshold || ctx.CONFIG?.autoCompactThreshold || 110000;
448
- const lastInputTokens = state.lastResultInputTokens || 0;
449
- const lastOutputTokens = state.lastResultOutputTokens || 0;
450
- const estimatedNewTokens = Math.ceil(effectivePrompt.length / 3); // conservative: ~3 chars per token
451
- // Include output_tokens: the assistant's last output becomes part of context for the next turn
452
- const estimatedTotal = lastInputTokens + lastOutputTokens + estimatedNewTokens;
453
-
454
- if (estimatedTotal > autoCompactThreshold && state.inputStream) {
455
- console.log(`[${conversationId}] Pre-send compact: estimated ${estimatedTotal} tokens (input: ${lastInputTokens} + output: ${lastOutputTokens} + new: ~${estimatedNewTokens}) exceeds threshold ${autoCompactThreshold}`);
456
- ctx.sendToServer({
457
- type: 'compact_status',
458
- conversationId,
459
- status: 'compacting',
460
- message: `Auto-compacting before send: estimated ${estimatedTotal} tokens (threshold: ${autoCompactThreshold})`
461
- });
462
- // Send /compact first, then the user message will be sent after compact completes
463
- // by storing it as a pending message
464
- state._pendingUserMessage = userMessage;
465
- state._pendingDisplayMessage = displayMessage;
466
- state.turnActive = true;
467
- state.turnResultReceived = false;
468
- sendConversationList();
469
- state.inputStream.enqueue({
470
- type: 'user',
471
- message: { role: 'user', content: '/compact' }
472
- });
473
- return;
474
- }
449
+ // const autoCompactThreshold = state.autoCompactThreshold || ctx.CONFIG?.autoCompactThreshold || 110000;
450
+ // const lastInputTokens = state.lastResultInputTokens || 0;
451
+ // const lastOutputTokens = state.lastResultOutputTokens || 0;
452
+ // const estimatedNewTokens = Math.ceil(effectivePrompt.length / 3); // conservative: ~3 chars per token
453
+ // // Include output_tokens: the assistant's last output becomes part of context for the next turn
454
+ // const estimatedTotal = lastInputTokens + lastOutputTokens + estimatedNewTokens;
455
+ //
456
+ // if (estimatedTotal > autoCompactThreshold && state.inputStream) {
457
+ // console.log(`[${conversationId}] Pre-send compact: estimated ${estimatedTotal} tokens (input: ${lastInputTokens} + output: ${lastOutputTokens} + new: ~${estimatedNewTokens}) exceeds threshold ${autoCompactThreshold}`);
458
+ // ctx.sendToServer({
459
+ // type: 'compact_status',
460
+ // conversationId,
461
+ // status: 'compacting',
462
+ // message: `Auto-compacting before send: estimated ${estimatedTotal} tokens (threshold: ${autoCompactThreshold})`
463
+ // });
464
+ // // Send /compact first, then the user message will be sent after compact completes
465
+ // // by storing it as a pending message
466
+ // state._pendingUserMessage = userMessage;
467
+ // state._pendingDisplayMessage = displayMessage;
468
+ // state.turnActive = true;
469
+ // state.turnResultReceived = false;
470
+ // sendConversationList();
471
+ // state.inputStream.enqueue({
472
+ // type: 'user',
473
+ // message: { role: 'user', content: '/compact' }
474
+ // });
475
+ // return;
476
+ // }
475
477
 
476
478
  state.turnActive = true;
477
479
  state.turnResultReceived = false; // 重置 per-turn 去重标志
package/crew/routing.js CHANGED
@@ -256,38 +256,40 @@ export async function dispatchToRole(session, roleName, content, fromSource, tas
256
256
  timestamp: Date.now()
257
257
  });
258
258
 
259
+ // DISABLED (2026-03): Opus 4.6 has 200k context. Claude Code handles its own compaction.
260
+ // Keeping code for reference; re-enable if we ever need custom crew pre-send compact.
259
261
  // ★ Pre-send compact check: estimate total tokens and clear+rebuild if needed
260
- const autoCompactThreshold = ctx.CONFIG?.autoCompactThreshold || 100000;
261
- const lastInputTokens = roleState.lastInputTokens || 0;
262
- const estimatedNewTokens = Math.ceil((typeof content === 'string' ? content.length : 0) / 3);
263
- const estimatedTotal = lastInputTokens + estimatedNewTokens;
264
-
265
- if (lastInputTokens > 0 && estimatedTotal > autoCompactThreshold) {
266
- console.log(`[Crew] Pre-send compact for ${roleName}: estimated ${estimatedTotal} tokens (last: ${lastInputTokens} + new: ~${estimatedNewTokens}) exceeds threshold ${autoCompactThreshold}`);
267
-
268
- // Save work summary before clearing (use lastTurnText since accumulatedText is cleared after result)
269
- await saveRoleWorkSummary(session, roleName, roleState.lastTurnText || roleState.accumulatedText || '').catch(e =>
270
- console.warn(`[Crew] Failed to save work summary for ${roleName}:`, e.message));
271
-
272
- // Clear role session and rebuild
273
- await clearRoleSessionId(session.sharedDir, roleName);
274
- roleState.claudeSessionId = null;
275
-
276
- if (roleState.abortController) roleState.abortController.abort();
277
- roleState.query = null;
278
- roleState.inputStream = null;
279
-
280
- sendCrewMessage({
281
- type: 'crew_role_cleared',
282
- sessionId: session.id,
283
- role: roleName,
284
- contextPercentage: Math.round((lastInputTokens / (ctx.CONFIG?.maxContextTokens || 128000)) * 100),
285
- reason: 'pre_send_compact'
286
- });
287
-
288
- // Recreate the query (fresh Claude process)
289
- roleState = await createRoleQuery(session, roleName);
290
- }
262
+ // const autoCompactThreshold = ctx.CONFIG?.autoCompactThreshold || 100000;
263
+ // const lastInputTokens = roleState.lastInputTokens || 0;
264
+ // const estimatedNewTokens = Math.ceil((typeof content === 'string' ? content.length : 0) / 3);
265
+ // const estimatedTotal = lastInputTokens + estimatedNewTokens;
266
+ //
267
+ // if (lastInputTokens > 0 && estimatedTotal > autoCompactThreshold) {
268
+ // console.log(`[Crew] Pre-send compact for ${roleName}: estimated ${estimatedTotal} tokens (last: ${lastInputTokens} + new: ~${estimatedNewTokens}) exceeds threshold ${autoCompactThreshold}`);
269
+ //
270
+ // // Save work summary before clearing (use lastTurnText since accumulatedText is cleared after result)
271
+ // await saveRoleWorkSummary(session, roleName, roleState.lastTurnText || roleState.accumulatedText || '').catch(e =>
272
+ // console.warn(`[Crew] Failed to save work summary for ${roleName}:`, e.message));
273
+ //
274
+ // // Clear role session and rebuild
275
+ // await clearRoleSessionId(session.sharedDir, roleName);
276
+ // roleState.claudeSessionId = null;
277
+ //
278
+ // if (roleState.abortController) roleState.abortController.abort();
279
+ // roleState.query = null;
280
+ // roleState.inputStream = null;
281
+ //
282
+ // sendCrewMessage({
283
+ // type: 'crew_role_cleared',
284
+ // sessionId: session.id,
285
+ // role: roleName,
286
+ // contextPercentage: Math.round((lastInputTokens / (ctx.CONFIG?.maxContextTokens || 128000)) * 100),
287
+ // reason: 'pre_send_compact'
288
+ // });
289
+ //
290
+ // // Recreate the query (fresh Claude process)
291
+ // roleState = await createRoleQuery(session, roleName);
292
+ // }
291
293
 
292
294
  // P1-4: 守卫 stream.enqueue — stream 可能已被 abort 关闭
293
295
  roleState.lastDispatchContent = content;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.160",
3
+ "version": "0.1.162",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",