@hybridaione/hybridclaw 0.1.21 → 0.1.24

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.
Files changed (113) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/README.md +50 -8
  3. package/config.example.json +3 -0
  4. package/container/package-lock.json +2 -2
  5. package/container/package.json +1 -1
  6. package/container/src/browser-tools.ts +53 -3
  7. package/container/src/hybridai-client.ts +270 -8
  8. package/container/src/index.ts +66 -3
  9. package/container/src/token-usage.ts +89 -0
  10. package/container/src/tools.ts +9 -2
  11. package/container/src/types.ts +19 -0
  12. package/container/src/web-fetch.ts +98 -7
  13. package/dist/agent.d.ts +1 -1
  14. package/dist/agent.d.ts.map +1 -1
  15. package/dist/agent.js +2 -2
  16. package/dist/agent.js.map +1 -1
  17. package/dist/chunk.d.ts +6 -0
  18. package/dist/chunk.d.ts.map +1 -0
  19. package/dist/chunk.js +129 -0
  20. package/dist/chunk.js.map +1 -0
  21. package/dist/container-runner.d.ts +1 -1
  22. package/dist/container-runner.d.ts.map +1 -1
  23. package/dist/container-runner.js +25 -1
  24. package/dist/container-runner.js.map +1 -1
  25. package/dist/conversation.d.ts +4 -0
  26. package/dist/conversation.d.ts.map +1 -1
  27. package/dist/conversation.js +13 -3
  28. package/dist/conversation.js.map +1 -1
  29. package/dist/discord-stream.d.ts +32 -0
  30. package/dist/discord-stream.d.ts.map +1 -0
  31. package/dist/discord-stream.js +196 -0
  32. package/dist/discord-stream.js.map +1 -0
  33. package/dist/discord.d.ts +9 -2
  34. package/dist/discord.d.ts.map +1 -1
  35. package/dist/discord.js +452 -23
  36. package/dist/discord.js.map +1 -1
  37. package/dist/gateway-client.d.ts.map +1 -1
  38. package/dist/gateway-client.js +5 -0
  39. package/dist/gateway-client.js.map +1 -1
  40. package/dist/gateway-service.d.ts +1 -0
  41. package/dist/gateway-service.d.ts.map +1 -1
  42. package/dist/gateway-service.js +60 -2
  43. package/dist/gateway-service.js.map +1 -1
  44. package/dist/gateway-types.d.ts +7 -1
  45. package/dist/gateway-types.d.ts.map +1 -1
  46. package/dist/gateway-types.js.map +1 -1
  47. package/dist/gateway.js +55 -4
  48. package/dist/gateway.js.map +1 -1
  49. package/dist/health.d.ts.map +1 -1
  50. package/dist/health.js +7 -0
  51. package/dist/health.js.map +1 -1
  52. package/dist/heartbeat.d.ts.map +1 -1
  53. package/dist/heartbeat.js +20 -0
  54. package/dist/heartbeat.js.map +1 -1
  55. package/dist/observability-ingest.d.ts.map +1 -1
  56. package/dist/observability-ingest.js +26 -0
  57. package/dist/observability-ingest.js.map +1 -1
  58. package/dist/prompt-hooks.d.ts +2 -0
  59. package/dist/prompt-hooks.d.ts.map +1 -1
  60. package/dist/prompt-hooks.js +29 -0
  61. package/dist/prompt-hooks.js.map +1 -1
  62. package/dist/runtime-config.d.ts +3 -0
  63. package/dist/runtime-config.d.ts.map +1 -1
  64. package/dist/runtime-config.js +17 -1
  65. package/dist/runtime-config.js.map +1 -1
  66. package/dist/scheduled-task-runner.d.ts.map +1 -1
  67. package/dist/scheduled-task-runner.js +20 -0
  68. package/dist/scheduled-task-runner.js.map +1 -1
  69. package/dist/session-maintenance.d.ts.map +1 -1
  70. package/dist/session-maintenance.js +1 -0
  71. package/dist/session-maintenance.js.map +1 -1
  72. package/dist/skills-guard.d.ts +36 -0
  73. package/dist/skills-guard.d.ts.map +1 -0
  74. package/dist/skills-guard.js +607 -0
  75. package/dist/skills-guard.js.map +1 -0
  76. package/dist/skills.d.ts +13 -2
  77. package/dist/skills.d.ts.map +1 -1
  78. package/dist/skills.js +494 -59
  79. package/dist/skills.js.map +1 -1
  80. package/dist/token-efficiency.d.ts +41 -0
  81. package/dist/token-efficiency.d.ts.map +1 -0
  82. package/dist/token-efficiency.js +164 -0
  83. package/dist/token-efficiency.js.map +1 -0
  84. package/dist/types.d.ts +11 -0
  85. package/dist/types.d.ts.map +1 -1
  86. package/dist/workspace.d.ts.map +1 -1
  87. package/dist/workspace.js +2 -1
  88. package/dist/workspace.js.map +1 -1
  89. package/docs/index.html +33 -7
  90. package/package.json +1 -1
  91. package/src/agent.ts +15 -1
  92. package/src/chunk.ts +153 -0
  93. package/src/container-runner.ts +24 -0
  94. package/src/conversation.ts +28 -4
  95. package/src/discord-stream.ts +240 -0
  96. package/src/discord.ts +517 -23
  97. package/src/gateway-client.ts +7 -0
  98. package/src/gateway-service.ts +72 -1
  99. package/src/gateway-types.ts +12 -1
  100. package/src/gateway.ts +65 -4
  101. package/src/health.ts +8 -0
  102. package/src/heartbeat.ts +20 -0
  103. package/src/observability-ingest.ts +24 -0
  104. package/src/prompt-hooks.ts +29 -0
  105. package/src/runtime-config.ts +18 -1
  106. package/src/scheduled-task-runner.ts +20 -0
  107. package/src/session-maintenance.ts +1 -0
  108. package/src/skills-guard.ts +736 -0
  109. package/src/skills.ts +570 -61
  110. package/src/token-efficiency.ts +228 -0
  111. package/src/types.ts +12 -0
  112. package/src/workspace.ts +2 -2
  113. package/.hybridclaw/container-image-state.json +0 -5
@@ -1,8 +1,15 @@
1
1
  import path from 'path';
2
2
 
3
3
  import { emitRuntimeEvent, runAfterToolHooks, runBeforeToolHooks } from './extensions.js';
4
- import { callHybridAI, HybridAIRequestError } from './hybridai-client.js';
4
+ import { callHybridAI, callHybridAIStream, HybridAIRequestError } from './hybridai-client.js';
5
5
  import { waitForInput, writeOutput } from './ipc.js';
6
+ import {
7
+ accumulateApiUsage,
8
+ createTokenUsageStats,
9
+ estimateMessageTokens,
10
+ estimateTextTokens,
11
+ finalizeTokenUsage,
12
+ } from './token-usage.js';
6
13
  import { executeTool, getPendingSideEffects, resetSideEffects, setModelContext, setScheduledTasks, setSessionContext, TOOL_DEFINITIONS } from './tools.js';
7
14
  import type { ArtifactMetadata, ChatMessage, ContainerInput, ContainerOutput, ToolDefinition, ToolExecution } from './types.js';
8
15
 
@@ -58,6 +65,12 @@ function sleep(ms: number): Promise<void> {
58
65
  return new Promise((resolve) => setTimeout(resolve, ms));
59
66
  }
60
67
 
68
+ function emitStreamDelta(delta: string): void {
69
+ if (!delta) return;
70
+ const payload = Buffer.from(delta, 'utf-8').toString('base64');
71
+ console.error(`[stream] ${payload}`);
72
+ }
73
+
61
74
  function isRetryableError(err: unknown): boolean {
62
75
  if (err instanceof HybridAIRequestError) {
63
76
  return err.status === 429 || (err.status >= 500 && err.status <= 504);
@@ -144,8 +157,18 @@ async function callHybridAIWithRetry(params: {
144
157
  enableRag: boolean;
145
158
  history: ChatMessage[];
146
159
  tools: ToolDefinition[];
160
+ onTextDelta?: (delta: string) => void;
147
161
  }): Promise<Awaited<ReturnType<typeof callHybridAI>>> {
148
- const { baseUrl, apiKey, model, chatbotId, enableRag, history, tools } = params;
162
+ const {
163
+ baseUrl,
164
+ apiKey,
165
+ model,
166
+ chatbotId,
167
+ enableRag,
168
+ history,
169
+ tools,
170
+ onTextDelta,
171
+ } = params;
149
172
  let attempt = 0;
150
173
  let delayMs = RETRY_BASE_DELAY_MS;
151
174
 
@@ -153,7 +176,31 @@ async function callHybridAIWithRetry(params: {
153
176
  attempt += 1;
154
177
  await emitRuntimeEvent({ event: 'before_model_call', attempt });
155
178
  try {
156
- const response = await callHybridAI(baseUrl, apiKey, model, chatbotId, enableRag, history, tools);
179
+ let response;
180
+ if (onTextDelta) {
181
+ try {
182
+ response = await callHybridAIStream(
183
+ baseUrl,
184
+ apiKey,
185
+ model,
186
+ chatbotId,
187
+ enableRag,
188
+ history,
189
+ tools,
190
+ onTextDelta,
191
+ );
192
+ } catch (streamErr) {
193
+ const fallbackEligible =
194
+ streamErr instanceof HybridAIRequestError
195
+ && streamErr.status >= 400
196
+ && streamErr.status < 500
197
+ && streamErr.status !== 429;
198
+ if (!fallbackEligible) throw streamErr;
199
+ response = await callHybridAI(baseUrl, apiKey, model, chatbotId, enableRag, history, tools);
200
+ }
201
+ } else {
202
+ response = await callHybridAI(baseUrl, apiKey, model, chatbotId, enableRag, history, tools);
203
+ }
157
204
  await emitRuntimeEvent({ event: 'after_model_call', attempt, toolCallCount: response.choices[0]?.message?.tool_calls?.length || 0 });
158
205
  return response;
159
206
  } catch (err) {
@@ -189,10 +236,13 @@ async function processRequest(
189
236
  const toolExecutions: ToolExecution[] = [];
190
237
  const artifacts: ArtifactMetadata[] = [];
191
238
  const artifactPaths = new Set<string>();
239
+ const tokenUsage = createTokenUsageStats();
192
240
  let iterations = 0;
193
241
 
194
242
  while (iterations < MAX_ITERATIONS) {
195
243
  iterations++;
244
+ tokenUsage.modelCalls += 1;
245
+ tokenUsage.estimatedPromptTokens += estimateMessageTokens(history);
196
246
 
197
247
  let response;
198
248
  try {
@@ -204,6 +254,7 @@ async function processRequest(
204
254
  enableRag,
205
255
  history,
206
256
  tools,
257
+ onTextDelta: emitStreamDelta,
207
258
  });
208
259
  } catch (err) {
209
260
  const failed: ContainerOutput = {
@@ -212,12 +263,15 @@ async function processRequest(
212
263
  toolsUsed,
213
264
  ...(artifacts.length > 0 ? { artifacts } : {}),
214
265
  toolExecutions,
266
+ tokenUsage: finalizeTokenUsage(tokenUsage),
215
267
  error: `API error: ${err instanceof Error ? err.message : String(err)}`,
216
268
  };
217
269
  await emitRuntimeEvent({ event: 'turn_end', status: failed.status, toolsUsed });
218
270
  return failed;
219
271
  }
220
272
 
273
+ accumulateApiUsage(tokenUsage, response);
274
+
221
275
  const choice = response.choices[0];
222
276
  if (!choice) {
223
277
  const failed: ContainerOutput = {
@@ -226,12 +280,18 @@ async function processRequest(
226
280
  toolsUsed,
227
281
  ...(artifacts.length > 0 ? { artifacts } : {}),
228
282
  toolExecutions,
283
+ tokenUsage: finalizeTokenUsage(tokenUsage),
229
284
  error: 'No response from API',
230
285
  };
231
286
  await emitRuntimeEvent({ event: 'turn_end', status: failed.status, toolsUsed });
232
287
  return failed;
233
288
  }
234
289
 
290
+ tokenUsage.estimatedCompletionTokens += estimateTextTokens(choice.message.content);
291
+ if (choice.message.tool_calls?.length) {
292
+ tokenUsage.estimatedCompletionTokens += estimateTextTokens(JSON.stringify(choice.message.tool_calls));
293
+ }
294
+
235
295
  const assistantMessage: ChatMessage = {
236
296
  role: 'assistant',
237
297
  content: choice.message.content,
@@ -251,6 +311,7 @@ async function processRequest(
251
311
  toolsUsed: [...new Set(toolsUsed)],
252
312
  ...(artifacts.length > 0 ? { artifacts } : {}),
253
313
  toolExecutions,
314
+ tokenUsage: finalizeTokenUsage(tokenUsage),
254
315
  };
255
316
  await emitRuntimeEvent({ event: 'turn_end', status: completed.status, toolsUsed: completed.toolsUsed });
256
317
  return completed;
@@ -293,6 +354,7 @@ async function processRequest(
293
354
  toolsUsed,
294
355
  ...(artifacts.length > 0 ? { artifacts } : {}),
295
356
  toolExecutions,
357
+ tokenUsage: finalizeTokenUsage(tokenUsage),
296
358
  error: result,
297
359
  };
298
360
  await emitRuntimeEvent({ event: 'turn_end', status: failed.status, toolsUsed });
@@ -308,6 +370,7 @@ async function processRequest(
308
370
  toolsUsed: [...new Set(toolsUsed)],
309
371
  ...(artifacts.length > 0 ? { artifacts } : {}),
310
372
  toolExecutions,
373
+ tokenUsage: finalizeTokenUsage(tokenUsage),
311
374
  };
312
375
  await emitRuntimeEvent({ event: 'turn_end', status: completed.status, toolsUsed: completed.toolsUsed });
313
376
  return completed;
@@ -0,0 +1,89 @@
1
+ import type { ChatCompletionResponse, ChatMessage, TokenUsageStats } from './types.js';
2
+
3
+ const CHARS_PER_TOKEN = 4;
4
+
5
+ function parseUsageNumber(value: unknown): number {
6
+ if (typeof value === 'number' && Number.isFinite(value)) {
7
+ return Math.max(0, Math.floor(value));
8
+ }
9
+ if (typeof value === 'string' && value.trim()) {
10
+ const parsed = Number.parseInt(value, 10);
11
+ if (Number.isFinite(parsed)) return Math.max(0, parsed);
12
+ }
13
+ return 0;
14
+ }
15
+
16
+ export function createTokenUsageStats(): TokenUsageStats {
17
+ return {
18
+ modelCalls: 0,
19
+ apiUsageAvailable: false,
20
+ apiPromptTokens: 0,
21
+ apiCompletionTokens: 0,
22
+ apiTotalTokens: 0,
23
+ estimatedPromptTokens: 0,
24
+ estimatedCompletionTokens: 0,
25
+ estimatedTotalTokens: 0,
26
+ };
27
+ }
28
+
29
+ export function estimateTextTokens(text: string | null | undefined): number {
30
+ const normalized = typeof text === 'string' ? text : '';
31
+ if (!normalized) return 0;
32
+ return Math.max(1, Math.ceil(normalized.length / CHARS_PER_TOKEN));
33
+ }
34
+
35
+ export function estimateMessageTokens(messages: ChatMessage[]): number {
36
+ if (!Array.isArray(messages) || messages.length === 0) return 0;
37
+
38
+ let total = 2;
39
+ for (const message of messages) {
40
+ total += 4;
41
+ total += estimateTextTokens(message.role);
42
+ total += estimateTextTokens(message.content);
43
+ if (message.tool_calls) total += estimateTextTokens(JSON.stringify(message.tool_calls));
44
+ if (message.tool_call_id) total += estimateTextTokens(message.tool_call_id);
45
+ }
46
+ return total;
47
+ }
48
+
49
+ export function accumulateApiUsage(
50
+ stats: TokenUsageStats,
51
+ response: ChatCompletionResponse,
52
+ ): void {
53
+ const usage = response.usage;
54
+ if (!usage) return;
55
+
56
+ const hasUsageFields =
57
+ usage.prompt_tokens != null
58
+ || usage.completion_tokens != null
59
+ || usage.total_tokens != null
60
+ || usage.input_tokens != null
61
+ || usage.output_tokens != null;
62
+ if (!hasUsageFields) return;
63
+
64
+ const promptTokens = parseUsageNumber(usage.prompt_tokens ?? usage.input_tokens);
65
+ const completionTokens = parseUsageNumber(usage.completion_tokens ?? usage.output_tokens);
66
+ let totalTokens = parseUsageNumber(usage.total_tokens);
67
+ if (totalTokens === 0 && (promptTokens > 0 || completionTokens > 0)) {
68
+ totalTokens = promptTokens + completionTokens;
69
+ }
70
+
71
+ stats.apiUsageAvailable = true;
72
+ stats.apiPromptTokens += promptTokens;
73
+ stats.apiCompletionTokens += completionTokens;
74
+ stats.apiTotalTokens += totalTokens;
75
+ }
76
+
77
+ export function finalizeTokenUsage(stats: TokenUsageStats): TokenUsageStats {
78
+ const estimatedTotalTokens = stats.estimatedPromptTokens + stats.estimatedCompletionTokens;
79
+ let apiTotalTokens = stats.apiTotalTokens;
80
+ if (stats.apiUsageAvailable && apiTotalTokens === 0) {
81
+ apiTotalTokens = stats.apiPromptTokens + stats.apiCompletionTokens;
82
+ }
83
+
84
+ return {
85
+ ...stats,
86
+ apiTotalTokens,
87
+ estimatedTotalTokens,
88
+ };
89
+ }
@@ -879,7 +879,14 @@ export async function executeTool(name: string, argsJson: string): Promise<strin
879
879
  });
880
880
  const header = result.title ? `# ${result.title}\n\n` : '';
881
881
  const meta = `[${result.extractor}] ${result.finalUrl} (${result.status}, ${result.tookMs}ms)`;
882
- return `${meta}\n\n${header}${result.text}`;
882
+ const lines = [meta];
883
+ if (result.escalationHint) {
884
+ lines.push(`Escalation hint: ${result.escalationHint} (retry with browser_navigate for this URL).`);
885
+ }
886
+ if (result.warning) {
887
+ lines.push(`Warning: ${result.warning}`);
888
+ }
889
+ return `${lines.join('\n')}\n\n${header}${result.text}`;
883
890
  }
884
891
 
885
892
  case 'browser_navigate':
@@ -1207,7 +1214,7 @@ export const TOOL_DEFINITIONS: ToolDefinition[] = [
1207
1214
  function: {
1208
1215
  name: 'web_fetch',
1209
1216
  description:
1210
- 'Fetch a URL and extract its readable content as markdown or plain text. Works with HTML pages, JSON APIs, and markdown URLs. Use for reading web pages, documentation, API responses, etc.',
1217
+ 'Fetch a URL via plain HTTP GET and extract readable content (HTML to markdown/text). No JavaScript execution, no clicks, no form interaction. Use for static read-only retrieval: articles, docs, wikis, READMEs, API JSON/text endpoints, and direct files/PDFs. Avoid for SPAs (React/Vue/Angular/Next client routes), auth/login-gated pages, dashboards/web apps, bot/challenge flows, or content loaded after render via XHR/fetch. Cost: typically ~10-100x cheaper/faster than browser tools. Default to web_fetch for read-only retrieval, then escalate to browser_navigate when output is empty/boilerplate, JavaScript-required, SPA shell-only, or bot-blocked.',
1211
1218
  parameters: {
1212
1219
  type: 'object',
1213
1220
  properties: {
@@ -25,6 +25,13 @@ export interface ChatCompletionResponse {
25
25
  finish_reason: string;
26
26
  }>;
27
27
  model: string;
28
+ usage?: {
29
+ prompt_tokens?: number;
30
+ completion_tokens?: number;
31
+ total_tokens?: number;
32
+ input_tokens?: number;
33
+ output_tokens?: number;
34
+ };
28
35
  }
29
36
 
30
37
  export interface ToolDefinition {
@@ -76,6 +83,17 @@ export interface ToolExecution {
76
83
  blockedReason?: string;
77
84
  }
78
85
 
86
+ export interface TokenUsageStats {
87
+ modelCalls: number;
88
+ apiUsageAvailable: boolean;
89
+ apiPromptTokens: number;
90
+ apiCompletionTokens: number;
91
+ apiTotalTokens: number;
92
+ estimatedPromptTokens: number;
93
+ estimatedCompletionTokens: number;
94
+ estimatedTotalTokens: number;
95
+ }
96
+
79
97
  export interface ArtifactMetadata {
80
98
  path: string;
81
99
  filename: string;
@@ -88,6 +106,7 @@ export interface ContainerOutput {
88
106
  toolsUsed: string[];
89
107
  artifacts?: ArtifactMetadata[];
90
108
  toolExecutions?: ToolExecution[];
109
+ tokenUsage?: TokenUsageStats;
91
110
  error?: string;
92
111
  sideEffects?: {
93
112
  schedules?: ScheduleSideEffect[];
@@ -19,6 +19,25 @@ const TIMEOUT_MS = 30_000;
19
19
  const CACHE_TTL_MS = 15 * 60_000; // 15 min
20
20
  const CACHE_MAX_ENTRIES = 100;
21
21
  const READABILITY_MAX_HTML_CHARS = 1_000_000;
22
+ const ESCALATION_MIN_TEXT_CHARS = 200;
23
+ const ESCALATION_MIN_HTML_CHARS = 5_000;
24
+ const BOT_BLOCKED_PATTERNS = [
25
+ 'access denied',
26
+ 'bot detected',
27
+ 'captcha',
28
+ 'cf-chl-',
29
+ 'checking your browser',
30
+ 'cloudflare',
31
+ 'just a moment',
32
+ 'attention required',
33
+ 'verification required',
34
+ ];
35
+ const JAVASCRIPT_REQUIRED_PATTERNS = [
36
+ 'enable javascript',
37
+ 'javascript required',
38
+ 'requires javascript',
39
+ 'turn on javascript',
40
+ ];
22
41
  const USER_AGENT =
23
42
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36';
24
43
 
@@ -271,10 +290,71 @@ async function fetchWithRedirects(
271
290
  throw new Error(`Too many redirects (max ${maxRedirects})`);
272
291
  }
273
292
 
293
+ function normalizeForDetection(value: string): string {
294
+ return String(value || '').toLowerCase().replace(/\s+/g, ' ').trim();
295
+ }
296
+
297
+ function includesAny(haystack: string, needles: readonly string[]): boolean {
298
+ for (const needle of needles) {
299
+ if (haystack.includes(needle)) return true;
300
+ }
301
+ return false;
302
+ }
303
+
304
+ function detectEscalationHint(params: {
305
+ status: number;
306
+ contentType: string;
307
+ body: string;
308
+ extractedText: string;
309
+ }): WebFetchEscalationHint | undefined {
310
+ const normalizedBody = normalizeForDetection(params.body);
311
+ if (params.status === 403 || params.status === 429 || includesAny(normalizedBody, BOT_BLOCKED_PATTERNS)) {
312
+ return 'bot_blocked';
313
+ }
314
+
315
+ const isHtml = params.contentType.toLowerCase().includes('text/html');
316
+ if (!isHtml) return undefined;
317
+
318
+ if (
319
+ /<noscript[\s\S]{0,2000}javascript[\s\S]{0,2000}<\/noscript>/i.test(params.body) ||
320
+ includesAny(normalizedBody, JAVASCRIPT_REQUIRED_PATTERNS)
321
+ ) {
322
+ return 'javascript_required';
323
+ }
324
+
325
+ if (
326
+ /<div[^>]+id=["'](?:root|app|__next)["'][^>]*>\s*<\/div>/i.test(params.body) &&
327
+ normalizeForDetection(params.extractedText).length < ESCALATION_MIN_TEXT_CHARS
328
+ ) {
329
+ return 'spa_shell_only';
330
+ }
331
+
332
+ const normalizedExtracted = normalizeForDetection(params.extractedText);
333
+ if (normalizedExtracted.length === 0) {
334
+ return 'empty_extraction';
335
+ }
336
+
337
+ if (
338
+ normalizedExtracted.length < ESCALATION_MIN_TEXT_CHARS &&
339
+ params.body.length > ESCALATION_MIN_HTML_CHARS
340
+ ) {
341
+ return 'boilerplate_only';
342
+ }
343
+
344
+ return undefined;
345
+ }
346
+
274
347
  // ---------------------------------------------------------------------------
275
348
  // Public API
276
349
  // ---------------------------------------------------------------------------
277
350
 
351
+ export type WebFetchEscalationHint =
352
+ | 'javascript_required'
353
+ | 'empty_extraction'
354
+ | 'spa_shell_only'
355
+ | 'bot_blocked'
356
+ | 'boilerplate_only';
357
+
278
358
  export interface WebFetchResult {
279
359
  url: string;
280
360
  finalUrl: string;
@@ -290,6 +370,7 @@ export interface WebFetchResult {
290
370
  text: string;
291
371
  cached?: boolean;
292
372
  warning?: string;
373
+ escalationHint?: WebFetchEscalationHint;
293
374
  }
294
375
 
295
376
  export async function webFetch(params: {
@@ -327,10 +408,6 @@ export async function webFetch(params: {
327
408
  controller.signal,
328
409
  );
329
410
 
330
- if (!res.ok) {
331
- throw new Error(`Web fetch failed (${res.status}): ${res.statusText}`);
332
- }
333
-
334
411
  const contentType = res.headers.get('content-type') ?? 'application/octet-stream';
335
412
  const normalizedContentType = contentType.split(';')[0]?.trim() || 'application/octet-stream';
336
413
  const bodyResult = await readResponseText(res, MAX_RESPONSE_BYTES);
@@ -357,13 +434,26 @@ export async function webFetch(params: {
357
434
  }
358
435
  }
359
436
 
437
+ const extractedText = extractMode === 'text' ? text : markdownToText(text);
438
+ const escalationHint = detectEscalationHint({
439
+ status: res.status,
440
+ contentType: normalizedContentType,
441
+ body,
442
+ extractedText,
443
+ });
444
+
445
+ if (!res.ok && !escalationHint) {
446
+ throw new Error(`Web fetch failed (${res.status}): ${res.statusText}`);
447
+ }
448
+
360
449
  // Truncate
361
450
  const truncated = text.length > maxChars;
362
451
  if (truncated) text = text.slice(0, maxChars);
363
452
 
364
- const warning = bodyResult.truncated
365
- ? `Response body truncated after ${MAX_RESPONSE_BYTES} bytes.`
366
- : undefined;
453
+ const warnings: string[] = [];
454
+ if (!res.ok) warnings.push(`HTTP ${res.status} ${res.statusText}.`);
455
+ if (bodyResult.truncated) warnings.push(`Response body truncated after ${MAX_RESPONSE_BYTES} bytes.`);
456
+ const warning = warnings.length > 0 ? warnings.join(' ') : undefined;
367
457
 
368
458
  const result: WebFetchResult = {
369
459
  url: params.url,
@@ -379,6 +469,7 @@ export async function webFetch(params: {
379
469
  tookMs: Date.now() - start,
380
470
  text,
381
471
  warning,
472
+ escalationHint,
382
473
  };
383
474
 
384
475
  writeCache(cacheKey, result);
package/dist/agent.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import type { ChatMessage, ContainerOutput, ScheduledTask, ToolProgressEvent } from './types.js';
2
- export declare function runAgent(sessionId: string, messages: ChatMessage[], chatbotId: string, enableRag: boolean, model: string, agentId: string, channelId: string, scheduledTasks?: ScheduledTask[], allowedTools?: string[], onToolProgress?: (event: ToolProgressEvent) => void, abortSignal?: AbortSignal): Promise<ContainerOutput>;
2
+ export declare function runAgent(sessionId: string, messages: ChatMessage[], chatbotId: string, enableRag: boolean, model: string, agentId: string, channelId: string, scheduledTasks?: ScheduledTask[], allowedTools?: string[], onTextDelta?: (delta: string) => void, onToolProgress?: (event: ToolProgressEvent) => void, abortSignal?: AbortSignal): Promise<ContainerOutput>;
3
3
  //# sourceMappingURL=agent.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAWjG,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,aAAa,EAAE,EAChC,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,EACnD,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,eAAe,CAAC,CAG1B"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAWjG,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,cAAc,CAAC,EAAE,aAAa,EAAE,EAChC,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACrC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,EACnD,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,eAAe,CAAC,CAgB1B"}
package/dist/agent.js CHANGED
@@ -14,8 +14,8 @@ function dumpPrompt(sessionId, messages, model, chatbotId) {
14
14
  }
15
15
  catch { /* best-effort */ }
16
16
  }
17
- export async function runAgent(sessionId, messages, chatbotId, enableRag, model, agentId, channelId, scheduledTasks, allowedTools, onToolProgress, abortSignal) {
17
+ export async function runAgent(sessionId, messages, chatbotId, enableRag, model, agentId, channelId, scheduledTasks, allowedTools, onTextDelta, onToolProgress, abortSignal) {
18
18
  dumpPrompt(sessionId, messages, model, chatbotId);
19
- return runContainer(sessionId, messages, chatbotId, enableRag, model, agentId, channelId, scheduledTasks, allowedTools, onToolProgress, abortSignal);
19
+ return runContainer(sessionId, messages, chatbotId, enableRag, model, agentId, channelId, scheduledTasks, allowedTools, onTextDelta, onToolProgress, abortSignal);
20
20
  }
21
21
  //# sourceMappingURL=agent.js.map
package/dist/agent.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD,yFAAyF;AACzF,SAAS,UAAU,CAAC,SAAiB,EAAE,QAAuB,EAAE,KAAa,EAAE,SAAiB;IAC9F,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;QACtF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAiB,EACjB,QAAuB,EACvB,SAAiB,EACjB,SAAkB,EAClB,KAAa,EACb,OAAe,EACf,SAAiB,EACjB,cAAgC,EAChC,YAAuB,EACvB,cAAmD,EACnD,WAAyB;IAEzB,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,YAAY,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,WAAW,CAAC,CAAC;AACvJ,CAAC"}
1
+ {"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAGrD,yFAAyF;AACzF,SAAS,UAAU,CAAC,SAAiB,EAAE,QAAuB,EAAE,KAAa,EAAE,SAAiB;IAC9F,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,EAAE,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;QACtF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,mBAAmB,CAAC,CAAC;QAC1D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,SAAiB,EACjB,QAAuB,EACvB,SAAiB,EACjB,SAAkB,EAClB,KAAa,EACb,OAAe,EACf,SAAiB,EACjB,cAAgC,EAChC,YAAuB,EACvB,WAAqC,EACrC,cAAmD,EACnD,WAAyB;IAEzB,UAAU,CAAC,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAClD,OAAO,YAAY,CACjB,SAAS,EACT,QAAQ,EACR,SAAS,EACT,SAAS,EACT,KAAK,EACL,OAAO,EACP,SAAS,EACT,cAAc,EACd,YAAY,EACZ,WAAW,EACX,cAAc,EACd,WAAW,CACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface ChunkMessageOptions {
2
+ maxChars?: number;
3
+ maxLines?: number;
4
+ }
5
+ export declare function chunkMessage(text: string, opts?: ChunkMessageOptions): string[];
6
+ //# sourceMappingURL=chunk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.d.ts","sourceRoot":"","sources":["../src/chunk.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAiFD,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,MAAM,EAAE,CAoE/E"}
package/dist/chunk.js ADDED
@@ -0,0 +1,129 @@
1
+ const DEFAULT_MAX_CHARS = 1_900;
2
+ const DEFAULT_MAX_LINES = 20;
3
+ function isFenceLine(line) {
4
+ return line.trim().startsWith('```');
5
+ }
6
+ function parseFenceLanguage(line) {
7
+ const trimmed = line.trim();
8
+ if (!trimmed.startsWith('```'))
9
+ return '';
10
+ return trimmed.slice(3).trim();
11
+ }
12
+ function findSentenceBoundary(input) {
13
+ let best = -1;
14
+ const re = /[.!?]\s+/g;
15
+ for (let match = re.exec(input); match; match = re.exec(input)) {
16
+ best = match.index + match[0].length;
17
+ }
18
+ return best;
19
+ }
20
+ function findPreferredSplit(input, hardLimit) {
21
+ const limit = Math.max(1, Math.min(hardLimit, input.length));
22
+ const window = input.slice(0, limit);
23
+ const paragraph = window.lastIndexOf('\n\n');
24
+ if (paragraph >= Math.floor(limit * 0.45)) {
25
+ return paragraph + 2;
26
+ }
27
+ const line = window.lastIndexOf('\n');
28
+ if (line >= Math.floor(limit * 0.45)) {
29
+ return line + 1;
30
+ }
31
+ const sentence = findSentenceBoundary(window);
32
+ if (sentence >= Math.floor(limit * 0.45)) {
33
+ return sentence;
34
+ }
35
+ const word = window.lastIndexOf(' ');
36
+ if (word >= Math.floor(limit * 0.35)) {
37
+ return word + 1;
38
+ }
39
+ return limit;
40
+ }
41
+ function splitLongLine(line, maxChars) {
42
+ if (line.length <= maxChars)
43
+ return [line];
44
+ const pieces = [];
45
+ let remaining = line;
46
+ while (remaining.length > maxChars) {
47
+ let splitAt = findPreferredSplit(remaining, maxChars);
48
+ if (splitAt <= 0 || splitAt > remaining.length) {
49
+ splitAt = Math.min(maxChars, remaining.length);
50
+ }
51
+ const head = remaining.slice(0, splitAt).trimEnd();
52
+ if (!head) {
53
+ const fallback = remaining.slice(0, maxChars);
54
+ pieces.push(fallback);
55
+ remaining = remaining.slice(maxChars);
56
+ continue;
57
+ }
58
+ pieces.push(head);
59
+ remaining = remaining.slice(splitAt).trimStart();
60
+ }
61
+ if (remaining.length > 0) {
62
+ pieces.push(remaining);
63
+ }
64
+ return pieces;
65
+ }
66
+ export function chunkMessage(text, opts) {
67
+ const maxChars = Math.max(200, opts?.maxChars ?? DEFAULT_MAX_CHARS);
68
+ const maxLines = Math.max(4, opts?.maxLines ?? DEFAULT_MAX_LINES);
69
+ const normalized = (text || '').replace(/\r\n?/g, '\n');
70
+ if (!normalized.trim())
71
+ return [];
72
+ const inputLines = normalized.split('\n');
73
+ const chunks = [];
74
+ let currentLines = [];
75
+ let currentChars = 0;
76
+ let openFence = false;
77
+ let fenceLanguage = '';
78
+ const flush = (isFinal) => {
79
+ if (currentLines.length === 0)
80
+ return;
81
+ let chunk = currentLines.join('\n');
82
+ if (openFence) {
83
+ chunk += '\n```';
84
+ }
85
+ chunks.push(chunk);
86
+ if (!isFinal && openFence) {
87
+ const reopenedFence = fenceLanguage ? `\`\`\`${fenceLanguage}` : '```';
88
+ currentLines = [reopenedFence];
89
+ currentChars = reopenedFence.length;
90
+ }
91
+ else {
92
+ currentLines = [];
93
+ currentChars = 0;
94
+ if (isFinal && openFence) {
95
+ openFence = false;
96
+ fenceLanguage = '';
97
+ }
98
+ }
99
+ };
100
+ const appendLine = (line) => {
101
+ const addedChars = currentLines.length === 0 ? line.length : line.length + 1;
102
+ const nextChars = currentChars + addedChars;
103
+ const nextLines = currentLines.length + 1;
104
+ if (currentLines.length > 0 && (nextChars > maxChars || nextLines > maxLines)) {
105
+ flush(false);
106
+ }
107
+ currentLines.push(line);
108
+ currentChars = currentLines.length === 1 ? line.length : currentChars + line.length + 1;
109
+ if (isFenceLine(line)) {
110
+ if (!openFence) {
111
+ openFence = true;
112
+ fenceLanguage = parseFenceLanguage(line);
113
+ }
114
+ else {
115
+ openFence = false;
116
+ fenceLanguage = '';
117
+ }
118
+ }
119
+ };
120
+ for (const rawLine of inputLines) {
121
+ const splitLines = splitLongLine(rawLine, maxChars);
122
+ for (const part of splitLines) {
123
+ appendLine(part);
124
+ }
125
+ }
126
+ flush(true);
127
+ return chunks;
128
+ }
129
+ //# sourceMappingURL=chunk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk.js","sourceRoot":"","sources":["../src/chunk.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ export declare function getActiveContainerCount(): number;
3
3
  /**
4
4
  * Send a request to a persistent container and wait for the response.
5
5
  */
6
- export declare function runContainer(sessionId: string, messages: ChatMessage[], chatbotId: string, enableRag: boolean, model?: string, agentId?: string, channelId?: string, scheduledTasks?: ScheduledTask[], allowedTools?: string[], onToolProgress?: (event: ToolProgressEvent) => void, abortSignal?: AbortSignal): Promise<ContainerOutput>;
6
+ export declare function runContainer(sessionId: string, messages: ChatMessage[], chatbotId: string, enableRag: boolean, model?: string, agentId?: string, channelId?: string, scheduledTasks?: ScheduledTask[], allowedTools?: string[], onTextDelta?: (delta: string) => void, onToolProgress?: (event: ToolProgressEvent) => void, abortSignal?: AbortSignal): Promise<ContainerOutput>;
7
7
  /**
8
8
  * Stop all containers (for graceful shutdown).
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"container-runner.d.ts","sourceRoot":"","sources":["../src/container-runner.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAqC,WAAW,EAAkB,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAqDpJ,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAqKD;;GAEG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,KAAK,GAAE,MAAuB,EAC9B,OAAO,GAAE,MAAkB,EAC3B,SAAS,GAAE,MAAW,EACtB,cAAc,CAAC,EAAE,aAAa,EAAE,EAChC,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,EACnD,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,eAAe,CAAC,CA4F1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}
1
+ {"version":3,"file":"container-runner.d.ts","sourceRoot":"","sources":["../src/container-runner.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAqC,WAAW,EAAkB,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAsEpJ,wBAAgB,uBAAuB,IAAI,MAAM,CAEhD;AAuKD;;GAEG;AACH,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,WAAW,EAAE,EACvB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,OAAO,EAClB,KAAK,GAAE,MAAuB,EAC9B,OAAO,GAAE,MAAkB,EAC3B,SAAS,GAAE,MAAW,EACtB,cAAc,CAAC,EAAE,aAAa,EAAE,EAChC,YAAY,CAAC,EAAE,MAAM,EAAE,EACvB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACrC,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,EACnD,WAAW,CAAC,EAAE,WAAW,GACxB,OAAO,CAAC,eAAe,CAAC,CAgG1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC"}