@skaile/transform-proxy 0.2.0 → 0.2.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.1
4
+
5
+ - Anthropic walker: the pipeline `instruction` is appended after the host's system prompt (last text block, or after a blank line for a string `system`) instead of being prepended. Claude Code gateways such as TeamClaude answer 429 `rate_limit_error` for Sonnet when the system prompt does not start with the Claude Code blocks; with the instruction first, omp and Claude Code requests for `claude-sonnet-4-5` through the proxy were refused.
6
+
3
7
  ## 0.2.0
4
8
 
5
9
  - OpenAI chat completions (`/chat/completions`) and OpenAI responses (`/responses`) walkers: text leaves, tool-call arguments and reasoning summaries are transformed outbound and restored in JSON and SSE replies; `image_url`, `input_audio`, `file`, `input_image` and `input_file` parts are refused with 400 `unsupported_content`. Responses reasoning summaries are cached by item id so a replayed reasoning item is sent back byte for byte.
package/README.md CHANGED
@@ -21,10 +21,12 @@ The proxy strips all four headers before forwarding. Every other header, includi
21
21
  |---|---|
22
22
  | `POST …/messages` | Anthropic request and response: request rewritten, JSON reply and SSE stream restored. |
23
23
  | `POST …/messages/count_tokens` | Request rewritten; the response passes through untouched. |
24
+ | `POST …/chat/completions` | OpenAI chat request and response: request rewritten, JSON reply and SSE stream restored. |
25
+ | `POST …/responses` | OpenAI responses request and response: request rewritten, JSON reply and SSE stream restored. |
24
26
  | `GET …/models` | Passthrough. |
25
- | anything else (chat completions, responses, embeddings) | 404 `unsupported_route`. |
27
+ | anything else (embeddings included) | 404 `unsupported_route`. |
26
28
 
27
- OpenAI chat and responses walkers are not implemented yet. Embeddings are refused on purpose: a vector of pseudonymized input cannot be restored.
29
+ Embeddings are refused on purpose: a vector of pseudonymized input cannot be restored.
28
30
 
29
31
  ## Refusals
30
32
 
@@ -111,6 +113,8 @@ A module default-exports a `TransformModule`:
111
113
  - It returns a `TransformFactory`, which runs once per `(session, pipeline)`: `(ctx: { sessionId, facts, log }) => Transform`.
112
114
  - A `Transform` has `instruction?`, `outbound(text, leaf)`, `inbound(text)`, `inboundStream()` and `dispose?()`.
113
115
 
116
+ The Anthropic walker appends the pipeline `instruction` after the host's system prompt: a string `system` gets it after a blank line, a block array gets it as one more text block at the end, and an absent `system` becomes the instruction. The host's leading blocks stay first, because Claude Code gateways (TeamClaude) refuse Sonnet requests with 429 `rate_limit_error` when the system prompt does not start with the Claude Code blocks. The OpenAI walkers still put the instruction first: a leading `system` message in chat completions, a prefix of `instructions` in responses.
117
+
114
118
  `inboundStream().push(c1) + … + flush()` must equal `inbound(c1 + … + cn)` for every chunking.
115
119
 
116
120
  Connectors publish facts through `proxy.session(id).publisher(source)`. Facts are in-process only.
package/dist/cli.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as TransformProxy } from "./server-D5nBnO7T.mjs";
1
+ import { i as TransformProxy } from "./server-C_eKyCTV.mjs";
2
2
  /** Environment variable that supplies the client token when `--token` is absent. */
3
3
  export declare const TOKEN_ENV = "SKAILE_TRANSFORM_PROXY_TOKEN";
4
4
  export interface CliEnvironment {
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { d as createTransformProxy, o as resolveProxyConfig, v as errorMessage } from "./config-DQPsbYLi.mjs";
2
+ import { d as createTransformProxy, o as resolveProxyConfig, v as errorMessage } from "./config-Xdn_LYpZ.mjs";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { homedir } from "node:os";
@@ -434,13 +434,18 @@ function toolNamesById(messages) {
434
434
  }
435
435
  return names;
436
436
  }
437
+ /**
438
+ * Appends the instruction after the host's system prompt. Gateways that serve only Claude Code
439
+ * traffic check that the system prompt starts with the Claude Code blocks (TeamClaude answers
440
+ * 429 for Sonnet otherwise), so the host's leading blocks must stay first.
441
+ */
437
442
  function withInstruction(system, instruction) {
438
443
  if (system === void 0) return instruction;
439
- if (typeof system === "string") return `${instruction}\n\n${system}`;
440
- if (Array.isArray(system)) return [{
444
+ if (typeof system === "string") return `${system}\n\n${instruction}`;
445
+ if (Array.isArray(system)) return [...system, {
441
446
  type: "text",
442
447
  text: instruction
443
- }, ...system];
448
+ }];
444
449
  return system;
445
450
  }
446
451
  function newBlockState(kind, transform) {
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { _ as TransformModule, a as TransformProxyOptions, c as TransformSpec, d as SessionFactPublisher, f as SessionFacts, g as TransformFactory, h as Transform, i as TransformProxy, l as Logger, m as TextLeafKind, n as SESSION_HEADER, o as UPSTREAM_HEADER, p as TextLeaf, r as TOKEN_HEADER, s as createTransformProxy, t as PIPELINE_HEADER, u as SessionFact, v as TransformSessionContext } from "./server-D5nBnO7T.mjs";
1
+ import { _ as TransformModule, a as TransformProxyOptions, c as TransformSpec, d as SessionFactPublisher, f as SessionFacts, g as TransformFactory, h as Transform, i as TransformProxy, l as Logger, m as TextLeafKind, n as SESSION_HEADER, o as UPSTREAM_HEADER, p as TextLeaf, r as TOKEN_HEADER, s as createTransformProxy, t as PIPELINE_HEADER, u as SessionFact, v as TransformSessionContext } from "./server-C_eKyCTV.mjs";
2
2
  /** One stage's streamed inbound transformer, as returned by `Transform.inboundStream()`. */
3
3
  interface InboundStream {
4
4
  push(chunk: string): string;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export { t as CONFIG_FILE_NAME, s as PIPELINE_HEADER, c as SESSION_HEADER, l as TOKEN_HEADER, u as UPSTREAM_HEADER, h as UnsupportedContentError, m as anthropicWalker, _ as composeTransforms, n as configCandidates, g as createSessionFacts, d as createTransformProxy, r as findProxyConfig, i as loadProxyConfig, p as openaiChatWalker, f as openaiResponsesWalker, a as parseProxyConfig, o as resolveProxyConfig, y as silentLogger } from "./config-DQPsbYLi.mjs";
1
+ export { t as CONFIG_FILE_NAME, s as PIPELINE_HEADER, c as SESSION_HEADER, l as TOKEN_HEADER, u as UPSTREAM_HEADER, h as UnsupportedContentError, m as anthropicWalker, _ as composeTransforms, n as configCandidates, g as createSessionFacts, d as createTransformProxy, r as findProxyConfig, i as loadProxyConfig, p as openaiChatWalker, f as openaiResponsesWalker, a as parseProxyConfig, o as resolveProxyConfig, y as silentLogger } from "./config-Xdn_LYpZ.mjs";
@@ -1,4 +1,4 @@
1
- import { a as TransformProxyOptions, i as TransformProxy } from "../server-D5nBnO7T.mjs";
1
+ import { a as TransformProxyOptions, i as TransformProxy } from "../server-C_eKyCTV.mjs";
2
2
  interface OmpModel {
3
3
  readonly provider: string;
4
4
  readonly id: string;
@@ -1,4 +1,4 @@
1
- import { c as SESSION_HEADER, d as createTransformProxy, i as loadProxyConfig, l as TOKEN_HEADER, n as configCandidates, r as findProxyConfig, u as UPSTREAM_HEADER, v as errorMessage, y as silentLogger } from "../config-DQPsbYLi.mjs";
1
+ import { c as SESSION_HEADER, d as createTransformProxy, i as loadProxyConfig, l as TOKEN_HEADER, n as configCandidates, r as findProxyConfig, u as UPSTREAM_HEADER, v as errorMessage, y as silentLogger } from "../config-Xdn_LYpZ.mjs";
2
2
  import { randomUUID } from "node:crypto";
3
3
  import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { dirname, join } from "node:path";
@@ -12,7 +12,7 @@ interface TextLeaf {
12
12
  }
13
13
  /** One session's instance of one pipeline stage. */
14
14
  interface Transform {
15
- /** Prepended to the system prompt; omit when the model needs no instruction. */
15
+ /** Added to the system prompt by each wire walker (Anthropic: appended last; OpenAI: placed first); omit when the model needs no instruction. */
16
16
  readonly instruction?: string;
17
17
  /** Host -> model, one text leaf. A throw refuses the request (502 `transform_failed`); nothing is sent. */
18
18
  outbound(text: string, leaf: TextLeaf): Promise<string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skaile/transform-proxy",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "description": "Same-protocol passthrough proxy that runs model requests and replies through a chain of text transforms (Anthropic Messages, OpenAI chat, OpenAI responses)",
6
6
  "license": "MIT",