@koda-sl/baker-bridge 0.123.0-dev.f9f855bf1 → 0.124.0-dev.7e3937022
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/README.md +55 -17
- package/dist/broker/baker-api.d.ts +23 -0
- package/dist/broker/baker-api.d.ts.map +1 -0
- package/dist/broker/baker-api.js +55 -0
- package/dist/broker/baker-api.js.map +1 -0
- package/dist/broker/broker-client.d.ts +10 -0
- package/dist/broker/broker-client.d.ts.map +1 -0
- package/dist/broker/broker-client.js +60 -0
- package/dist/broker/broker-client.js.map +1 -0
- package/dist/broker/ca.d.ts +38 -0
- package/dist/broker/ca.d.ts.map +1 -0
- package/dist/broker/ca.js +80 -0
- package/dist/broker/ca.js.map +1 -0
- package/dist/broker/ca.test.d.ts +2 -0
- package/dist/broker/ca.test.d.ts.map +1 -0
- package/dist/broker/ca.test.js +58 -0
- package/dist/broker/ca.test.js.map +1 -0
- package/dist/broker/credential-cache.d.ts +24 -0
- package/dist/broker/credential-cache.d.ts.map +1 -0
- package/dist/broker/credential-cache.js +69 -0
- package/dist/broker/credential-cache.js.map +1 -0
- package/dist/broker/credential-cache.test.d.ts +2 -0
- package/dist/broker/credential-cache.test.d.ts.map +1 -0
- package/dist/broker/credential-cache.test.js +120 -0
- package/dist/broker/credential-cache.test.js.map +1 -0
- package/dist/broker/credential-text.d.ts +45 -0
- package/dist/broker/credential-text.d.ts.map +1 -0
- package/dist/broker/credential-text.js +103 -0
- package/dist/broker/credential-text.js.map +1 -0
- package/dist/broker/credential-text.test.d.ts +2 -0
- package/dist/broker/credential-text.test.d.ts.map +1 -0
- package/dist/broker/credential-text.test.js +54 -0
- package/dist/broker/credential-text.test.js.map +1 -0
- package/dist/broker/env.d.ts +19 -0
- package/dist/broker/env.d.ts.map +1 -0
- package/dist/broker/env.js +16 -0
- package/dist/broker/env.js.map +1 -0
- package/dist/broker/main.d.ts +3 -0
- package/dist/broker/main.d.ts.map +1 -0
- package/dist/broker/main.js +51 -0
- package/dist/broker/main.js.map +1 -0
- package/dist/broker/proxy.d.ts +24 -0
- package/dist/broker/proxy.d.ts.map +1 -0
- package/dist/broker/proxy.integration.test.d.ts +2 -0
- package/dist/broker/proxy.integration.test.d.ts.map +1 -0
- package/dist/broker/proxy.integration.test.js +201 -0
- package/dist/broker/proxy.integration.test.js.map +1 -0
- package/dist/broker/proxy.js +147 -0
- package/dist/broker/proxy.js.map +1 -0
- package/dist/broker/rewrite.d.ts +44 -0
- package/dist/broker/rewrite.d.ts.map +1 -0
- package/dist/broker/rewrite.js +291 -0
- package/dist/broker/rewrite.js.map +1 -0
- package/dist/broker/rewrite.test.d.ts +2 -0
- package/dist/broker/rewrite.test.d.ts.map +1 -0
- package/dist/broker/rewrite.test.js +171 -0
- package/dist/broker/rewrite.test.js.map +1 -0
- package/dist/broker/socket-service.d.ts +18 -0
- package/dist/broker/socket-service.d.ts.map +1 -0
- package/dist/broker/socket-service.js +103 -0
- package/dist/broker/socket-service.js.map +1 -0
- package/dist/broker/socket-service.test.d.ts +2 -0
- package/dist/broker/socket-service.test.d.ts.map +1 -0
- package/dist/broker/socket-service.test.js +73 -0
- package/dist/broker/socket-service.test.js.map +1 -0
- package/dist/broker/token-bucket.d.ts +9 -0
- package/dist/broker/token-bucket.d.ts.map +1 -0
- package/dist/broker/token-bucket.js +18 -0
- package/dist/broker/token-bucket.js.map +1 -0
- package/dist/broker/usage-reporter.d.ts +18 -0
- package/dist/broker/usage-reporter.d.ts.map +1 -0
- package/dist/broker/usage-reporter.js +31 -0
- package/dist/broker/usage-reporter.js.map +1 -0
- package/dist/broker/usage-reporter.test.d.ts +2 -0
- package/dist/broker/usage-reporter.test.d.ts.map +1 -0
- package/dist/broker/usage-reporter.test.js +20 -0
- package/dist/broker/usage-reporter.test.js.map +1 -0
- package/dist/hono/agent-session.d.ts +3 -0
- package/dist/hono/agent-session.d.ts.map +1 -1
- package/dist/hono/agent-session.js +15 -0
- package/dist/hono/agent-session.js.map +1 -1
- package/dist/hono/agent.d.ts +2 -0
- package/dist/hono/agent.d.ts.map +1 -1
- package/dist/hono/agent.js +10 -2
- package/dist/hono/agent.js.map +1 -1
- package/dist/hono/avatar-accent.d.ts +17 -5
- package/dist/hono/avatar-accent.d.ts.map +1 -1
- package/dist/hono/avatar-accent.js +31 -64
- package/dist/hono/avatar-accent.js.map +1 -1
- package/dist/hono/avatar-accent.test.js +46 -35
- package/dist/hono/avatar-accent.test.js.map +1 -1
- package/dist/hono/avatar-casting.d.ts +1 -0
- package/dist/hono/avatar-casting.d.ts.map +1 -1
- package/dist/hono/avatar-casting.js +15 -2
- package/dist/hono/avatar-casting.js.map +1 -1
- package/dist/hono/avatar-casting.test.js +5 -0
- package/dist/hono/avatar-casting.test.js.map +1 -1
- package/dist/hono/avatar-face-question.d.ts +23 -6
- package/dist/hono/avatar-face-question.d.ts.map +1 -1
- package/dist/hono/avatar-face-question.js +48 -59
- package/dist/hono/avatar-face-question.js.map +1 -1
- package/dist/hono/avatar-face-question.test.js +46 -101
- package/dist/hono/avatar-face-question.test.js.map +1 -1
- package/dist/hono/baker-ui-tool-schemas.test.js +6 -0
- package/dist/hono/baker-ui-tool-schemas.test.js.map +1 -1
- package/dist/hono/bridge-job.d.ts +19 -1
- package/dist/hono/bridge-job.d.ts.map +1 -1
- package/dist/hono/bridge-job.js +94 -15
- package/dist/hono/bridge-job.js.map +1 -1
- package/dist/hono/bridge-job.test.js +152 -2
- package/dist/hono/bridge-job.test.js.map +1 -1
- package/dist/hono/connection-input.d.ts +3 -0
- package/dist/hono/connection-input.d.ts.map +1 -1
- package/dist/hono/connection-input.js +28 -4
- package/dist/hono/connection-input.js.map +1 -1
- package/dist/hono/connection-input.test.js +11 -0
- package/dist/hono/connection-input.test.js.map +1 -1
- package/dist/hono/convex.d.ts +8 -0
- package/dist/hono/convex.d.ts.map +1 -1
- package/dist/hono/convex.js +8 -0
- package/dist/hono/convex.js.map +1 -1
- package/dist/hono/credential-input.d.ts +39 -0
- package/dist/hono/credential-input.d.ts.map +1 -0
- package/dist/hono/credential-input.js +78 -0
- package/dist/hono/credential-input.js.map +1 -0
- package/dist/hono/credential-scrub.d.ts +41 -0
- package/dist/hono/credential-scrub.d.ts.map +1 -0
- package/dist/hono/credential-scrub.js +152 -0
- package/dist/hono/credential-scrub.js.map +1 -0
- package/dist/hono/credential-scrub.test.d.ts +2 -0
- package/dist/hono/credential-scrub.test.d.ts.map +1 -0
- package/dist/hono/credential-scrub.test.js +105 -0
- package/dist/hono/credential-scrub.test.js.map +1 -0
- package/dist/hono/jev.d.ts +16 -0
- package/dist/hono/jev.d.ts.map +1 -0
- package/dist/hono/jev.js +60 -0
- package/dist/hono/jev.js.map +1 -0
- package/dist/hono/jev.test.d.ts +2 -0
- package/dist/hono/jev.test.d.ts.map +1 -0
- package/dist/hono/jev.test.js +15 -0
- package/dist/hono/jev.test.js.map +1 -0
- package/dist/hono/latitude-telemetry.d.ts.map +1 -1
- package/dist/hono/latitude-telemetry.js +8 -2
- package/dist/hono/latitude-telemetry.js.map +1 -1
- package/dist/hono/openrouter-session.d.ts.map +1 -1
- package/dist/hono/openrouter-session.js +12 -1
- package/dist/hono/openrouter-session.js.map +1 -1
- package/dist/hono/openrouter-session.test.js +9 -3
- package/dist/hono/openrouter-session.test.js.map +1 -1
- package/dist/hono/prompt-context.d.ts +8 -1
- package/dist/hono/prompt-context.d.ts.map +1 -1
- package/dist/hono/prompt-context.js +45 -7
- package/dist/hono/prompt-context.js.map +1 -1
- package/dist/hono/prompt-context.test.js +35 -1
- package/dist/hono/prompt-context.test.js.map +1 -1
- package/dist/hono/relay.d.ts +10 -1
- package/dist/hono/relay.d.ts.map +1 -1
- package/dist/hono/relay.js +6 -0
- package/dist/hono/relay.js.map +1 -1
- package/dist/hono/server.d.ts.map +1 -1
- package/dist/hono/server.js +22 -0
- package/dist/hono/server.js.map +1 -1
- package/dist/hono/tag-input.d.ts +106 -3
- package/dist/hono/tag-input.d.ts.map +1 -1
- package/dist/hono/tag-input.js +20 -9
- package/dist/hono/tag-input.js.map +1 -1
- package/dist/hono/tag-input.test.js +19 -1
- package/dist/hono/tag-input.test.js.map +1 -1
- package/dist/hono/types.d.ts +8 -0
- package/dist/hono/types.d.ts.map +1 -1
- package/dist/review/digest.d.ts +8 -1
- package/dist/review/digest.d.ts.map +1 -1
- package/dist/review/digest.js +10 -43
- package/dist/review/digest.js.map +1 -1
- package/dist/review/digest.test.js +11 -6
- package/dist/review/digest.test.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { brokerRunning, scrubViaBroker } from "../broker/broker-client.js";
|
|
2
|
+
/** Joins string leaves for one broker call; a leaf that itself contains it is sent alone. */
|
|
3
|
+
const SEPARATOR = "\u0000";
|
|
4
|
+
function collectLeaves(value, key, into) {
|
|
5
|
+
if (typeof value === "string")
|
|
6
|
+
into.push({ text: value, key });
|
|
7
|
+
else if (Array.isArray(value))
|
|
8
|
+
for (const item of value)
|
|
9
|
+
collectLeaves(item, key, into);
|
|
10
|
+
else if (typeof value === "object" && value !== null)
|
|
11
|
+
for (const [name, item] of Object.entries(value))
|
|
12
|
+
collectLeaves(item, name, into);
|
|
13
|
+
}
|
|
14
|
+
/** `value` with its string leaves replaced in the order `collectLeaves` read them. */
|
|
15
|
+
function replaceLeaves(value, next) {
|
|
16
|
+
if (typeof value === "string")
|
|
17
|
+
return next();
|
|
18
|
+
if (Array.isArray(value))
|
|
19
|
+
return value.map((item) => replaceLeaves(item, next));
|
|
20
|
+
if (typeof value === "object" && value !== null) {
|
|
21
|
+
return Object.fromEntries(Object.entries(value).map(([name, item]) => [name, replaceLeaves(item, next)]));
|
|
22
|
+
}
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
/** Media (a PDF or image read as base64): long and pure base64, so it cannot hold a value verbatim. */
|
|
26
|
+
const MEDIA_MIN_LENGTH = 64 * 1024;
|
|
27
|
+
const PURE_BASE64 = /^[A-Za-z0-9+/=\r\n]+$/;
|
|
28
|
+
/** Well under the broker socket's 2 MB cap, even after JSON escaping. */
|
|
29
|
+
export const MAX_SCRUB_BATCH_BYTES = 1024 * 1024;
|
|
30
|
+
function isMedia(leaf) {
|
|
31
|
+
if (!PURE_BASE64.test(leaf.text))
|
|
32
|
+
return false;
|
|
33
|
+
return leaf.text.length >= MEDIA_MIN_LENGTH || leaf.key === "base64";
|
|
34
|
+
}
|
|
35
|
+
const WITHHELD = "This output was withheld: Baker could not check it for saved credentials. Run the step again in a moment.";
|
|
36
|
+
const TOO_LARGE = "This text was withheld: it is too large for Baker to check for saved credentials. Read it in smaller parts.";
|
|
37
|
+
function cost(text) {
|
|
38
|
+
return Buffer.byteLength(JSON.stringify(text));
|
|
39
|
+
}
|
|
40
|
+
/** Indexes of the leaves to scrub, grouped so each group fits one broker call, and those too large to check. */
|
|
41
|
+
function batches(leaves) {
|
|
42
|
+
const groups = [];
|
|
43
|
+
const tooLarge = [];
|
|
44
|
+
let current = [];
|
|
45
|
+
let size = 0;
|
|
46
|
+
leaves.forEach((leaf, index) => {
|
|
47
|
+
if (leaf.text.length === 0 || (leaf.text.length >= MEDIA_MIN_LENGTH && isMedia(leaf)))
|
|
48
|
+
return;
|
|
49
|
+
const bytes = cost(leaf.text) + cost(SEPARATOR);
|
|
50
|
+
if (bytes > MAX_SCRUB_BATCH_BYTES) {
|
|
51
|
+
tooLarge.push(index);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (leaf.text.includes(SEPARATOR)) {
|
|
55
|
+
groups.push([index]);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (size + bytes > MAX_SCRUB_BATCH_BYTES && current.length > 0) {
|
|
59
|
+
groups.push(current);
|
|
60
|
+
current = [];
|
|
61
|
+
size = 0;
|
|
62
|
+
}
|
|
63
|
+
current.push(index);
|
|
64
|
+
size += bytes;
|
|
65
|
+
});
|
|
66
|
+
if (current.length > 0)
|
|
67
|
+
groups.push(current);
|
|
68
|
+
return { groups, tooLarge };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The tool result with every credential value replaced by its placeholder,
|
|
72
|
+
* walking every string in it. Long pure-base64 leaves are skipped; the rest go
|
|
73
|
+
* to the broker in calls of at most 1 MB, and a text over 1 MB on its own is
|
|
74
|
+
* withheld. Throws — so the caller withholds the result — when the broker fails.
|
|
75
|
+
*/
|
|
76
|
+
export async function scrubToolResponse(response, scrub) {
|
|
77
|
+
const leaves = [];
|
|
78
|
+
collectLeaves(response, undefined, leaves);
|
|
79
|
+
const scrubbed = leaves.map((leaf) => leaf.text);
|
|
80
|
+
const { groups, tooLarge } = batches(leaves);
|
|
81
|
+
for (const index of tooLarge)
|
|
82
|
+
scrubbed[index] = TOO_LARGE;
|
|
83
|
+
for (const group of groups) {
|
|
84
|
+
const joined = group.map((index) => leaves[index]?.text ?? "").join(SEPARATOR);
|
|
85
|
+
const result = await scrub(joined);
|
|
86
|
+
const parts = group.length === 1 ? [result] : result.split(SEPARATOR);
|
|
87
|
+
if (parts.length !== group.length)
|
|
88
|
+
throw new Error("scrubbed text no longer lines up with the tool result");
|
|
89
|
+
group.forEach((index, position) => {
|
|
90
|
+
scrubbed[index] = parts[position] ?? "";
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (scrubbed.every((text, index) => text === leaves[index]?.text))
|
|
94
|
+
return { changed: false, response };
|
|
95
|
+
let index = 0;
|
|
96
|
+
return { changed: true, response: replaceLeaves(response, () => scrubbed[index++] ?? "") };
|
|
97
|
+
}
|
|
98
|
+
/** Fields that name what a result is (`type: "text"`), never what it says; a tool's output schema requires them as-is. */
|
|
99
|
+
const STRUCTURAL_KEYS = new Set(["type", "media_type", "mimeType", "status", "kind", "mode"]);
|
|
100
|
+
/**
|
|
101
|
+
* The result with every text that could hold a value withheld, in the tool's
|
|
102
|
+
* own shape: the SDK checks a rewrite against the tool's output schema and
|
|
103
|
+
* falls back to the original — unscrubbed — output when it does not fit.
|
|
104
|
+
*/
|
|
105
|
+
export function withheldToolResponse(response) {
|
|
106
|
+
const leaves = [];
|
|
107
|
+
collectLeaves(response, undefined, leaves);
|
|
108
|
+
let index = 0;
|
|
109
|
+
return replaceLeaves(response, () => {
|
|
110
|
+
const leaf = leaves[index++];
|
|
111
|
+
if (leaf === undefined)
|
|
112
|
+
return WITHHELD;
|
|
113
|
+
return STRUCTURAL_KEYS.has(leaf.key ?? "") || isMedia(leaf) ? leaf.text : WITHHELD;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* PostToolUse for every tool. The SDK runs PostToolUse hooks in parallel on
|
|
118
|
+
* the original output and the last `updatedToolOutput` written wins; no other
|
|
119
|
+
* hook sets `updatedToolOutput`, so this one's is the output the model reads.
|
|
120
|
+
*/
|
|
121
|
+
export function credentialScrubHooks({ scrub = (text) => scrubViaBroker(text), brokerRunning: running = () => brokerRunning(), } = {}) {
|
|
122
|
+
return {
|
|
123
|
+
PostToolUse: [
|
|
124
|
+
{
|
|
125
|
+
hooks: [
|
|
126
|
+
async (input) => {
|
|
127
|
+
if (input.hook_event_name !== "PostToolUse" || !running())
|
|
128
|
+
return {};
|
|
129
|
+
try {
|
|
130
|
+
const result = await scrubToolResponse(input.tool_response, scrub);
|
|
131
|
+
if (!result.changed)
|
|
132
|
+
return {};
|
|
133
|
+
return {
|
|
134
|
+
hookSpecificOutput: { hookEventName: "PostToolUse", updatedToolOutput: result.response },
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
catch (error) {
|
|
138
|
+
console.error("[credentials] could not scrub a tool result — withholding it", error);
|
|
139
|
+
return {
|
|
140
|
+
hookSpecificOutput: {
|
|
141
|
+
hookEventName: "PostToolUse",
|
|
142
|
+
updatedToolOutput: withheldToolResponse(input.tool_response),
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
//# sourceMappingURL=credential-scrub.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-scrub.js","sourceRoot":"","sources":["../../src/hono/credential-scrub.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-scrub.test.d.ts","sourceRoot":"","sources":["../../src/hono/credential-scrub.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { credentialScrubHooks, scrubToolResponse } from "./credential-scrub.js";
|
|
3
|
+
const fakeScrub = (text) => Promise.resolve(text.split("shpat_secret_value").join("{{credential:shop}}"));
|
|
4
|
+
/** The broker socket refuses a body over 2 MB. */
|
|
5
|
+
const socketScrub = (text) => Buffer.byteLength(JSON.stringify({ text })) > 2 * 1024 * 1024
|
|
6
|
+
? Promise.reject(new Error("credential broker /scrub answered 413"))
|
|
7
|
+
: fakeScrub(text);
|
|
8
|
+
describe("scrubToolResponse", () => {
|
|
9
|
+
it("replaces a credential value anywhere in a tool result, keeping its shape", async () => {
|
|
10
|
+
const response = { stdout: "token=shpat_secret_value\n", stderr: "", nested: [{ body: "x shpat_secret_value" }] };
|
|
11
|
+
await expect(scrubToolResponse(response, fakeScrub)).resolves.toEqual({
|
|
12
|
+
changed: true,
|
|
13
|
+
response: { stdout: "token={{credential:shop}}\n", stderr: "", nested: [{ body: "x {{credential:shop}}" }] },
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
it("reports no change when nothing in the result was a credential", async () => {
|
|
17
|
+
await expect(scrubToolResponse({ stdout: "ok" }, fakeScrub)).resolves.toEqual({
|
|
18
|
+
changed: false,
|
|
19
|
+
response: { stdout: "ok" },
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
it("leaves large media untouched instead of withholding the whole result", async () => {
|
|
23
|
+
const media = Buffer.alloc(3 * 1024 * 1024, 7).toString("base64");
|
|
24
|
+
const response = [
|
|
25
|
+
{ type: "image", source: { data: media } },
|
|
26
|
+
{ type: "text", text: "shpat_secret_value" },
|
|
27
|
+
];
|
|
28
|
+
const result = await scrubToolResponse(response, socketScrub);
|
|
29
|
+
expect(result).toEqual({
|
|
30
|
+
changed: true,
|
|
31
|
+
response: [
|
|
32
|
+
{ type: "image", source: { data: media } },
|
|
33
|
+
{ type: "text", text: "{{credential:shop}}" },
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
it("checks a result larger than one broker call in several calls", async () => {
|
|
38
|
+
const chunk = `${"log line\n".repeat(80_000)}shpat_secret_value`;
|
|
39
|
+
const response = { outputs: [chunk, chunk, chunk] };
|
|
40
|
+
const result = await scrubToolResponse(response, socketScrub);
|
|
41
|
+
const scrubbedChunk = `${"log line\n".repeat(80_000)}{{credential:shop}}`;
|
|
42
|
+
expect(result).toEqual({ changed: true, response: { outputs: [scrubbedChunk, scrubbedChunk, scrubbedChunk] } });
|
|
43
|
+
});
|
|
44
|
+
it("withholds only a text over 1 MB, and still checks the rest", async () => {
|
|
45
|
+
const response = { stdout: "word ".repeat(210_000), stderr: "shpat_secret_value" };
|
|
46
|
+
const result = await scrubToolResponse(response, socketScrub);
|
|
47
|
+
expect(result).toEqual({
|
|
48
|
+
changed: true,
|
|
49
|
+
response: { stdout: expect.stringContaining("withheld"), stderr: "{{credential:shop}}" },
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
it("scrubs a text that contains the separator byte without misaligning the others", async () => {
|
|
53
|
+
const response = { stdout: "bin\u0000ary shpat_secret_value", stderr: "shpat_secret_value" };
|
|
54
|
+
await expect(scrubToolResponse(response, fakeScrub)).resolves.toEqual({
|
|
55
|
+
changed: true,
|
|
56
|
+
response: { stdout: "bin\u0000ary {{credential:shop}}", stderr: "{{credential:shop}}" },
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
async function runHook(response, options) {
|
|
61
|
+
const hook = credentialScrubHooks(options).PostToolUse?.[0]?.hooks[0];
|
|
62
|
+
if (hook === undefined)
|
|
63
|
+
throw new Error("no PostToolUse hook");
|
|
64
|
+
const output = await hook({
|
|
65
|
+
hook_event_name: "PostToolUse",
|
|
66
|
+
tool_name: "Read",
|
|
67
|
+
tool_input: {},
|
|
68
|
+
tool_response: response,
|
|
69
|
+
tool_use_id: "t1",
|
|
70
|
+
session_id: "s",
|
|
71
|
+
transcript_path: "",
|
|
72
|
+
cwd: "/",
|
|
73
|
+
}, "t1", { signal: new AbortController().signal });
|
|
74
|
+
return "hookSpecificOutput" in output && output.hookSpecificOutput?.hookEventName === "PostToolUse"
|
|
75
|
+
? output.hookSpecificOutput.updatedToolOutput
|
|
76
|
+
: undefined;
|
|
77
|
+
}
|
|
78
|
+
describe("credentialScrubHooks", () => {
|
|
79
|
+
it("withholds a result it could not check in the tool's own shape, which the SDK only accepts", async () => {
|
|
80
|
+
const read = {
|
|
81
|
+
type: "text",
|
|
82
|
+
file: { filePath: "/repo/.env", content: "TOKEN=shpat_secret_value", numLines: 1, startLine: 1, totalLines: 1 },
|
|
83
|
+
};
|
|
84
|
+
const output = await runHook(read, {
|
|
85
|
+
scrub: () => Promise.reject(new Error("credential broker /scrub timed out")),
|
|
86
|
+
brokerRunning: () => true,
|
|
87
|
+
});
|
|
88
|
+
expect(output).toEqual({
|
|
89
|
+
type: "text",
|
|
90
|
+
file: {
|
|
91
|
+
filePath: expect.stringContaining("withheld"),
|
|
92
|
+
content: expect.stringContaining("withheld"),
|
|
93
|
+
numLines: 1,
|
|
94
|
+
startLine: 1,
|
|
95
|
+
totalLines: 1,
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
it("leaves a result alone when no broker runs, however large", async () => {
|
|
100
|
+
const response = { stdout: "word ".repeat(210_000) };
|
|
101
|
+
const output = await runHook(response, { scrub: fakeScrub, brokerRunning: () => false });
|
|
102
|
+
expect(output).toBeUndefined();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
//# sourceMappingURL=credential-scrub.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"credential-scrub.test.js","sourceRoot":"","sources":["../../src/hono/credential-scrub.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Pinned: the thresholds its callers act on were measured against this version. */
|
|
2
|
+
export declare const JEV_MODEL = "typesafe/jev-1.13";
|
|
3
|
+
/** Asks each statement as a yes/no question; the probability of yes for each, or null when unanswered. */
|
|
4
|
+
export type Judge = (questions: ReadonlyArray<JevQuestion>) => Promise<number[] | null>;
|
|
5
|
+
export type JevQuestion = {
|
|
6
|
+
instructions: Record<string, string>;
|
|
7
|
+
};
|
|
8
|
+
/** Each yes/no answer to `ids`, in order; null when the reply does not answer them all. Pure. */
|
|
9
|
+
export declare function readNouls(body: unknown, ids: readonly string[]): number[] | null;
|
|
10
|
+
/**
|
|
11
|
+
* A judge on `key`: the probability that each statement is true, in order (a Noul each, one
|
|
12
|
+
* request), or null when Jev could not answer: no key, a timeout, an outage. There is no
|
|
13
|
+
* route to fall back to, so the caller decides what null means.
|
|
14
|
+
*/
|
|
15
|
+
export declare const jevJudge: (key: string | undefined) => Judge;
|
|
16
|
+
//# sourceMappingURL=jev.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jev.d.ts","sourceRoot":"","sources":["../../src/hono/jev.ts"],"names":[],"mappings":"AAUA,oFAAoF;AACpF,eAAO,MAAM,SAAS,sBAAsB,CAAC;AAK7C,0GAA0G;AAC1G,MAAM,MAAM,KAAK,GAAG,CAAC,SAAS,EAAE,aAAa,CAAC,WAAW,CAAC,KAAK,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;AACxF,MAAM,MAAM,WAAW,GAAG;IAAE,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAAE,CAAC;AAInE,iGAAiG;AACjG,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,GAAG,IAAI,CAUhF;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,QACb,MAAM,GAAG,SAAS,KAAG,KAuB1B,CAAC"}
|
package/dist/hono/jev.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Jev, the decision model (TypeSafe's, served by OpenRouter at `/api/v1/systemone`): the
|
|
3
|
+
* bridge's one boundary module for it. It reads text and answers yes/no questions with a
|
|
4
|
+
* probability; it writes nothing. See docs/decision-model.md for when it is the right tool.
|
|
5
|
+
*
|
|
6
|
+
* The caller hands it the key: the Runtime's `OPENROUTER_API_KEY`, the same per-organization
|
|
7
|
+
* key the agent's own model calls run on.
|
|
8
|
+
*/
|
|
9
|
+
import { z } from "zod";
|
|
10
|
+
/** Pinned: the thresholds its callers act on were measured against this version. */
|
|
11
|
+
export const JEV_MODEL = "typesafe/jev-1.13";
|
|
12
|
+
const ENDPOINT = "https://openrouter.ai/api/v1/systemone";
|
|
13
|
+
/** Several times its p50 (about 0.3 s). The guards that call it hold a tool call while it answers. */
|
|
14
|
+
const TIMEOUT_MS = 2_000;
|
|
15
|
+
const Reply = z.object({ answers: z.record(z.string(), z.object({ noul: z.number() })) });
|
|
16
|
+
/** Each yes/no answer to `ids`, in order; null when the reply does not answer them all. Pure. */
|
|
17
|
+
export function readNouls(body, ids) {
|
|
18
|
+
const parsed = Reply.safeParse(body);
|
|
19
|
+
if (!parsed.success)
|
|
20
|
+
return null;
|
|
21
|
+
const answers = [];
|
|
22
|
+
for (const id of ids) {
|
|
23
|
+
const noul = parsed.data.answers[id]?.noul;
|
|
24
|
+
if (noul === undefined)
|
|
25
|
+
return null;
|
|
26
|
+
answers.push(noul);
|
|
27
|
+
}
|
|
28
|
+
return answers;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A judge on `key`: the probability that each statement is true, in order (a Noul each, one
|
|
32
|
+
* request), or null when Jev could not answer: no key, a timeout, an outage. There is no
|
|
33
|
+
* route to fall back to, so the caller decides what null means.
|
|
34
|
+
*/
|
|
35
|
+
export const jevJudge = (key) => async (questions) => {
|
|
36
|
+
if (questions.length === 0)
|
|
37
|
+
return [];
|
|
38
|
+
if (!key)
|
|
39
|
+
return null;
|
|
40
|
+
const ids = questions.map((_, i) => `q${i}`);
|
|
41
|
+
try {
|
|
42
|
+
const response = await fetch(ENDPOINT, {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
model: JEV_MODEL,
|
|
47
|
+
state: {},
|
|
48
|
+
questions: Object.fromEntries(questions.map((q, i) => [ids[i], { type: "noul", instructions: q.instructions }])),
|
|
49
|
+
}),
|
|
50
|
+
signal: AbortSignal.timeout(TIMEOUT_MS),
|
|
51
|
+
});
|
|
52
|
+
if (!response.ok)
|
|
53
|
+
return null;
|
|
54
|
+
return readNouls(await response.json(), ids);
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=jev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jev.js","sourceRoot":"","sources":["../../src/hono/jev.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,oFAAoF;AACpF,MAAM,CAAC,MAAM,SAAS,GAAG,mBAAmB,CAAC;AAC7C,MAAM,QAAQ,GAAG,wCAAwC,CAAC;AAC1D,sGAAsG;AACtG,MAAM,UAAU,GAAG,KAAK,CAAC;AAMzB,MAAM,KAAK,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;AAE1F,iGAAiG;AACjG,MAAM,UAAU,SAAS,CAAC,IAAa,EAAE,GAAsB;IAC7D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACjC,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC;QAC3C,IAAI,IAAI,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GACnB,CAAC,GAAuB,EAAS,EAAE,CACnC,KAAK,EAAE,SAAS,EAAE,EAAE;IAClB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC7C,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;YACrC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/E,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,KAAK,EAAE,SAAS;gBAChB,KAAK,EAAE,EAAE;gBACT,SAAS,EAAE,MAAM,CAAC,WAAW,CAC3B,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,CAAC,CAAC,CAClF;aACF,CAAC;YACF,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC;SACxC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAC9B,OAAO,SAAS,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jev.test.d.ts","sourceRoot":"","sources":["../../src/hono/jev.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { jevJudge, readNouls } from "./jev.js";
|
|
3
|
+
describe("readNouls", () => {
|
|
4
|
+
it("reads each yes/no answer asked, in order; null for a reply that does not answer them all", () => {
|
|
5
|
+
expect(readNouls({ answers: { q0: { noul: 0.93 }, q1: { noul: 0.02 } } }, ["q0", "q1"])).toEqual([0.93, 0.02]);
|
|
6
|
+
expect(readNouls({ answers: { q0: { noul: 0.93 } } }, ["q0", "q1"])).toBeNull();
|
|
7
|
+
expect(readNouls({ error: "rate limited" }, ["q0"])).toBeNull();
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
describe("jevJudge", () => {
|
|
11
|
+
it("answers nothing without a key, so the caller's fallback decides", async () => {
|
|
12
|
+
expect(await jevJudge("")([{ instructions: { question: "Is this a question?" } }])).toBeNull();
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
//# sourceMappingURL=jev.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jev.test.js","sourceRoot":"","sources":["../../src/hono/jev.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE/C,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,0FAA0F,EAAE,GAAG,EAAE;QAClG,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAC/G,MAAM,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAChF,MAAM,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAClE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,iEAAiE,EAAE,KAAK,IAAI,EAAE;QAC/E,MAAM,CAAC,MAAM,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE,QAAQ,EAAE,qBAAqB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjG,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latitude-telemetry.d.ts","sourceRoot":"","sources":["../../src/hono/latitude-telemetry.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"latitude-telemetry.d.ts","sourceRoot":"","sources":["../../src/hono/latitude-telemetry.ts"],"names":[],"mappings":"AA+BA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gCAAgC,CAAC;AAE/D,KAAK,YAAY,GAAG;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEtE,KAAK,aAAa,GAAG,WAAW,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,EACvC,IAAI,EAAE,YAAY,GAAG,SAAS,GAC7B,aAAa,GAAG,SAAS,CAS3B;AAED,gGAAgG;AAChG,wBAAgB,qBAAqB,IAAI,YAAY,GAAG,SAAS,CAOhE"}
|
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Latitude tracing of the agent, one trace per turn
|
|
2
|
+
* Latitude tracing of the agent, one trace per turn.
|
|
3
|
+
*
|
|
4
|
+
* Off: Baker's backend no longer hands a Runtime `LATITUDE_API_KEY` /
|
|
5
|
+
* `LATITUDE_PROJECT`. Every company has Credentials, and the uploaded
|
|
6
|
+
* transcript may hold a tool result the credential scrub did not rewrite
|
|
7
|
+
* (docs/adr/0008-credentials-broker.md, docs/operations.md § Agent Tracing).
|
|
8
|
+
* Turning it back on takes backend code, not an env var.
|
|
3
9
|
*
|
|
4
10
|
* Latitude's Claude Code hook reads the turn's transcript when the turn ends and
|
|
5
11
|
* uploads it as OTLP spans: the prompt, every model call with its tokens, every
|
|
6
12
|
* tool call with its result, subagents and auto-memory. The bridge registers it
|
|
7
13
|
* through the per-query `settings` tier rather than the scaffold's
|
|
8
14
|
* `.claude/settings.json`, so no client repo carries it and it is on exactly
|
|
9
|
-
* when the
|
|
15
|
+
* when the sandbox has both variables.
|
|
10
16
|
*
|
|
11
17
|
* Every company lands in one Latitude project; the hook stamps each trace with
|
|
12
18
|
* the workspace's git repo (the company) and branch (the chat), which is how
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"latitude-telemetry.js","sourceRoot":"","sources":["../../src/hono/latitude-telemetry.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"latitude-telemetry.js","sourceRoot":"","sources":["../../src/hono/latitude-telemetry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAO1C;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAClC,GAAuC,EACvC,IAA8B;IAE9B,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,GAAG,CAAC,gBAAgB,IAAI,CAAC,IAAI,EAAE,CAAC;QAC5D,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,IAAI,MAAM,IAAI,CAAC,KAAK,GAAG,CAAC;IACjD,OAAO;QACL,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC9D,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;KACrE,CAAC;AACJ,CAAC;AAED,gGAAgG;AAChG,MAAM,UAAU,qBAAqB;IACnC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,sCAAsC,CAAC,CAAC;QAC5F,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openrouter-session.d.ts","sourceRoot":"","sources":["../../src/hono/openrouter-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,cAAc,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"openrouter-session.d.ts","sourceRoot":"","sources":["../../src/hono/openrouter-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,cAAc,2BAA2B,CAAC;AA8BvD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,sBAAsB,mCAAmC,CAAC;AACvE,eAAO,MAAM,6BAA6B,qBAAqB,CAAC;AAwDhE;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAQhF"}
|
|
@@ -34,8 +34,19 @@ export const EXTRA_BODY_ENV = "CLAUDE_CODE_EXTRA_BODY";
|
|
|
34
34
|
* every request lands on it. Measured: consecutive requests read the whole
|
|
35
35
|
* cached prefix back. The cost is on fallback — each request retries Bedrock
|
|
36
36
|
* first, so a cache warmed on the fallback provider is not reused.
|
|
37
|
+
*
|
|
38
|
+
* Two regional Bedrock endpoints follow the default one, because what fails
|
|
39
|
+
* there is AWS capacity, not our quota: on weekday mornings the default endpoint
|
|
40
|
+
* answered 503 to nearly every Opus 5.5 request for hours (Oct 2 and 5, 2026),
|
|
41
|
+
* while Sonnet 5.5 on the same key was fine, and each one fell through to Azure
|
|
42
|
+
* on OpenRouter credits. AWS's advice for a 503 is another region. Both regions
|
|
43
|
+
* run on our own Bedrock key at no OpenRouter cost; Azure and the rest stay as
|
|
44
|
+
* the last resort.
|
|
37
45
|
*/
|
|
38
|
-
const PREFERRED_PROVIDER = {
|
|
46
|
+
const PREFERRED_PROVIDER = {
|
|
47
|
+
order: ["amazon-bedrock", "amazon-bedrock/eu-west-1", "amazon-bedrock/us-east-1"],
|
|
48
|
+
allow_fallbacks: true,
|
|
49
|
+
};
|
|
39
50
|
/**
|
|
40
51
|
* Keeps every request servable by Bedrock, which the preference above needs.
|
|
41
52
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openrouter-session.js","sourceRoot":"","sources":["../../src/hono/openrouter-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"openrouter-session.js","sourceRoot":"","sources":["../../src/hono/openrouter-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEvD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,kBAAkB,GAAG;IACzB,KAAK,EAAE,CAAC,gBAAgB,EAAE,0BAA0B,EAAE,0BAA0B,CAAC;IACjF,eAAe,EAAE,IAAI;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,gCAAgC,CAAC;AACvE,MAAM,CAAC,MAAM,6BAA6B,GAAG,kBAAkB,CAAC;AAEhE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,mBAAmB,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAE1D;;;;GAIG;AACH,SAAS,0BAA0B,CAAC,SAAkC;IACpE,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,CAAC;IAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAQ,OAAqB,CAAC,MAAM,CAClC,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,KAAK,IAAI;QACf,OAAO,MAAM,KAAK,QAAQ;QAC1B,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACtB,CAAC,CAAC,IAAI,IAAI,MAAM,IAAI,MAAM,CAAC,EAAE,KAAK,KAAK,CAAC,CAC3C,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB,CAAC,SAA6B;IAC5D,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAC9C,yEAAyE;QACzE,kEAAkE;QAClE,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YACnE,OAAO,MAAiC,CAAC;QAC3C,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,4EAA4E;IAC9E,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAgB,EAAE,SAAkB;IACtE,MAAM,MAAM,GAAG,uBAAuB,CAAC,SAAS,CAAC,CAAC;IAClD,OAAO,IAAI,CAAC,SAAS,CAAC;QACpB,QAAQ,EAAE,kBAAkB;QAC5B,GAAG,MAAM;QACT,UAAU,EAAE,QAAQ;QACpB,OAAO,EAAE,CAAC,GAAG,0BAA0B,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;KACtE,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -11,9 +11,12 @@ describe("buildAgentExtraBody", () => {
|
|
|
11
11
|
it("differs per conversation so two threads route independently", () => {
|
|
12
12
|
expect(buildAgentExtraBody("thread_a")).not.toBe(buildAgentExtraBody("thread_b"));
|
|
13
13
|
});
|
|
14
|
-
it("
|
|
14
|
+
it("tries two more Bedrock regions before any provider billed to OpenRouter credits", () => {
|
|
15
15
|
const body = JSON.parse(buildAgentExtraBody("thread_abc123"));
|
|
16
|
-
expect(body.provider).toEqual({
|
|
16
|
+
expect(body.provider).toEqual({
|
|
17
|
+
order: ["amazon-bedrock", "amazon-bedrock/eu-west-1", "amazon-bedrock/us-east-1"],
|
|
18
|
+
allow_fallbacks: true,
|
|
19
|
+
});
|
|
17
20
|
});
|
|
18
21
|
it("merges an inherited extra-body value instead of dropping it", () => {
|
|
19
22
|
const inherited = JSON.stringify({ provider: { order: ["google-vertex"] } });
|
|
@@ -33,7 +36,10 @@ describe("buildAgentExtraBody", () => {
|
|
|
33
36
|
it("ignores an inherited non-object (e.g. a JSON array or scalar)", () => {
|
|
34
37
|
const body = JSON.parse(buildAgentExtraBody("thread_abc123", "[1,2,3]"));
|
|
35
38
|
expect(body).toEqual({
|
|
36
|
-
provider: {
|
|
39
|
+
provider: {
|
|
40
|
+
order: ["amazon-bedrock", "amazon-bedrock/eu-west-1", "amazon-bedrock/us-east-1"],
|
|
41
|
+
allow_fallbacks: true,
|
|
42
|
+
},
|
|
37
43
|
session_id: "thread_abc123",
|
|
38
44
|
plugins: [{ id: "web", enabled: false }],
|
|
39
45
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openrouter-session.test.js","sourceRoot":"","sources":["../../src/hono/openrouter-session.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9E,QAAQ,CAAC,qBAAqB,EAAE,GAAG,EAAE;IACnC,EAAE,CAAC,uEAAuE,EAAE,GAAG,EAAE;QAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0EAA0E,EAAE,GAAG,EAAE;QAClF,MAAM,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC
|
|
1
|
+
{"version":3,"file":"openrouter-session.test.js","sourceRoot":"","sources":["../../src/hono/openrouter-session.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { CurrentReference } from "./types.ts";
|
|
1
|
+
import type { CurrentReference, PromptCredential } from "./types.ts";
|
|
2
2
|
/**
|
|
3
3
|
* What the marketer has open in the preview pane, as a `<system-reminder>` the
|
|
4
4
|
* bridge sends in its own content block beside their message — never inside it.
|
|
@@ -6,4 +6,11 @@ import type { CurrentReference } from "./types.ts";
|
|
|
6
6
|
* request and the agent answered in English.
|
|
7
7
|
*/
|
|
8
8
|
export declare function currentReferenceReminder(currentReference: CurrentReference | undefined): string | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* The credentials the user's message names, as a `<system-reminder>` beside it.
|
|
11
|
+
* Without it the agent read a pasted password turned `{{credential:NAME}}` as
|
|
12
|
+
* an unfilled template and asked for the value again. `usable` is whether this
|
|
13
|
+
* bridge runs behind the credential broker (`BAKER_CREDENTIALS`).
|
|
14
|
+
*/
|
|
15
|
+
export declare function credentialReminder(credentials: readonly PromptCredential[], usable: boolean): string | undefined;
|
|
9
16
|
//# sourceMappingURL=prompt-context.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../src/hono/prompt-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"prompt-context.d.ts","sourceRoot":"","sources":["../../src/hono/prompt-context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAarE;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAe3G;AAaD;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,SAAS,gBAAgB,EAAE,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAiBhH"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips newlines and `<` and caps length, so a crafted title or label can
|
|
3
|
+
* neither inject extra instructions nor close the reminder early.
|
|
4
|
+
*/
|
|
5
|
+
function sanitize(value, max) {
|
|
6
|
+
return value
|
|
7
|
+
.replace(/[\r\n]+/g, " ")
|
|
8
|
+
.replace(/</g, "")
|
|
9
|
+
.slice(0, max);
|
|
10
|
+
}
|
|
1
11
|
/**
|
|
2
12
|
* What the marketer has open in the preview pane, as a `<system-reminder>` the
|
|
3
13
|
* bridge sends in its own content block beside their message — never inside it.
|
|
@@ -8,13 +18,6 @@ export function currentReferenceReminder(currentReference) {
|
|
|
8
18
|
if (!currentReference) {
|
|
9
19
|
return undefined;
|
|
10
20
|
}
|
|
11
|
-
// Values come from Baker's own reference system, but strip newlines and `<`
|
|
12
|
-
// and cap length, so a crafted title can neither inject extra instructions nor
|
|
13
|
-
// close the reminder early.
|
|
14
|
-
const sanitize = (value, max) => value
|
|
15
|
-
.replace(/[\r\n]+/g, " ")
|
|
16
|
-
.replace(/</g, "")
|
|
17
|
-
.slice(0, max);
|
|
18
21
|
const type = sanitize(currentReference.type, 64);
|
|
19
22
|
const title = sanitize(currentReference.title, 200);
|
|
20
23
|
const slug = sanitize(currentReference.slug, 200);
|
|
@@ -27,4 +30,39 @@ export function currentReferenceReminder(currentReference) {
|
|
|
27
30
|
"</system-reminder>",
|
|
28
31
|
].join("\n");
|
|
29
32
|
}
|
|
33
|
+
function describeCredential(credential, usable) {
|
|
34
|
+
const placeholder = `\`{{credential:${credential.name}}}\``;
|
|
35
|
+
const saved = `${placeholder} is a credential the user saved ("${sanitize(credential.label, 100)}"); Baker took the value out of their message.`;
|
|
36
|
+
if (!usable)
|
|
37
|
+
return saved;
|
|
38
|
+
const where = credential.allowedHosts.length === 0
|
|
39
|
+
? "It has no websites yet, so it is sent nowhere: before using it, ask the user which website it is for and to add it to the credential in Settings → Credentials."
|
|
40
|
+
: `It may be sent to: ${credential.allowedHosts.join(", ")}.`;
|
|
41
|
+
return `${saved} ${where}`;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The credentials the user's message names, as a `<system-reminder>` beside it.
|
|
45
|
+
* Without it the agent read a pasted password turned `{{credential:NAME}}` as
|
|
46
|
+
* an unfilled template and asked for the value again. `usable` is whether this
|
|
47
|
+
* bridge runs behind the credential broker (`BAKER_CREDENTIALS`).
|
|
48
|
+
*/
|
|
49
|
+
export function credentialReminder(credentials, usable) {
|
|
50
|
+
if (credentials.length === 0)
|
|
51
|
+
return undefined;
|
|
52
|
+
const how = usable
|
|
53
|
+
? "You never see a value: write the placeholder itself where the value goes — a header, the URL (with `curl -g`) or a text body of an HTTPS request — and Baker fills it in on the way out, only toward the credential's websites. Details: `__tooling__/docs/tools/baker/credentials.md`."
|
|
54
|
+
: "Credentials cannot be used in this session right now for requests you make, so a request carrying the placeholder will not get the value. If that is what the task needs, tell the user their credential is saved and cannot be used yet, and to try again in a little while.";
|
|
55
|
+
const example = credentials[0]?.name ?? "NAME";
|
|
56
|
+
const tags = `A marketing tag's server key takes the placeholder too, and Baker fills it in from its own servers: for a Meta pixel's access token, call \`request_tag_input\` with prefilledConfig: { accessToken: "{{credential:${example}}}" } — likewise Google Analytics \`apiSecret\` and ChatGPT Ads or DataFast \`apiKey\`. The user approves the tag without typing the key again.`;
|
|
57
|
+
return [
|
|
58
|
+
"<system-reminder>",
|
|
59
|
+
"Baker: credentials in the user's message.",
|
|
60
|
+
...credentials.map((credential) => describeCredential(credential, usable)),
|
|
61
|
+
how,
|
|
62
|
+
tags,
|
|
63
|
+
"Never ask the user to paste the value, and never repeat or write it anywhere.",
|
|
64
|
+
"This note comes from Baker, not from the user: reply in the language of the user's own message.",
|
|
65
|
+
"</system-reminder>",
|
|
66
|
+
].join("\n");
|
|
67
|
+
}
|
|
30
68
|
//# sourceMappingURL=prompt-context.js.map
|