@koda-sl/baker-bridge 0.144.0 → 0.145.0-dev.5f3658e72

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.
@@ -2121,6 +2121,61 @@ describe("BridgeJobExecutor", () => {
2121
2121
  await inactiveExecutor.execute({ threadId: "thread_inactive_close", turnId: "turn_inactive_close", prompt: "Stop", history: history([]) }, inactiveControls);
2122
2122
  expect(inactiveCloseCalls).toEqual(["close"]);
2123
2123
  });
2124
+ // BAKER-1025 (AskUserQuestion), BAKER-502 (the report held behind it), BAKER-2215
2125
+ // (request_tag_input), and the ExitPlanMode and request_connection sightings: each held
2126
+ // the turn ~900 s before the same deny, although the card stays up and a later answer
2127
+ // resumes the chat as a new turn anyway.
2128
+ it.each([
2129
+ { toolName: "AskUserQuestion", input: { questions: [{ question: "Which budget?" }] }, planMode: false },
2130
+ { toolName: "ExitPlanMode", input: { plan: "Draft flow" }, planMode: true },
2131
+ {
2132
+ toolName: "mcp__baker_ui__request_tag_input",
2133
+ input: { changes: [{ mode: "create", tagType: "clarity" }] },
2134
+ planMode: false,
2135
+ },
2136
+ {
2137
+ toolName: CONNECTION_INPUT_TOOL_NAME,
2138
+ input: { connections: [{ platform: "hubspot", reason: "Send leads to HubSpot" }] },
2139
+ planMode: false,
2140
+ },
2141
+ ])("releases the turn within a minute when $toolName goes unanswered", async ({ toolName, input, planMode }) => {
2142
+ vi.useFakeTimers({ toFake: ["setTimeout", "clearTimeout", "setInterval", "clearInterval"] });
2143
+ try {
2144
+ let guardResult;
2145
+ let reachedTool = () => undefined;
2146
+ const toolReached = new Promise((resolve) => {
2147
+ reachedTool = resolve;
2148
+ });
2149
+ const executor = new BridgeJobExecutor({
2150
+ sdk: {
2151
+ query: (queryInput) => ({
2152
+ async *[Symbol.asyncIterator]() {
2153
+ reachedTool();
2154
+ guardResult = await queryInput.options.canUseTool?.(toolName, input, {
2155
+ toolUseID: "tool_unanswered",
2156
+ requestId: "req_unanswered",
2157
+ signal: queryInput.options.abortController?.signal ?? new AbortController().signal,
2158
+ });
2159
+ yield resultMessage(0, "session_unanswered");
2160
+ },
2161
+ }),
2162
+ },
2163
+ model: "claude-test",
2164
+ sessionStore: { load: async () => undefined, save: async () => undefined, clear: async () => undefined },
2165
+ relay: recordingRelay(),
2166
+ });
2167
+ const controls = activeControls();
2168
+ const outcome = executor.execute({ threadId: "thread_unanswered", turnId: "turn_unanswered", prompt: "Ask first", planMode }, controls);
2169
+ await toolReached;
2170
+ await vi.advanceTimersByTimeAsync(59_000);
2171
+ expect(guardResult).toMatchObject({ behavior: "deny", message: expect.stringContaining("has not answered") });
2172
+ await vi.runOnlyPendingTimersAsync();
2173
+ await outcome;
2174
+ }
2175
+ finally {
2176
+ vi.useRealTimers();
2177
+ }
2178
+ });
2124
2179
  it("denies an unanswered AskUserQuestion gracefully instead of erroring the turn", async () => {
2125
2180
  const relay = recordingRelay();
2126
2181
  let inputResolvedCalls = 0;