@jsonstudio/llms 0.6.141 → 0.6.147

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.
@@ -358,6 +358,18 @@ export function buildAnthropicResponseFromChat(chatResponse, options) {
358
358
  default: return 'end_turn';
359
359
  }
360
360
  })();
361
+ const promptTokens = usage && typeof usage === 'object'
362
+ ? Number(usage.prompt_tokens ?? usage.input_tokens ?? 0)
363
+ : 0;
364
+ const completionTokens = usage && typeof usage === 'object'
365
+ ? Number(usage.completion_tokens ?? usage.output_tokens ?? 0)
366
+ : 0;
367
+ const usagePayload = (promptTokens || completionTokens)
368
+ ? {
369
+ input_tokens: promptTokens,
370
+ output_tokens: completionTokens
371
+ }
372
+ : undefined;
361
373
  const raw = {
362
374
  id: typeof chatResponse.id === 'string' ? chatResponse.id : `resp_${Date.now()}`,
363
375
  type: 'message',
@@ -366,10 +378,7 @@ export function buildAnthropicResponseFromChat(chatResponse, options) {
366
378
  model: typeof chatResponse.model === 'string' ? chatResponse.model : 'unknown',
367
379
  stop_reason: stopReasonMapped,
368
380
  usage: usage && typeof usage === 'object'
369
- ? {
370
- input_tokens: usage.prompt_tokens ?? 0,
371
- output_tokens: usage.completion_tokens ?? 0
372
- }
381
+ ? usagePayload
373
382
  : undefined
374
383
  };
375
384
  const sanitized = sanitizeAnthropicMessage(raw);
@@ -540,7 +540,7 @@ const captureToolResultsAction = (ctx) => {
540
540
  name: typeof entry?.name === 'string' ? entry.name : undefined
541
541
  }));
542
542
  }
543
- if (ctx.stage === 'request_outbound' &&
543
+ if ((ctx.stage === 'request_outbound' || ctx.stage === 'response_outbound') &&
544
544
  Array.isArray(ctx.state.capturedToolResults) &&
545
545
  ctx.state.capturedToolResults.length) {
546
546
  const metadata = ensureMetadataRecord(ctx.state);
@@ -232,6 +232,7 @@ const ANTHROPIC_POLICY = {
232
232
  ],
233
233
  outbound: [
234
234
  reasoningAction('anthropic_reasoning'),
235
+ { name: 'tools.capture-results' },
235
236
  toolCallNormalizationAction('anthropic_tool_call'),
236
237
  { name: 'metadata.extra-fields', options: { allowedKeys: ANTHROPIC_ALLOWED_FIELDS } }
237
238
  ]
@@ -36,22 +36,21 @@ export class RoutingClassifier {
36
36
  : undefined;
37
37
  const websearchKeywordHit = containsKeywords(features.userTextSample, WEBSEARCH_HINT_KEYWORDS);
38
38
  const codingContinuation = hasWriteToolCall || lastToolCategory === 'write';
39
- const thinkingContinuation = hasReadToolCall || lastToolCategory === 'read';
39
+ const readingContinuation = hasReadToolCall || lastToolCategory === 'read';
40
40
  const userInputDetected = typeof features.userTextSample === 'string'
41
41
  ? features.userTextSample.trim().length > 0
42
42
  : false;
43
- const searchContinuation = features.assistantCalledWebSearchTool === true;
43
+ const searchContinuation = features.assistantCalledWebSearchTool === true || hasSearchToolCall;
44
44
  const toolsContinuation = hasOtherToolCall ||
45
45
  searchContinuation ||
46
- (hasToolCall && !hasSearchToolCall && !hasWriteToolCall && !hasReadToolCall);
46
+ (hasToolCall && !hasWriteToolCall && !readingContinuation);
47
47
  const toolContinuationReason = hasOtherToolCall
48
48
  ? formatToolContinuationReason(features.lastAssistantToolName, features.lastAssistantToolDetail)
49
- : 'tools:tool-call-detected';
50
- const thinkingReason = thinkingContinuation
51
- ? 'thinking:last-tool-read'
52
- : userInputDetected
53
- ? 'thinking:user-input'
54
- : 'thinking';
49
+ : searchContinuation
50
+ ? 'tools:last-tool-search'
51
+ : 'tools:tool-call-detected';
52
+ const thinkingContinuation = readingContinuation || (Boolean(features.hasThinkingKeyword) && !toolsContinuation && !codingContinuation);
53
+ const thinkingReason = readingContinuation ? 'thinking:last-tool-read' : 'thinking:keywords';
55
54
  const evaluationMap = {
56
55
  vision: {
57
56
  triggered: features.hasVisionTool && features.hasImageAttachment,
@@ -70,7 +69,7 @@ export class RoutingClassifier {
70
69
  reason: 'coding:last-tool-write'
71
70
  },
72
71
  thinking: {
73
- triggered: thinkingContinuation || userInputDetected,
72
+ triggered: thinkingContinuation,
74
73
  reason: thinkingReason
75
74
  },
76
75
  tools: {
@@ -34,6 +34,7 @@ export declare class VirtualRouterEngine {
34
34
  private recordSelectionSnapshot;
35
35
  private buildStickyPlan;
36
36
  private storeStickyPlan;
37
+ private dropStickyForRequest;
37
38
  private resolveStickyDescriptor;
38
39
  private maybeForceStickyFromHistory;
39
40
  private shouldForceApplyPatchSticky;
@@ -108,6 +108,9 @@ export class VirtualRouterEngine {
108
108
  }
109
109
  }
110
110
  handleProviderError(event) {
111
+ if (event?.runtime?.requestId) {
112
+ this.dropStickyForRequest(event.runtime.requestId);
113
+ }
111
114
  const derived = this.mapProviderError(event);
112
115
  if (!derived) {
113
116
  return;
@@ -225,6 +228,13 @@ export class VirtualRouterEngine {
225
228
  this.stickyPlans.delete(requestId);
226
229
  }
227
230
  }
231
+ dropStickyForRequest(requestId) {
232
+ if (!requestId) {
233
+ return;
234
+ }
235
+ this.stickyPlans.delete(requestId);
236
+ this.selectionHistory.delete(requestId);
237
+ }
228
238
  resolveStickyDescriptor(routeName, features) {
229
239
  if (this.shouldForceApplyPatchSticky(features)) {
230
240
  return { strategy: 'target', rounds: 1, reason: 'apply_patch' };
@@ -68,25 +68,11 @@ const SHELL_WRITE_PATTERNS = [
68
68
  'chown',
69
69
  'chgrp',
70
70
  'tar ',
71
- 'git add',
72
- 'git commit',
73
71
  'git apply',
74
72
  'git am',
75
- 'git rebase',
76
73
  'git checkout',
77
74
  'git merge',
78
- 'patch <<',
79
- 'npm install',
80
- 'pnpm install',
81
- 'yarn add',
82
- 'yarn install',
83
- 'pip install',
84
- 'pip3 install',
85
- 'brew install',
86
- 'cargo add',
87
- 'cargo install',
88
- 'go install',
89
- 'make install'
75
+ 'patch <<'
90
76
  ];
91
77
  const SHELL_SEARCH_PATTERNS = [
92
78
  'rg ',
@@ -105,18 +91,13 @@ const SHELL_SEARCH_PATTERNS = [
105
91
  'codesearch'
106
92
  ];
107
93
  const SHELL_READ_PATTERNS = [
108
- 'ls',
109
- 'dir ',
110
- 'pwd',
111
94
  'cat ',
112
95
  'type ',
113
96
  'head ',
114
97
  'tail ',
98
+ 'stat ',
115
99
  'stat',
116
- 'tree',
117
100
  'wc ',
118
- 'du ',
119
- 'printf "',
120
101
  'python - <<',
121
102
  'python -c',
122
103
  'node - <<',
@@ -522,26 +503,31 @@ function classifyShellCommand(command) {
522
503
  if (SHELL_HEREDOC_PATTERN.test(command)) {
523
504
  return 'write';
524
505
  }
525
- const segments = splitCommandSegments(command).map(stripShellWrapper);
526
- if (segments.some((segment) => matchesAnyPattern(segment, SHELL_WRITE_PATTERNS))) {
506
+ const segments = splitCommandSegments(command)
507
+ .map(stripShellWrapper)
508
+ .filter(Boolean);
509
+ const hasWrite = segments.some((segment) => matchesAnyPattern(segment, SHELL_WRITE_PATTERNS));
510
+ const hasRead = segments.some((segment) => matchesAnyPattern(segment, SHELL_READ_PATTERNS));
511
+ if (hasWrite) {
527
512
  return 'write';
528
513
  }
529
- if (segments.some((segment) => matchesAnyPattern(segment, SHELL_SEARCH_PATTERNS))) {
530
- return 'search';
531
- }
532
- if (segments.some((segment) => matchesAnyPattern(segment, SHELL_READ_PATTERNS))) {
514
+ if (hasRead) {
533
515
  return 'read';
534
516
  }
517
+ const hasSearch = segments.some((segment) => matchesAnyPattern(segment, SHELL_SEARCH_PATTERNS));
518
+ if (hasSearch) {
519
+ return 'search';
520
+ }
535
521
  const stripped = stripShellWrapper(command);
536
522
  if (matchesAnyPattern(stripped, SHELL_WRITE_PATTERNS)) {
537
523
  return 'write';
538
524
  }
539
- if (matchesAnyPattern(stripped, SHELL_SEARCH_PATTERNS)) {
540
- return 'search';
541
- }
542
525
  if (matchesAnyPattern(stripped, SHELL_READ_PATTERNS)) {
543
526
  return 'read';
544
527
  }
528
+ if (matchesAnyPattern(stripped, SHELL_SEARCH_PATTERNS)) {
529
+ return 'search';
530
+ }
545
531
  return 'other';
546
532
  }
547
533
  function splitCommandSegments(command) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jsonstudio/llms",
3
- "version": "0.6.141",
3
+ "version": "0.6.147",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",