pi-roundtable-sandbox 0.7.13 → 0.7.14
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 +4 -0
- package/package.json +2 -2
- package/src/pi-broker.ts +23 -7
- package/src/pi-model-input.ts +8 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.14
|
|
4
|
+
|
|
5
|
+
- A model request the broker refuses (an unknown role, a native tool, remote media, a remote schema) is answered with a 400 in the Anthropic API's error shape (`invalid_request_error`) instead of a 502, and logged as `sandbox upstream call failed`. Claude Code 2.1.284 (agent SDK 0.3.284) sends a mid-conversation `role: "system"` message when MCP tools arrive late; the broker refused it with a 502, which Claude Code retried about ten times over several minutes before the turn failed. The refusal of a role now reads `messages.N: Unexpected role "system": the input message role must be "user" or "assistant"`, which Claude Code recognizes: it resends without system messages for the rest of that conversation. The broker still never forwards a system message.
|
|
6
|
+
|
|
3
7
|
## 0.7.13
|
|
4
8
|
|
|
5
9
|
- `precheckScriptRunner` forwards only the tools approved with the script (pi-roundtable 0.7.13's `PrecheckScriptContext.tools`) among those `grant` allows, and takes `toolName(server, tool)`, the name the host's hold rules know a tool by (default the tool's own name).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-roundtable-sandbox",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.14",
|
|
4
4
|
"description": "Sealed guest channels and an allow-listed credential broker for pi-roundtable",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -52,7 +52,7 @@
|
|
|
52
52
|
"@biomejs/biome": "2.5.15",
|
|
53
53
|
"@types/bun": "1.4.2",
|
|
54
54
|
"discord.js": "14.27.0",
|
|
55
|
-
"pi-roundtable": "0.7.
|
|
55
|
+
"pi-roundtable": "0.7.14",
|
|
56
56
|
"typebox": "1.3.34",
|
|
57
57
|
"typescript": "7.0.2"
|
|
58
58
|
}
|
package/src/pi-broker.ts
CHANGED
|
@@ -616,13 +616,29 @@ export class PiSandboxBroker {
|
|
|
616
616
|
if (value && value.length <= 256) headers.set(name, value);
|
|
617
617
|
}
|
|
618
618
|
} else {
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
619
|
+
try {
|
|
620
|
+
body = piModelInput(
|
|
621
|
+
input,
|
|
622
|
+
this.#options.model,
|
|
623
|
+
url.pathname.endsWith("/count_tokens"),
|
|
624
|
+
this.#options.maxOutputTokens ?? 128_000,
|
|
625
|
+
turn.context.thinking,
|
|
626
|
+
);
|
|
627
|
+
} catch (error) {
|
|
628
|
+
// The request itself is refused: a 400 the client does not retry unchanged, in the
|
|
629
|
+
// API's error shape so it can resend without what was refused. A 502 would be retried.
|
|
630
|
+
failed(secret, { status: 400, error });
|
|
631
|
+
return Response.json(
|
|
632
|
+
{
|
|
633
|
+
type: "error",
|
|
634
|
+
error: {
|
|
635
|
+
type: "invalid_request_error",
|
|
636
|
+
message: error instanceof Error ? error.message : String(error),
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
{ status: 400 },
|
|
640
|
+
);
|
|
641
|
+
}
|
|
626
642
|
target = `${this.#options.upstream ?? "https://api.anthropic.com"}${url.pathname.slice("/anthropic".length)}${url.search}`;
|
|
627
643
|
secret = await this.#options.oauthToken();
|
|
628
644
|
for (const name of HEADERS) {
|
package/src/pi-model-input.ts
CHANGED
|
@@ -106,12 +106,14 @@ export function piModelInput(
|
|
|
106
106
|
throw new Error("Invalid messages");
|
|
107
107
|
const body: Record<string, unknown> = {
|
|
108
108
|
model,
|
|
109
|
-
messages: input.messages.map((message: unknown) => {
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
throw new Error(
|
|
109
|
+
messages: input.messages.map((message: unknown, index) => {
|
|
110
|
+
if (!isRecord(message)) throw new Error("Invalid message");
|
|
111
|
+
// Worded as the Anthropic API words it, so a client that sends a mid-conversation
|
|
112
|
+
// `role: "system"` message (Claude Code does) recognizes the refusal and resends without it.
|
|
113
|
+
if (!["user", "assistant"].includes(String(message.role)))
|
|
114
|
+
throw new Error(
|
|
115
|
+
`messages.${index}: Unexpected role ${JSON.stringify(String(message.role))}: the input message role must be "user" or "assistant"`,
|
|
116
|
+
);
|
|
115
117
|
return { role: message.role, content: blocks(message.content) };
|
|
116
118
|
}),
|
|
117
119
|
};
|