pi-freeflow 1.0.1 → 1.0.3
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/extensions/index.ts +59 -26
- package/package.json +1 -1
package/extensions/index.ts
CHANGED
|
@@ -13,6 +13,14 @@ import { Readable } from "node:stream";
|
|
|
13
13
|
import { fileURLToPath } from "node:url";
|
|
14
14
|
import type { ExtensionAPI, ExtensionUIContext } from "@earendil-works/pi-coding-agent";
|
|
15
15
|
|
|
16
|
+
|
|
17
|
+
// Ensure subagents and OMP resolver recognize active credentials for freeflow
|
|
18
|
+
if (!process.env.FREEFLOW_API_KEY) {
|
|
19
|
+
process.env.FREEFLOW_API_KEY = "freeflow";
|
|
20
|
+
}
|
|
21
|
+
if (!process.env.BANSOS_API_KEY) {
|
|
22
|
+
process.env.BANSOS_API_KEY = "freeflow";
|
|
23
|
+
}
|
|
16
24
|
// ── Configuration ──────────────────────────────────────────────────
|
|
17
25
|
const UPSTREAM_OPENCODE = "https://opencode.ai/zen";
|
|
18
26
|
// KiloCode gateway — OpenAI-compatible; free models are keyless (200 req/hr per IP)
|
|
@@ -836,6 +844,54 @@ function sanitizeHeaders(
|
|
|
836
844
|
return sanitized;
|
|
837
845
|
}
|
|
838
846
|
|
|
847
|
+
function normalizeRequestBody(
|
|
848
|
+
body: Record<string, unknown>,
|
|
849
|
+
isRelay = false,
|
|
850
|
+
): Record<string, unknown> {
|
|
851
|
+
// 1. Tool choice normalization (OpenCode Zen only supports "auto" or undefined)
|
|
852
|
+
if (body.tool_choice === "none") {
|
|
853
|
+
delete body.tool_choice;
|
|
854
|
+
delete body.tools;
|
|
855
|
+
} else if (body.tool_choice && body.tool_choice !== "auto") {
|
|
856
|
+
body.tool_choice = "auto";
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// 2. Reasoning normalization
|
|
860
|
+
if (typeof body.reasoning_effort === "string") {
|
|
861
|
+
const re = body.reasoning_effort.toLowerCase();
|
|
862
|
+
if (re === "xhigh" || re === "max") {
|
|
863
|
+
body.reasoning_effort = "max";
|
|
864
|
+
} else if (re === "high" || re === "medium") {
|
|
865
|
+
body.reasoning_effort = "high";
|
|
866
|
+
} else if (re === "none" || re === "off") {
|
|
867
|
+
delete body.reasoning_effort;
|
|
868
|
+
} else {
|
|
869
|
+
body.reasoning_effort = "low";
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
if (body.reasoning && typeof body.reasoning === "object") {
|
|
873
|
+
const r = body.reasoning as Record<string, unknown>;
|
|
874
|
+
if (r.effort === "none" || r.effort === "off") {
|
|
875
|
+
delete r.effort;
|
|
876
|
+
} else if (typeof r.effort === "string") {
|
|
877
|
+
const re = r.effort.toLowerCase();
|
|
878
|
+
if (re === "xhigh" || re === "max") r.effort = "max";
|
|
879
|
+
else if (re === "high" || re === "medium") r.effort = "high";
|
|
880
|
+
else r.effort = "low";
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
// 3. Max tokens clamp for Vercel relay
|
|
885
|
+
if (isRelay) {
|
|
886
|
+
const mt = body.max_tokens ?? body.maxTokens;
|
|
887
|
+
if (typeof mt === "number" && mt > RELAY_MAX_TOKENS) {
|
|
888
|
+
body.max_tokens = RELAY_MAX_TOKENS;
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
return body;
|
|
893
|
+
}
|
|
894
|
+
|
|
839
895
|
// ponytail: shared stream pipe — upstream abort/timeout must end response,
|
|
840
896
|
// not become an uncaught exception that crashes pi.
|
|
841
897
|
function pipeUpstreamStream(
|
|
@@ -1009,27 +1065,11 @@ function startProxy(
|
|
|
1009
1065
|
activeHost,
|
|
1010
1066
|
);
|
|
1011
1067
|
try {
|
|
1012
|
-
// ponytail: clamp max_tokens for Vercel relay — large values
|
|
1013
|
-
// cause 400 "Upstream request failed" (response size / duration
|
|
1014
|
-
// limits). Direct mode stays unconstrained.
|
|
1015
1068
|
let relayBody = bodyChunks.length
|
|
1016
1069
|
? Buffer.concat(bodyChunks)
|
|
1017
1070
|
: undefined;
|
|
1018
1071
|
if (relayBody && parsedBody) {
|
|
1019
|
-
|
|
1020
|
-
if (typeof mt === "number" && mt > RELAY_MAX_TOKENS) {
|
|
1021
|
-
parsedBody.max_tokens = RELAY_MAX_TOKENS;
|
|
1022
|
-
}
|
|
1023
|
-
if (typeof parsedBody.reasoning_effort === "string") {
|
|
1024
|
-
const re = parsedBody.reasoning_effort.toLowerCase();
|
|
1025
|
-
if (re === "xhigh" || re === "max") {
|
|
1026
|
-
parsedBody.reasoning_effort = "max";
|
|
1027
|
-
} else if (re === "high" || re === "medium") {
|
|
1028
|
-
parsedBody.reasoning_effort = "high";
|
|
1029
|
-
} else {
|
|
1030
|
-
parsedBody.reasoning_effort = "low";
|
|
1031
|
-
}
|
|
1032
|
-
}
|
|
1072
|
+
normalizeRequestBody(parsedBody, true);
|
|
1033
1073
|
relayBody = Buffer.from(JSON.stringify(parsedBody));
|
|
1034
1074
|
}
|
|
1035
1075
|
const response = await relayFetch(fullUrl, {
|
|
@@ -1073,15 +1113,8 @@ function startProxy(
|
|
|
1073
1113
|
}
|
|
1074
1114
|
// direct path (existing, untouched)
|
|
1075
1115
|
let directBody = Buffer.concat(bodyChunks);
|
|
1076
|
-
if (parsedBody
|
|
1077
|
-
|
|
1078
|
-
if (re === "xhigh" || re === "max") {
|
|
1079
|
-
parsedBody.reasoning_effort = "max";
|
|
1080
|
-
} else if (re === "high" || re === "medium") {
|
|
1081
|
-
parsedBody.reasoning_effort = "high";
|
|
1082
|
-
} else {
|
|
1083
|
-
parsedBody.reasoning_effort = "low";
|
|
1084
|
-
}
|
|
1116
|
+
if (parsedBody) {
|
|
1117
|
+
normalizeRequestBody(parsedBody, false);
|
|
1085
1118
|
directBody = Buffer.from(JSON.stringify(parsedBody));
|
|
1086
1119
|
}
|
|
1087
1120
|
const fwd = sanitizeHeaders(req.headers, target.hostname);
|