@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
package/dist/hono/bridge-job.js
CHANGED
|
@@ -13,13 +13,16 @@ import { AVATAR_CASTING_TOOL_NAME, normalizeAvatarCastingToolInput, } from "./av
|
|
|
13
13
|
import { asksForAvatarFace, FACE_QUESTION_MESSAGE } from "./avatar-face-question.js";
|
|
14
14
|
import { CONNECTION_INPUT_TOOL_NAME, CONNECTION_PLATFORMS, connectedButNotLoaded, normalizeConnectionInputToolInput, toolsConnected, } from "./connection-input.js";
|
|
15
15
|
import { messageContextTokens, pickContextWindow, streamEventContextUsage, turnContextUsage, } from "./context-usage.js";
|
|
16
|
+
import { CREDENTIAL_INPUT_TOOL_NAME, normalizeCredentialInputToolInput, } from "./credential-input.js";
|
|
17
|
+
import { credentialScrubHooks } from "./credential-scrub.js";
|
|
16
18
|
import { FLOW_INPUT_TOOL_NAME, nodeTargetRefusals, normalizeFlowInputToolInput, readRawFlowTree, sanitizeFlowRequests, } from "./flow-input.js";
|
|
17
19
|
import { writesInventedLogo } from "./invented-logo.js";
|
|
20
|
+
import { jevJudge } from "./jev.js";
|
|
18
21
|
import { installedLatitudeHook, latitudeTelemetryFor } from "./latitude-telemetry.js";
|
|
19
22
|
import { composioScopesToInvalidate, failedCustomServers, reportMcpInvalid, serversConnectedWithNoTools, } from "./mcp-servers.js";
|
|
20
23
|
import { agentModelOverride, autoCompactWindowForModel, betasForModel, contextWindowForModel, } from "./model-capabilities.js";
|
|
21
24
|
import { BEDROCK_SERVABLE_CAPABILITIES, buildAgentExtraBody, EXTRA_BODY_ENV, MODEL_CAPABILITIES_ENV, } from "./openrouter-session.js";
|
|
22
|
-
import { currentReferenceReminder } from "./prompt-context.js";
|
|
25
|
+
import { credentialReminder, currentReferenceReminder } from "./prompt-context.js";
|
|
23
26
|
import { bridgeRuntimeFiles } from "./runtime-files.js";
|
|
24
27
|
import { applyCurrentSenderAttribution, buildCleanBoundary, buildRehydratedPrompt, compactionRetryAttempt, emptyInProgressSession, isContextOverflowError, isResumeSessionError, observeInProgressSession, prepareSessionAttempt, stallContinuationAttempt, } from "./session-continuity.js";
|
|
25
28
|
import { readSessionSkills } from "./session-skills.js";
|
|
@@ -220,6 +223,16 @@ export function reviewQueryOptions(job) {
|
|
|
220
223
|
? { outputFormat: { type: "json_schema", schema: job.review.outputSchema }, hooks: reviewOutputHooks() }
|
|
221
224
|
: {};
|
|
222
225
|
}
|
|
226
|
+
/**
|
|
227
|
+
* Every job's tool results pass through the credential scrub, appended after
|
|
228
|
+
* any other PostToolUse hook so its rewrite is the one the SDK keeps.
|
|
229
|
+
*/
|
|
230
|
+
function withCredentialScrub(options) {
|
|
231
|
+
const scrub = credentialScrubHooks();
|
|
232
|
+
const hooks = { ...(options.hooks ?? {}) };
|
|
233
|
+
hooks.PostToolUse = [...(hooks.PostToolUse ?? []), ...(scrub.PostToolUse ?? [])];
|
|
234
|
+
return { ...options, hooks };
|
|
235
|
+
}
|
|
223
236
|
/**
|
|
224
237
|
* The review runs on Baker's own OpenRouter credential and its own read-only
|
|
225
238
|
* key, both replacing whatever the Runtime was started with — the client never
|
|
@@ -246,6 +259,12 @@ export const REVIEW_DEBUG_LOG = "/tmp/baker-review-cli.jsonl";
|
|
|
246
259
|
// briefly; model activity cancels it (the CLI clearly isn't idle). Turns that
|
|
247
260
|
// never ran a task skip the delay entirely.
|
|
248
261
|
const DEFAULT_TURN_END_SETTLE_MS = 1_000;
|
|
262
|
+
// When the last task settles after the clean result, the CLI wakes the model on
|
|
263
|
+
// it and streams nothing until the woken turn's first token — seconds, and 30+
|
|
264
|
+
// on a slow route. Ending input inside that silence strands the woken turn on a
|
|
265
|
+
// closed channel, and the CLI's exit at its result kills anything it backgrounds
|
|
266
|
+
// (dev, Oct 2026: a render). A settle that wakes nobody waits this long once.
|
|
267
|
+
const DEFAULT_TURN_END_WAKE_MS = 45_000;
|
|
249
268
|
// After input ends, a healthy idle CLI exits within seconds. If the stream is
|
|
250
269
|
// still open past this window with no messages arriving, the CLI is wedged —
|
|
251
270
|
// close the query so the turn can finish instead of hanging to the watchdog.
|
|
@@ -273,10 +292,10 @@ const UNATTENDED_PROMPT_MESSAGE = "This run is unattended — it was started by
|
|
|
273
292
|
* asked for in a text prompt renders a question nobody can answer. In each case the user
|
|
274
293
|
* is left waiting for something that was never going to come.
|
|
275
294
|
*/
|
|
276
|
-
function refuseMalformedCall(toolName, input, userText, priorUserText) {
|
|
295
|
+
async function refuseMalformedCall(toolName, input, judge, userText, priorUserText) {
|
|
277
296
|
if (toolName === "Bash") {
|
|
278
297
|
const command = typeof input?.command === "string" ? input.command : undefined;
|
|
279
|
-
return toolRunAsCommand(command) ?? accentContradictsUser(command, userText, priorUserText);
|
|
298
|
+
return toolRunAsCommand(command) ?? (await accentContradictsUser(command, userText, priorUserText, judge));
|
|
280
299
|
}
|
|
281
300
|
if (toolName === "Agent" || toolName === "Task") {
|
|
282
301
|
return blockingToolDelegated(typeof input?.prompt === "string" ? input.prompt : undefined);
|
|
@@ -284,11 +303,18 @@ function refuseMalformedCall(toolName, input, userText, priorUserText) {
|
|
|
284
303
|
if (toolName === "Write" || toolName === "Edit" || toolName === "MultiEdit") {
|
|
285
304
|
return writesInventedLogo(typeof input?.file_path === "string" ? input.file_path : undefined);
|
|
286
305
|
}
|
|
287
|
-
if (toolName === "AskUserQuestion" && asksForAvatarFace(input)) {
|
|
306
|
+
if (toolName === "AskUserQuestion" && (await asksForAvatarFace(input, judge))) {
|
|
288
307
|
return FACE_QUESTION_MESSAGE;
|
|
289
308
|
}
|
|
290
309
|
return null;
|
|
291
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Whether this Runtime has Credentials: the backend sets `BAKER_CREDENTIALS=1`
|
|
313
|
+
* alongside the credential broker's proxy env. Read per turn, not at import.
|
|
314
|
+
*/
|
|
315
|
+
function credentialsEnabled() {
|
|
316
|
+
return process.env.BAKER_CREDENTIALS === "1";
|
|
317
|
+
}
|
|
292
318
|
/** Blocking prompts: every tool that would leave a card the backend parks on. */
|
|
293
319
|
function deniedUnattendedPrompt(toolName) {
|
|
294
320
|
return (toolName === "AskUserQuestion" ||
|
|
@@ -296,6 +322,7 @@ function deniedUnattendedPrompt(toolName) {
|
|
|
296
322
|
toolName === TAG_INPUT_TOOL_NAME ||
|
|
297
323
|
toolName === FLOW_INPUT_TOOL_NAME ||
|
|
298
324
|
toolName === CONNECTION_INPUT_TOOL_NAME ||
|
|
325
|
+
toolName === CREDENTIAL_INPUT_TOOL_NAME ||
|
|
299
326
|
toolName === AVATAR_CASTING_TOOL_NAME);
|
|
300
327
|
}
|
|
301
328
|
export class StreamStallError extends Error {
|
|
@@ -817,7 +844,7 @@ beforeEndInput) {
|
|
|
817
844
|
}, timings.exitGuardMs);
|
|
818
845
|
exitGuard.unref?.();
|
|
819
846
|
};
|
|
820
|
-
const scheduleEnd = () => {
|
|
847
|
+
const scheduleEnd = (delayMs = timings.settleMs) => {
|
|
821
848
|
if (settleTimer) {
|
|
822
849
|
return;
|
|
823
850
|
}
|
|
@@ -829,7 +856,7 @@ beforeEndInput) {
|
|
|
829
856
|
endInput();
|
|
830
857
|
return;
|
|
831
858
|
}
|
|
832
|
-
settleTimer = setTimeout(endInput,
|
|
859
|
+
settleTimer = setTimeout(endInput, delayMs);
|
|
833
860
|
settleTimer.unref?.();
|
|
834
861
|
};
|
|
835
862
|
return {
|
|
@@ -895,7 +922,7 @@ beforeEndInput) {
|
|
|
895
922
|
// second result) would strand the turn until the watchdog. An emptied
|
|
896
923
|
// background_tasks_changed level counts as a settle the same way.
|
|
897
924
|
if (lastWasTaskLifecycle && cleanResultSeen && !hasInFlightWork()) {
|
|
898
|
-
scheduleEnd();
|
|
925
|
+
scheduleEnd(timings.wakeMs);
|
|
899
926
|
}
|
|
900
927
|
},
|
|
901
928
|
hasInFlightWork,
|
|
@@ -988,12 +1015,14 @@ export class BridgeJobExecutor {
|
|
|
988
1015
|
stallCheckIntervalMs;
|
|
989
1016
|
recordTurnEvent;
|
|
990
1017
|
turnEndSettleMs;
|
|
1018
|
+
turnEndWakeMs;
|
|
991
1019
|
turnEndExitGuardMs;
|
|
992
1020
|
now;
|
|
993
1021
|
readUsageBaseline;
|
|
994
1022
|
stalledCliExitMs;
|
|
995
1023
|
readFlowTree;
|
|
996
1024
|
settleWorkspace;
|
|
1025
|
+
judge;
|
|
997
1026
|
constructor(dependencies) {
|
|
998
1027
|
this.sdk = dependencies.sdk;
|
|
999
1028
|
this.relay = dependencies.relay;
|
|
@@ -1006,6 +1035,7 @@ export class BridgeJobExecutor {
|
|
|
1006
1035
|
this.stallCheckIntervalMs = dependencies.stallCheckIntervalMs ?? DEFAULT_STALL_CHECK_INTERVAL_MS;
|
|
1007
1036
|
this.recordTurnEvent = dependencies.recordTurnEvent ?? recordTurnEvent;
|
|
1008
1037
|
this.turnEndSettleMs = dependencies.turnEndSettleMs ?? DEFAULT_TURN_END_SETTLE_MS;
|
|
1038
|
+
this.turnEndWakeMs = dependencies.turnEndWakeMs ?? DEFAULT_TURN_END_WAKE_MS;
|
|
1009
1039
|
this.turnEndExitGuardMs = dependencies.turnEndExitGuardMs ?? DEFAULT_TURN_END_EXIT_GUARD_MS;
|
|
1010
1040
|
this.now = dependencies.now ?? Date.now;
|
|
1011
1041
|
this.readUsageBaseline =
|
|
@@ -1015,6 +1045,7 @@ export class BridgeJobExecutor {
|
|
|
1015
1045
|
this.settleWorkspace = dependencies.settleWorkspace ?? (() => settleWorkspaceAfterTasks());
|
|
1016
1046
|
this.readFlowTree =
|
|
1017
1047
|
dependencies.readFlowTree ?? ((flowSlug) => readRawFlowTree(bridgeRuntimeFiles.flowsDir(), flowSlug));
|
|
1048
|
+
this.judge = dependencies.judge ?? jevJudge(process.env.OPENROUTER_API_KEY);
|
|
1018
1049
|
}
|
|
1019
1050
|
async execute(job, controls) {
|
|
1020
1051
|
const state = createBridgeJobState();
|
|
@@ -1087,7 +1118,11 @@ export class BridgeJobExecutor {
|
|
|
1087
1118
|
const verbatim = verbatimSlashCommand(job.prompt) ??
|
|
1088
1119
|
leadingSkillCommand(job.prompt, (await readSessionSkills(bridgeRuntimeFiles.projectRoot())) ?? []);
|
|
1089
1120
|
let requestPrompt = verbatim ?? applyCurrentSenderAttribution(job.prompt, job.currentSender);
|
|
1090
|
-
const
|
|
1121
|
+
const reminders = [
|
|
1122
|
+
verbatim === null ? currentReferenceReminder(job.currentReference) : undefined,
|
|
1123
|
+
credentialReminder(job.credentials ?? [], credentialsEnabled()),
|
|
1124
|
+
].filter((text) => text !== undefined);
|
|
1125
|
+
const reminder = reminders.length > 0 ? reminders.join("\n\n") : undefined;
|
|
1091
1126
|
if (job.attachments && job.attachments.length > 0) {
|
|
1092
1127
|
requestPrompt += await this.prepareAttachments(job.attachments, "message");
|
|
1093
1128
|
}
|
|
@@ -1364,7 +1399,7 @@ export class BridgeJobExecutor {
|
|
|
1364
1399
|
if (!servers) {
|
|
1365
1400
|
return notReady;
|
|
1366
1401
|
}
|
|
1367
|
-
const result = await settleWithin(query.setMcpServers({ ...servers, baker_ui: createBakerUiServer() }), MCP_RELOAD_TIMEOUT_MS);
|
|
1402
|
+
const result = await settleWithin(query.setMcpServers({ ...servers, baker_ui: createBakerUiServer({ credentials: credentialsEnabled() }) }), MCP_RELOAD_TIMEOUT_MS);
|
|
1368
1403
|
const failed = Object.keys(result?.errors ?? {});
|
|
1369
1404
|
if (failed.length > 0) {
|
|
1370
1405
|
console.error(`[mcp] reload after a connection could not start ${failed.join(", ")}`);
|
|
@@ -1396,7 +1431,7 @@ export class BridgeJobExecutor {
|
|
|
1396
1431
|
* handlers — except on an unattended turn, where there is nobody to answer and
|
|
1397
1432
|
* every one of them is refused up front (see `UNATTENDED_PROMPT_MESSAGE`).
|
|
1398
1433
|
*/
|
|
1399
|
-
routeToolUse(job, controls, toolName, input, opts) {
|
|
1434
|
+
async routeToolUse(job, controls, toolName, input, opts) {
|
|
1400
1435
|
const refusedShell = reviewShellRefusal(job, toolName, input);
|
|
1401
1436
|
if (refusedShell) {
|
|
1402
1437
|
return Promise.resolve({ behavior: "deny", message: refusedShell });
|
|
@@ -1412,9 +1447,9 @@ export class BridgeJobExecutor {
|
|
|
1412
1447
|
});
|
|
1413
1448
|
return Promise.resolve({ behavior: "deny", message: UNATTENDED_PROMPT_MESSAGE });
|
|
1414
1449
|
}
|
|
1415
|
-
const malformed = refuseMalformedCall(toolName, input, job.prompt, userTurnsBefore(job));
|
|
1450
|
+
const malformed = await refuseMalformedCall(toolName, input, this.judge, job.prompt, userTurnsBefore(job));
|
|
1416
1451
|
if (malformed)
|
|
1417
|
-
return
|
|
1452
|
+
return { behavior: "deny", message: malformed };
|
|
1418
1453
|
// Cast what is owed FIRST, whatever comes next — including another create. Checked
|
|
1419
1454
|
// the other way round, a second create (the agent retrying after a rejected flag,
|
|
1420
1455
|
// which is what happens in practice) would overwrite the handle and the first
|
|
@@ -1467,6 +1502,15 @@ export class BridgeJobExecutor {
|
|
|
1467
1502
|
if (toolName === CONNECTION_INPUT_TOOL_NAME) {
|
|
1468
1503
|
return this.handleConnectionInputRequest(job, controls, input, opts.toolUseID, opts.signal);
|
|
1469
1504
|
}
|
|
1505
|
+
if (toolName === CREDENTIAL_INPUT_TOOL_NAME) {
|
|
1506
|
+
if (!credentialsEnabled()) {
|
|
1507
|
+
return Promise.resolve({
|
|
1508
|
+
behavior: "deny",
|
|
1509
|
+
message: "Saved credentials are not available in this workspace. Ask the user to share what you need another way, and never ask them to paste a password or key into the chat.",
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1512
|
+
return this.handleCredentialInputRequest(job, controls, input, opts.toolUseID, opts.signal);
|
|
1513
|
+
}
|
|
1470
1514
|
if (toolName === AVATAR_CASTING_TOOL_NAME) {
|
|
1471
1515
|
return this.handleAvatarCastingRequest(job, controls, input, opts.toolUseID, opts.signal);
|
|
1472
1516
|
}
|
|
@@ -1537,7 +1581,7 @@ export class BridgeJobExecutor {
|
|
|
1537
1581
|
settingSources: ["project"],
|
|
1538
1582
|
// `baker_ui` hosts in-process interactive dashboard tools (secure tag
|
|
1539
1583
|
// form). Always present; HTTP (Composio) servers merge alongside it.
|
|
1540
|
-
mcpServers: { ...(mcpServers ?? {}), baker_ui: createBakerUiServer() },
|
|
1584
|
+
mcpServers: { ...(mcpServers ?? {}), baker_ui: createBakerUiServer({ credentials: credentialsEnabled() }) },
|
|
1541
1585
|
// Only send the key when there is something in it: the SDK forwards
|
|
1542
1586
|
// `settings` to the CLI whenever it is present, so an empty object still
|
|
1543
1587
|
// becomes a `--settings {}` flag tier for the CLI to parse.
|
|
@@ -1550,7 +1594,7 @@ export class BridgeJobExecutor {
|
|
|
1550
1594
|
// the workspace could not be read; omit rather than allowlist nothing.
|
|
1551
1595
|
...(skills ? { skills } : {}),
|
|
1552
1596
|
disallowedTools: disallowedToolsFor(job),
|
|
1553
|
-
...reviewQueryOptions(job),
|
|
1597
|
+
...withCredentialScrub(reviewQueryOptions(job)),
|
|
1554
1598
|
...(job.planMode ? { permissionMode: "plan" } : {}),
|
|
1555
1599
|
canUseTool: (toolName, input, opts) => this.routeToolUse(job, controls, toolName, input, opts),
|
|
1556
1600
|
// No `fallbackModel`, and no server-side refusal `fallbacks` in the extra
|
|
@@ -1575,6 +1619,10 @@ export class BridgeJobExecutor {
|
|
|
1575
1619
|
// system-reminder repeatedly pushed the agent toward that ephemeral
|
|
1576
1620
|
// backlog instead of the product's own Tasks (`baker actions`).
|
|
1577
1621
|
CLAUDE_CODE_ENABLE_TASKS: "0",
|
|
1622
|
+
// The CLI clamps any Bash timeout to 10 minutes unless told otherwise;
|
|
1623
|
+
// the workspace's `inject-timeouts` hook gives a render up to 30, and at
|
|
1624
|
+
// the clamp the render was moved to the background mid-repair.
|
|
1625
|
+
BASH_MAX_TIMEOUT_MS: String(30 * 60_000),
|
|
1578
1626
|
// `citty` writes ANSI escapes into `baker --help` unconditionally —
|
|
1579
1627
|
// they are never TTY-gated, so every help read the agent does inside
|
|
1580
1628
|
// the sandbox pays ~5-10% of its tokens on colour codes it can't see.
|
|
@@ -1649,7 +1697,7 @@ export class BridgeJobExecutor {
|
|
|
1649
1697
|
// A reading that answers after the query drained (past its timeout) belongs
|
|
1650
1698
|
// to a query already folded into the turn — and a retry reuses `state`.
|
|
1651
1699
|
let drained = false;
|
|
1652
|
-
const turnEnd = createTurnEndController(job, query, closeQuery, { settleMs: this.turnEndSettleMs, exitGuardMs: this.turnEndExitGuardMs }, this.recordTurnEvent, async () => {
|
|
1700
|
+
const turnEnd = createTurnEndController(job, query, closeQuery, { settleMs: this.turnEndSettleMs, wakeMs: this.turnEndWakeMs, exitGuardMs: this.turnEndExitGuardMs }, this.recordTurnEvent, async () => {
|
|
1653
1701
|
const reading = await query.usage?.();
|
|
1654
1702
|
if (reading && !drained) {
|
|
1655
1703
|
settledCostUsd = recordCumulativeUsage(state, reading);
|
|
@@ -1757,6 +1805,7 @@ export class BridgeJobExecutor {
|
|
|
1757
1805
|
controls.pendingTagInputs.size > 0 ||
|
|
1758
1806
|
controls.pendingFlowInputs.size > 0 ||
|
|
1759
1807
|
controls.pendingConnectionInputs.size > 0 ||
|
|
1808
|
+
controls.pendingCredentialInputs.size > 0 ||
|
|
1760
1809
|
controls.pendingAvatarCastings.size > 0,
|
|
1761
1810
|
onStall: () => {
|
|
1762
1811
|
console.error(`[BridgeJob] no stream data for ${this.stallTimeoutMs}ms while awaiting the model — closing query for retry (thread ${job.threadId})`);
|
|
@@ -2265,6 +2314,36 @@ export class BridgeJobExecutor {
|
|
|
2265
2314
|
: decision.value;
|
|
2266
2315
|
return { behavior: "allow", updatedInput: { connections, resolution } };
|
|
2267
2316
|
}
|
|
2317
|
+
async handleCredentialInputRequest(job, controls, input, toolUseId, signal) {
|
|
2318
|
+
const target = { threadId: job.threadId, turnId: job.turnId };
|
|
2319
|
+
const request = normalizeCredentialInputToolInput(input);
|
|
2320
|
+
if (!request) {
|
|
2321
|
+
return {
|
|
2322
|
+
behavior: "deny",
|
|
2323
|
+
message: "request_credential requires { label, reason, allowedHosts: [...] } (and optionally a lowercase `name`). Retry with that shape.",
|
|
2324
|
+
};
|
|
2325
|
+
}
|
|
2326
|
+
const posted = await this.relay.credentialInputRequest(target, { toolUseId, ...request });
|
|
2327
|
+
if (!posted) {
|
|
2328
|
+
console.error(`[request_credential] failed to post credential-input-request for ${toolUseId} — denying`);
|
|
2329
|
+
return {
|
|
2330
|
+
behavior: "deny",
|
|
2331
|
+
message: "The credential request could not be delivered to the dashboard — retry request_credential in a moment.",
|
|
2332
|
+
};
|
|
2333
|
+
}
|
|
2334
|
+
const decision = await this.awaitPromptDecision({
|
|
2335
|
+
label: "request_credential",
|
|
2336
|
+
signal,
|
|
2337
|
+
controls,
|
|
2338
|
+
pending: controls.pendingCredentialInputs,
|
|
2339
|
+
toolUseId,
|
|
2340
|
+
});
|
|
2341
|
+
if (decision.kind === "unanswered") {
|
|
2342
|
+
return { behavior: "deny", message: PROMPT_UNANSWERED_MESSAGE };
|
|
2343
|
+
}
|
|
2344
|
+
void this.relay.credentialInputResolved(target);
|
|
2345
|
+
return { behavior: "allow", updatedInput: { ...request, resolution: decision.value } };
|
|
2346
|
+
}
|
|
2268
2347
|
async handlePlanReview(job, controls, input, toolUseId, signal) {
|
|
2269
2348
|
const target = { threadId: job.threadId, turnId: job.turnId };
|
|
2270
2349
|
controls.markEvent?.();
|