@flink-app/flink 2.0.0-alpha.95 → 2.0.0-alpha.96

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 CHANGED
@@ -1,5 +1,13 @@
1
1
  # @flink-app/flink
2
2
 
3
+ ## 2.0.0-alpha.96
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix double-JSON-encoding of tool results on conversation reload.
8
+
9
+ `ConversationAgent.convertToAgentMessages` was re-stringifying tool result payloads that `ToolExecutor.formatResultForAI` had already serialized to a JSON string. Each reload escaped the value one more level, corrupting tool history on every continuation turn (visible through any LLM adapter, including Anthropic). Strings are now passed through untouched; non-string values are still stringified for backward compatibility with older storage rows.
10
+
3
11
  ## 2.0.0-alpha.95
4
12
 
5
13
  ## 2.0.0-alpha.94
@@ -297,7 +297,9 @@ var ConversationAgent = /** @class */ (function (_super) {
297
297
  role: "tool",
298
298
  toolCallId: toolResult.id,
299
299
  toolName: "", // Not stored, but required by type
300
- result: JSON.stringify(toolResult.result),
300
+ // Tool results are persisted as already-stringified payloads (see formatResultForAI).
301
+ // JSON.stringify-ing again would double-encode strings into escaped JSON literals.
302
+ result: typeof toolResult.result === "string" ? toolResult.result : JSON.stringify(toolResult.result),
301
303
  }); });
302
304
  }
303
305
  else if (msg.role === "user") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/flink",
3
- "version": "2.0.0-alpha.95",
3
+ "version": "2.0.0-alpha.96",
4
4
  "description": "Typescript only framework for creating REST-like APIs on top of Express and mongodb",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "main": "dist/src/index.js",
@@ -324,6 +324,35 @@ describe("ConversationAgent", () => {
324
324
  });
325
325
  });
326
326
 
327
+ it("should not double-encode string tool results on reload", async () => {
328
+ // Real production flow: ToolExecutor.formatResultForAI returns a JSON string,
329
+ // which is what AgentRunner stores on tool_result content blocks. On the
330
+ // round-trip back from storage, that string must NOT be JSON.stringified
331
+ // again — otherwise the model sees escaped JSON literals on every reload.
332
+ const alreadyStringified = JSON.stringify({ temp: "22C", condition: "sunny" });
333
+
334
+ const conversationData: ConversationData = {
335
+ messages: [
336
+ {
337
+ role: "user",
338
+ toolResults: [{ id: "call_1", result: alreadyStringified }],
339
+ },
340
+ ],
341
+ };
342
+
343
+ agent.loadConversationSpy.and.returnValue(Promise.resolve(conversationData));
344
+
345
+ const input: any = { conversationId: "conv-123" };
346
+ await agent.testBeforeRun(input, {});
347
+
348
+ expect(input.history[0]).toEqual({
349
+ role: "tool",
350
+ toolCallId: "call_1",
351
+ toolName: "",
352
+ result: alreadyStringified, // not JSON.stringify(alreadyStringified)
353
+ });
354
+ });
355
+
327
356
  it("should preserve assistant messages with toolCalls", async () => {
328
357
  const conversationData: ConversationData = {
329
358
  messages: [
@@ -308,7 +308,9 @@ export abstract class ConversationAgent<Context extends FlinkContext = FlinkCont
308
308
  role: "tool",
309
309
  toolCallId: toolResult.id,
310
310
  toolName: "", // Not stored, but required by type
311
- result: JSON.stringify(toolResult.result),
311
+ // Tool results are persisted as already-stringified payloads (see formatResultForAI).
312
+ // JSON.stringify-ing again would double-encode strings into escaped JSON literals.
313
+ result: typeof toolResult.result === "string" ? toolResult.result : JSON.stringify(toolResult.result),
312
314
  })
313
315
  );
314
316
  } else if (msg.role === "user") {