@jaypie/mcp 0.8.178 → 0.8.180

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.js CHANGED
@@ -55,11 +55,11 @@ function getMcpAssetPaths() {
55
55
  */
56
56
  function getMcpBuildInfo() {
57
57
  return {
58
- commit: "77e42bd7"
58
+ commit: "bd1e9e07"
59
59
  ,
60
- version: "0.8.178"
60
+ version: "0.8.180"
61
61
  ,
62
- versionString: "@jaypie/mcp@0.8.178#77e42bd7"
62
+ versionString: "@jaypie/mcp@0.8.180#bd1e9e07"
63
63
  ,
64
64
  };
65
65
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jaypie/mcp",
3
- "version": "0.8.178",
3
+ "version": "0.8.180",
4
4
  "description": "Jaypie MCP",
5
5
  "repository": {
6
6
  "type": "git",
@@ -0,0 +1,9 @@
1
+ ---
2
+ version: 1.2.115
3
+ date: 2026-09-28
4
+ summary: Pulls @jaypie/llm 1.3.56
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`@jaypie/llm` 1.3.56**: failing over down a fallback chain logs at `debug` instead of `error` and `warn`; the report tally counts `failures` and `fallbacks`.
@@ -0,0 +1,9 @@
1
+ ---
2
+ version: 1.2.116
3
+ date: 2026-09-29
4
+ summary: Pulls @jaypie/llm 1.3.57
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **`@jaypie/llm` 1.3.57**: a fallback chain advances past an incomplete (truncated) response; new `incomplete: "return" | "throw"` option with `LlmIncompleteError`; `response.stopReason` normalized as `LlmStopReason`; Anthropic spending-cap and bundled 4xx errors no longer classify as retryable.
@@ -0,0 +1,26 @@
1
+ ---
2
+ version: 1.3.56
3
+ date: 2026-09-28
4
+ summary: Failing over down a fallback chain logs at debug instead of error and warn, and the report tally counts failures and fallbacks
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ ### Failover logging
10
+
11
+ A timeout (or any failure) on a fail-fast attempt down a fallback chain logged `API call failed after 0 retries` at `error` and `Provider X failed` at `warn`, although the next model took over as designed.
12
+
13
+ - `RetryExecutor` logs a zero-budget failure at `debug`: `API call timed out after 60000ms; not retrying` for a timeout, `API call failed; not retrying` or `API call rate limited; not retrying` otherwise. Exhausting a nonzero budget still logs `error`.
14
+ - `runWithFallback` logs `Provider X failed; failing over` at `debug` when another model remains. Only the failure that reaches the caller (a lone model, or the linger pass) logs at `warn`. Both carry `kind` (`timeout` or the `LlmError` category).
15
+ - A timeout that retries on the transient budget names its deadline in the retry `debug` line.
16
+
17
+ ### Report tally
18
+
19
+ Inside a Jaypie handler, `operate()`, `ocr()`, and `question()` tally each failed provider attempt onto the `llm` report key:
20
+
21
+ - `failures`: counts by `provider:model` and kind, e.g. `{ "openai:gpt-5.5": { "timeout": 1 } }`
22
+ - `fallbacks`: counts each hand-off to another model, including the linger pass back to the primary
23
+
24
+ ## Testing
25
+
26
+ - `npm run test -w packages/llm`: new `tallyFailure` and `runWithFallback` specs cover failure kinds, key resolution, the fallback count, and the debug/warn split; `RetryExecutor` specs cover the zero-budget debug lines for transient, rate-limit, and timeout failures
@@ -0,0 +1,47 @@
1
+ ---
2
+ version: 1.3.57
3
+ date: 2026-09-29
4
+ summary: A fallback chain advances past an incomplete (truncated) response; new incomplete option, LlmIncompleteError, and a standard stopReason; Anthropic spending-cap and bundled 4xx errors classify correctly
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ ### Fallback on incomplete (issue #602)
10
+
11
+ A response the provider cut short (`status: "incomplete"`, `error.reason: "incomplete"`: an output token ceiling or a content filter) was returned as success, so a configured chain never advanced.
12
+
13
+ - `operate` now moves to the next chain entry on an incomplete response, as it does on a thrown error.
14
+ - When no entry finishes, the latest incomplete response is returned rather than an error. `fallbackAttempts` counts every attempt made.
15
+ - A primary that cut off skips the linger pass: the same model would cut off the same way.
16
+ - Loop stops (`max_turns`, `tool_errors`) never fall over.
17
+ - The hand-off logs `Provider X returned incomplete; failing over` at `debug` and tallies under the `incomplete` kind.
18
+
19
+ ### `incomplete` option
20
+
21
+ `incomplete: "return" | "throw"` decides what reaches the caller once the chain is spent. `"return"` (default) keeps the partial with `status: "incomplete"`; `"throw"` raises `LlmIncompleteError` (status 502, category `incomplete`) carrying the settled response on `.response`. Jaypie 2 will default to `"throw"`.
22
+
23
+ ### Standard stop reason
24
+
25
+ `LlmOperateResponse.stopReason` (`LlmStopReason`) normalizes the final model call's stop across providers: `end_turn`, `max_tokens`, `content_filter`, `refusal`, `tool_use`, or `other`. Gemini `MAX_TOKENS`, Chat Completions `length`, Anthropic `max_tokens`, and OpenAI `max_output_tokens` all read `max_tokens`.
26
+
27
+ ### Error classification under bundling (issue #604)
28
+
29
+ An Anthropic spending-cap 400 surfaced as a retryable `LlmTransientError` (504) and tallied under `retryable`.
30
+
31
+ - `You have reached your specified API usage limits` classifies as `Quota` with `reason: "billing"`: no retries, immediate failover, tallied under `quota`.
32
+ - The Anthropic adapter classifies by HTTP `status` first: 429 rate limit, 408 and 5xx retryable, every other 4xx unrecoverable. An esbuild-renamed class (`BadRequestError3`) previously fell through to `Unknown`, so every bundled Anthropic 4xx was retried.
33
+ - A class-name fallback strips a bundler's numeric suffix.
34
+ - Bedrock matches on the AWS SDK's literal `error.name` instead of the class name.
35
+ - Anthropic and OpenAI client error classes carry a literal `name`.
36
+
37
+ ### Documentation
38
+
39
+ - An incomplete `content` is the raw partial string even when `format` was requested.
40
+ - Mixed chains mix output budgets: Anthropic, Google, and Mistral cap non-streaming at 16,384 while OpenAI, xAI, and Meta send no ceiling.
41
+
42
+ ## Testing
43
+
44
+ - `runWithFallback` specs cover soft failover, lone-model return, linger skip, partial over a later error, `settle`, and a `settle` throw that does not fail over.
45
+ - `Llm` class specs cover failover on incomplete, whole-chain cut-off, fallback error after a primary cut-off, loop stops not failing over, and `incomplete: "throw"`.
46
+ - Adapter specs cover the spending-cap message, renamed Anthropic classes by status (400, 401, 403, 404, 429, 500, 529) and without one, and renamed Bedrock exceptions.
47
+ - `standardStopReason` and `OperateLoop` specs cover the normalized stop reason, including a `structured_output` tool answer as `end_turn`.
@@ -0,0 +1,10 @@
1
+ ---
2
+ version: 0.8.179
3
+ date: 2026-09-28
4
+ summary: Release notes for @jaypie/llm 1.3.56 and jaypie 1.2.115; llm skill documents failover logging and the failures/fallbacks tally
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **Release notes**: `@jaypie/llm` 1.3.56 and `jaypie` 1.2.115
10
+ - **`llm` skill**: documents the `failures` and `fallbacks` report totals and the debug-level failover logging under Attempt Timeout
@@ -0,0 +1,10 @@
1
+ ---
2
+ version: 0.8.180
3
+ date: 2026-09-29
4
+ summary: Release notes for @jaypie/llm 1.3.57, @jaypie/testkit 1.2.80, and jaypie 1.2.116; llm skill documents incomplete failover and stopReason
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - **Release notes**: `@jaypie/llm` 1.3.57, `@jaypie/testkit` 1.2.80, and `jaypie` 1.2.116
10
+ - **`llm` skill**: documents failover on incomplete responses, the `incomplete` option and `LlmIncompleteError`, `response.stopReason`, raw partial `content` under `format`, and the output budget asymmetry across providers
@@ -0,0 +1,9 @@
1
+ ---
2
+ version: 1.2.80
3
+ date: 2026-09-29
4
+ summary: Re-exports LlmIncompleteError and LlmStopReason from the llm mock
5
+ ---
6
+
7
+ ## Changes
8
+
9
+ - `LlmIncompleteError` and `LlmStopReason` re-exported from the `@jaypie/llm` mock alongside the other errors and enums.
package/skills/llm.md CHANGED
@@ -130,8 +130,14 @@ response.history; // LlmHistory - for follow-up calls
130
130
  response.usage; // Token usage per turn: { input, output, reasoning, total, cacheRead?, cacheWrite?, cacheWriteTtl? }; reasoning ⊂ output, cache ⊂ input; price with tokenCost()
131
131
  response.reasoning; // Extended thinking (if available)
132
132
  response.status; // "completed" | "incomplete" | "in_progress"
133
+ response.stopReason; // LlmStopReason: "end_turn" | "max_tokens" | "content_filter" | "refusal" | "tool_use" | "other"
133
134
  ```
134
135
 
136
+ `stopReason` is normalized across providers (Gemini `MAX_TOKENS`, Chat
137
+ Completions `length`, Anthropic `max_tokens`, and OpenAI `max_output_tokens`
138
+ all read `max_tokens`). The provider's own value rides
139
+ `exchange.response.stopReason`.
140
+
135
141
  ### send() - Simple Completions
136
142
 
137
143
  For single-shot text completions:
@@ -800,7 +806,9 @@ for await (const chunk of Llm.stream(input, {
800
806
 
801
807
  ## Fallback Providers
802
808
 
803
- Configure a chain of fallback providers. Any error (rate limit, 5xx, network flake, bad request) moves to the next entry at once, with no retry or wait. When every entry has failed, the primary runs once more with its full retry policy; if that fails, the call throws. A caller abort (`LlmAbortError`) never falls over:
809
+ Configure a chain of fallback providers. Any error (rate limit, 5xx, network flake, bad request) moves to the next entry at once, with no retry or wait. When every entry has failed, the primary runs once more with its full retry policy; if that fails, the call throws. A caller abort (`LlmAbortError`) never falls over.
810
+
811
+ A response the provider cut short (`status: "incomplete"` with `error.reason: "incomplete"`: an output token ceiling or a content filter) also moves to the next entry. When no entry finishes, the latest incomplete response is returned rather than an error, and a primary that cut off does not linger (the same model would cut off the same way). Loop stops (`max_turns`, `tool_errors`) never fall over:
804
812
 
805
813
  ```typescript
806
814
  // Instance-level configuration
@@ -827,6 +835,28 @@ const response = await Llm.operate(input, {
827
835
  });
828
836
  ```
829
837
 
838
+ ### Incomplete Responses
839
+
840
+ `incomplete` decides what reaches the caller once the chain is spent. `"return"` (the default) returns the partial with `status: "incomplete"`; `"throw"` raises `LlmIncompleteError` (status 502) carrying the settled response on `.response`. Either way a chain moves past an incomplete first. Jaypie 2 will default to `"throw"`.
841
+
842
+ ```typescript
843
+ import { LlmIncompleteError, LlmStopReason } from "@jaypie/llm";
844
+
845
+ try {
846
+ const response = await Llm.operate(input, {
847
+ format: Extraction,
848
+ incomplete: "throw",
849
+ model: [LLM.MODEL.GEMINI_FLASH, LLM.MODEL.SOL],
850
+ });
851
+ } catch (error) {
852
+ if (error instanceof LlmIncompleteError) {
853
+ error.response.stopReason; // LlmStopReason.MaxTokens
854
+ }
855
+ }
856
+ ```
857
+
858
+ An incomplete `content` is the raw partial text, a string even when `format` was requested: a truncated answer is not the object the format promises. Check `status` (or use `incomplete: "throw"`) before reading `content` as the formatted object.
859
+
830
860
  ### Provider Options in a Chain
831
861
 
832
862
  `providerOptions` belongs to the model it was written for. Per-call `providerOptions` reach the primary only (including its linger pass); a fallback receives only the `providerOptions` on its own entry, or none. Options shaped for one provider (OpenAI `reasoning`, Gemini `thinkingConfig`) would otherwise break every other provider in the chain. Prefer first-class `effort` and `temperature`, which translate per provider.
@@ -864,6 +894,7 @@ const response = await llm.operate(input, {
864
894
  - `stream()` treats `timeout` as an idle timeout: it restarts with every chunk and pauses while the consumer holds one. A stall after partial output yields an error chunk.
865
895
  - No default applies; unset means no deadline. Size it above the slowest legitimate non-streaming response (long reasoning runs take minutes).
866
896
  - `Llm.ocr` keeps `timeout` as the LlamaParse job deadline.
897
+ - A timeout that hands off to the next model logs at `debug` (`API call timed out after 60000ms; not retrying`, then `Provider openai failed; failing over`). Only the failure that reaches the caller logs at `warn`. The report tally counts it under `failures` and `fallbacks`.
867
898
 
868
899
  ### Fallback Response Metadata
869
900
 
@@ -922,6 +953,8 @@ Inside a Jaypie handler (`expressHandler`, `lambdaHandler`, and stream variants)
922
953
  ```json
923
954
  {
924
955
  "llm": {
956
+ "failures": { "openai:gpt-5.5": { "timeout": 1 } },
957
+ "fallbacks": 1,
925
958
  "operates": 2,
926
959
  "toolCalls": 3,
927
960
  "tools": { "get_weather": 2, "roll": 1 },
@@ -941,6 +974,8 @@ Inside a Jaypie handler (`expressHandler`, `lambdaHandler`, and stream variants)
941
974
  - `operates` counts loop executions; repeated calls sum every number
942
975
  - `usage` is keyed `provider:model` — fallback providers appear as separate keys
943
976
  - `tools` appears only when tools were called
977
+ - `failures` counts failed provider attempts by `provider:model` and kind (`timeout`, `rate_limit`, `retryable`, `quota`, `unrecoverable`, `unknown`), from `operate()`, `ocr()`, and `question()`
978
+ - `fallbacks` counts hand-offs to another model, including the final retry of the primary once a chain is spent
944
979
  - Outside a handler session the tally is skipped entirely (guarded on `log.sessionActive`), so a CLI or script calling `operate()` directly emits nothing
945
980
 
946
981
  ## LLM Observability (Datadog)
@@ -1033,7 +1068,7 @@ exactly like a provider rate limit.
1033
1068
  | ------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
1034
1069
  | `max_turns` | 429 | The model asked for another tool call after `turns` ran out. Nothing failed; the run did not converge. |
1035
1070
  | `tool_errors` | 502 | Tool execution failed six times in a row and the loop stopped. |
1036
- | `incomplete` | 502 | The provider cut the model off before it finished (an output token ceiling, a content filter). `content` holds the partial text; `error.detail` names the provider's reason. |
1071
+ | `incomplete` | 502 | The provider cut the model off before it finished (an output token ceiling, a content filter). `content` holds the partial text; `error.detail` names the provider's reason and `stopReason` normalizes it. A fallback chain moves past it. |
1037
1072
 
1038
1073
  ```typescript
1039
1074
  import { LlmResponseErrorReason } from "@jaypie/llm";
@@ -1249,12 +1284,19 @@ await Llm.operate(input, {
1249
1284
  ```
1250
1285
 
1251
1286
  OpenAI, xAI, and Meta leave the limit unset (their defaults do not truncate early).
1287
+ A chain that mixes them with Anthropic, Google, or Mistral therefore mixes
1288
+ output budgets: a Gemini primary resolves to 16,384 non-streaming while an
1289
+ OpenAI fallback sends no ceiling, so swapping the primary moves the effective
1290
+ budget with nothing at the call site changing. Reasoning tokens count against
1291
+ the ceiling. Set `providerOptions.maxOutputTokens` (Google) or `max_tokens`
1292
+ (Anthropic, Mistral) on the call or chain entry to pin it.
1252
1293
  Mistral is capped (32,768 streaming / 16,384 non-streaming) despite publishing
1253
1294
  no low ceiling: a Mistral model can degenerate into restating its answer when
1254
1295
  `format` and tools are combined, and an uncapped completion turns that into a
1255
1296
  multi-minute request. Override with `providerOptions: { max_tokens }`.
1256
1297
  OpenRouter varies by routed model; pass `max_tokens` via `providerOptions`
1257
- when needed. A truncated response surfaces `stop_reason: "max_tokens"` —
1298
+ when needed. A truncated response settles `status: "incomplete"` with
1299
+ `stopReason: "max_tokens"` and moves a fallback chain to its next entry;
1258
1300
  raise the limit or switch to `stream()`.
1259
1301
 
1260
1302
  ## See Also