fix(pi-claude-cli,droid-cli,dashboard): stop ballooning resumed CLI sessions and thread per-chat session
In a multi-iteration tool loop, buildResumePrompt anchored on the last user message and walked forward through preceding tool results. The only user message stays at index 0, so each iteration re-sent the entire transcript over stdin to the resumed CLI session, duplicating the original query plus a growing stack of tool results into the on-disk session every turn. The agent saw its own context bloating and called it "compaction." Anchor on the last assistant message and slice forward instead — the genuine delta since the last turn already on disk. Same bug+fix in droid-cli (copy of pi-claude-cli). Quick chat also created a fresh pi/Claude CLI session per user message and faked continuity by stuffing the last 50 chat_messages into the prompt as "## Previous Conversation". Replace that with real session continuity: chat_sessions gains a cliSessionFile column (migration 56); ChatManager opens the existing pi SessionManager file when present and creates a fresh one (persisting its path) on the first turn. The prompt now carries only the new user content. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1145,6 +1145,42 @@ describe("buildResumePrompt", () => {
|
||||
expect(buildResumePrompt(context)).toBe("Hello from blocks");
|
||||
});
|
||||
|
||||
// Regression: multi-iteration tool loops re-anchor on the LAST assistant
|
||||
// turn, not the (only) user message at index 0. Previously this dumped the
|
||||
// entire transcript into a "user" prompt every iteration, ballooning the
|
||||
// resumed session.
|
||||
it("returns ONLY the trailing tool result during a multi-iteration tool loop", () => {
|
||||
const context = {
|
||||
messages: [
|
||||
{ role: "user", content: "Find foo" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", name: "find", arguments: { pattern: "foo" } }],
|
||||
},
|
||||
{ role: "toolResult", toolName: "find", content: "no matches (turn 1)" },
|
||||
{
|
||||
role: "assistant",
|
||||
content: [{ type: "toolCall", name: "find", arguments: { pattern: "foo" } }],
|
||||
},
|
||||
{ role: "toolResult", toolName: "find", content: "no matches (turn 2)" },
|
||||
],
|
||||
};
|
||||
const result = buildResumePrompt(context) as string;
|
||||
expect(result).toContain("no matches (turn 2)");
|
||||
expect(result).not.toContain("no matches (turn 1)");
|
||||
expect(result).not.toContain("Find foo");
|
||||
});
|
||||
|
||||
it("returns empty string mid-loop when only an assistant turn exists since the last delta", () => {
|
||||
const context = {
|
||||
messages: [
|
||||
{ role: "user", content: "Hi" },
|
||||
{ role: "assistant", content: "Working..." },
|
||||
],
|
||||
};
|
||||
expect(buildResumePrompt(context)).toBe("");
|
||||
});
|
||||
|
||||
it("handles images in the final user message by returning ContentBlock[]", () => {
|
||||
const context = {
|
||||
messages: [
|
||||
|
||||
Reference in New Issue
Block a user