@prestyj/cli 5.6.0 → 5.8.0
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 +2 -2
- package/dist/app-sidecar.js +369 -92
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli/auth.d.ts.map +1 -1
- package/dist/cli/auth.js +5 -2
- package/dist/cli/auth.js.map +1 -1
- package/dist/cli/shared.d.ts.map +1 -1
- package/dist/cli/shared.js +2 -0
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +33 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/config.test.js +7 -0
- package/dist/config.test.js.map +1 -1
- package/dist/core/agent-session-compaction.test.js +521 -10
- package/dist/core/agent-session-compaction.test.js.map +1 -1
- package/dist/core/agent-session-memory-tail.test.js +1 -1
- package/dist/core/agent-session-memory-tail.test.js.map +1 -1
- package/dist/core/agent-session-queue.test.js +1 -1
- package/dist/core/agent-session-queue.test.js.map +1 -1
- package/dist/core/agent-session-review-coverage.test.d.ts +2 -0
- package/dist/core/agent-session-review-coverage.test.d.ts.map +1 -0
- package/dist/core/agent-session-review-coverage.test.js +83 -0
- package/dist/core/agent-session-review-coverage.test.js.map +1 -0
- package/dist/core/agent-session-tool-result-policy.test.d.ts +2 -0
- package/dist/core/agent-session-tool-result-policy.test.d.ts.map +1 -0
- package/dist/core/agent-session-tool-result-policy.test.js +25 -0
- package/dist/core/agent-session-tool-result-policy.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +51 -12
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +415 -106
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-providers.d.ts.map +1 -1
- package/dist/core/auth-providers.js +15 -8
- package/dist/core/auth-providers.js.map +1 -1
- package/dist/core/compaction/active-context.d.ts +17 -0
- package/dist/core/compaction/active-context.d.ts.map +1 -0
- package/dist/core/compaction/active-context.js +20 -0
- package/dist/core/compaction/active-context.js.map +1 -0
- package/dist/core/compaction/active-context.test.d.ts +2 -0
- package/dist/core/compaction/active-context.test.d.ts.map +1 -0
- package/dist/core/compaction/active-context.test.js +36 -0
- package/dist/core/compaction/active-context.test.js.map +1 -0
- package/dist/core/compaction/compactor.d.ts +19 -18
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +64 -42
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +122 -65
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts +25 -1
- package/dist/core/compaction/token-estimator.d.ts.map +1 -1
- package/dist/core/compaction/token-estimator.js +86 -1
- package/dist/core/compaction/token-estimator.js.map +1 -1
- package/dist/core/compaction/token-estimator.test.js +147 -1
- package/dist/core/compaction/token-estimator.test.js.map +1 -1
- package/dist/core/compaction/tool-result-pruner.d.ts +37 -0
- package/dist/core/compaction/tool-result-pruner.d.ts.map +1 -0
- package/dist/core/compaction/tool-result-pruner.js +103 -0
- package/dist/core/compaction/tool-result-pruner.js.map +1 -0
- package/dist/core/compaction/tool-result-pruner.test.d.ts +2 -0
- package/dist/core/compaction/tool-result-pruner.test.d.ts.map +1 -0
- package/dist/core/compaction/tool-result-pruner.test.js +148 -0
- package/dist/core/compaction/tool-result-pruner.test.js.map +1 -0
- package/dist/core/event-bus.d.ts +2 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/event-bus.js.map +1 -1
- package/dist/core/ideal-review.d.ts +33 -0
- package/dist/core/ideal-review.d.ts.map +1 -1
- package/dist/core/ideal-review.js +78 -0
- package/dist/core/ideal-review.js.map +1 -1
- package/dist/core/ideal-review.test.js +43 -1
- package/dist/core/ideal-review.test.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/lsp/client.d.ts +3 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +20 -0
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/manager.d.ts +32 -9
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +89 -37
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/lsp/manager.test.js +56 -4
- package/dist/core/lsp/manager.test.js.map +1 -1
- package/dist/core/project-discovery.d.ts +6 -9
- package/dist/core/project-discovery.d.ts.map +1 -1
- package/dist/core/project-discovery.js +35 -28
- package/dist/core/project-discovery.js.map +1 -1
- package/dist/core/project-discovery.test.js +148 -0
- package/dist/core/project-discovery.test.js.map +1 -1
- package/dist/core/resolve-start.test.js +1 -0
- package/dist/core/resolve-start.test.js.map +1 -1
- package/dist/core/run-lifecycle.d.ts +40 -0
- package/dist/core/run-lifecycle.d.ts.map +1 -0
- package/dist/core/run-lifecycle.js +95 -0
- package/dist/core/run-lifecycle.js.map +1 -0
- package/dist/core/run-lifecycle.test.d.ts +2 -0
- package/dist/core/run-lifecycle.test.d.ts.map +1 -0
- package/dist/core/run-lifecycle.test.js +64 -0
- package/dist/core/run-lifecycle.test.js.map +1 -0
- package/dist/core/session-compaction.d.ts +3 -0
- package/dist/core/session-compaction.d.ts.map +1 -1
- package/dist/core/session-compaction.js +14 -1
- package/dist/core/session-compaction.js.map +1 -1
- package/dist/core/session-compaction.test.js +5 -0
- package/dist/core/session-compaction.test.js.map +1 -1
- package/dist/core/session-manager.d.ts +33 -2
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +73 -2
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-manager.test.js +97 -2
- package/dist/core/session-manager.test.js.map +1 -1
- package/dist/core/session-preview.d.ts +11 -0
- package/dist/core/session-preview.d.ts.map +1 -0
- package/dist/core/session-preview.js +50 -0
- package/dist/core/session-preview.js.map +1 -0
- package/dist/core/settings-manager.d.ts +1 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +3 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/subagent-manager.d.ts +42 -7
- package/dist/core/subagent-manager.d.ts.map +1 -1
- package/dist/core/subagent-manager.js +346 -30
- package/dist/core/subagent-manager.js.map +1 -1
- package/dist/core/subagent-manager.test.js +201 -5
- package/dist/core/subagent-manager.test.js.map +1 -1
- package/dist/core/subagent-store.d.ts +20 -0
- package/dist/core/subagent-store.d.ts.map +1 -0
- package/dist/core/subagent-store.js +177 -0
- package/dist/core/subagent-store.js.map +1 -0
- package/dist/core/subagent-store.test.d.ts +2 -0
- package/dist/core/subagent-store.test.d.ts.map +1 -0
- package/dist/core/subagent-store.test.js +94 -0
- package/dist/core/subagent-store.test.js.map +1 -0
- package/dist/modes/agent-home-mode.d.ts.map +1 -1
- package/dist/modes/agent-home-mode.js +10 -3
- package/dist/modes/agent-home-mode.js.map +1 -1
- package/dist/modes/serve-mode.d.ts.map +1 -1
- package/dist/modes/serve-mode.js +10 -3
- package/dist/modes/serve-mode.js.map +1 -1
- package/dist/modes/subagent-worker-mode.d.ts +2 -0
- package/dist/modes/subagent-worker-mode.d.ts.map +1 -1
- package/dist/modes/subagent-worker-mode.js +13 -3
- package/dist/modes/subagent-worker-mode.js.map +1 -1
- package/dist/tools/bash.d.ts +9 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +28 -18
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/bash.test.d.ts +2 -0
- package/dist/tools/bash.test.d.ts.map +1 -0
- package/dist/tools/bash.test.js +56 -0
- package/dist/tools/bash.test.js.map +1 -0
- package/dist/tools/goals.d.ts +3 -3
- package/dist/tools/overflow.d.ts +12 -2
- package/dist/tools/overflow.d.ts.map +1 -1
- package/dist/tools/overflow.js +49 -4
- package/dist/tools/overflow.js.map +1 -1
- package/dist/tools/overflow.test.d.ts +2 -0
- package/dist/tools/overflow.test.d.ts.map +1 -0
- package/dist/tools/overflow.test.js +62 -0
- package/dist/tools/overflow.test.js.map +1 -0
- package/dist/tools/subagent-shared.d.ts +8 -0
- package/dist/tools/subagent-shared.d.ts.map +1 -1
- package/dist/tools/subagent-shared.js.map +1 -1
- package/dist/tools/subagent.d.ts +3 -8
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +10 -8
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/subagent.test.js +17 -1
- package/dist/tools/subagent.test.js.map +1 -1
- package/dist/tools/tasks.d.ts +2 -2
- package/dist/tools/web-fetch.d.ts +1 -1
- package/dist/ui/App.d.ts +7 -3
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +68 -98
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/components/Footer.d.ts +1 -0
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +3 -3
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/Footer.test.d.ts +2 -0
- package/dist/ui/components/Footer.test.d.ts.map +1 -0
- package/dist/ui/components/Footer.test.js +16 -0
- package/dist/ui/components/Footer.test.js.map +1 -0
- package/dist/ui/components/ModelSelector.d.ts.map +1 -1
- package/dist/ui/components/ModelSelector.js +1 -0
- package/dist/ui/components/ModelSelector.js.map +1 -1
- package/dist/ui/components/SubAgentPanel.d.ts +2 -0
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
- package/dist/ui/components/SubAgentPanel.js +4 -2
- package/dist/ui/components/SubAgentPanel.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.d.ts +9 -6
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +68 -8
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useAgentLoop.test.js +31 -0
- package/dist/ui/hooks/useAgentLoop.test.js.map +1 -1
- package/dist/ui/hooks/useContextCompaction.d.ts +5 -8
- package/dist/ui/hooks/useContextCompaction.d.ts.map +1 -1
- package/dist/ui/hooks/useContextCompaction.js +77 -16
- package/dist/ui/hooks/useContextCompaction.js.map +1 -1
- package/dist/ui/hooks/useContextCompaction.test.d.ts +2 -0
- package/dist/ui/hooks/useContextCompaction.test.d.ts.map +1 -0
- package/dist/ui/hooks/useContextCompaction.test.js +135 -0
- package/dist/ui/hooks/useContextCompaction.test.js.map +1 -0
- package/dist/ui/hooks/useSessionPersistence.d.ts +4 -2
- package/dist/ui/hooks/useSessionPersistence.d.ts.map +1 -1
- package/dist/ui/hooks/useSessionPersistence.js +10 -1
- package/dist/ui/hooks/useSessionPersistence.js.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.d.ts +3 -3
- package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.js +5 -9
- package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +7 -2
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +7 -2
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +4 -4
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/render.test.js +10 -1
- package/dist/ui/render.test.js.map +1 -1
- package/dist/ui/terminal-history.js +6 -2
- package/dist/ui/terminal-history.js.map +1 -1
- package/dist/ui/terminal-history.test.js +2 -2
- package/dist/ui/terminal-history.test.js.map +1 -1
- package/dist/ui/tui-history-parity.test.js +8 -1
- package/dist/ui/tui-history-parity.test.js.map +1 -1
- package/dist/utils/git.d.ts +2 -0
- package/dist/utils/git.d.ts.map +1 -1
- package/dist/utils/git.js +12 -0
- package/dist/utils/git.js.map +1 -1
- package/dist/utils/git.test.d.ts +2 -0
- package/dist/utils/git.test.d.ts.map +1 -0
- package/dist/utils/git.test.js +36 -0
- package/dist/utils/git.test.js.map +1 -0
- package/package.json +5 -5
- package/dist/utils/session-title.d.ts +0 -19
- package/dist/utils/session-title.d.ts.map +0 -1
- package/dist/utils/session-title.js +0 -82
- package/dist/utils/session-title.js.map +0 -1
|
@@ -2,6 +2,8 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { MODELS } from "./model-registry.js";
|
|
6
|
+
import { estimateConversationTokens } from "./compaction/token-estimator.js";
|
|
5
7
|
const shouldCompactMock = vi.hoisted(() => vi.fn());
|
|
6
8
|
const compactMock = vi.hoisted(() => vi.fn());
|
|
7
9
|
const agentLoopMock = vi.hoisted(() => vi.fn());
|
|
@@ -39,6 +41,20 @@ async function writeJson(filePath, value) {
|
|
|
39
41
|
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
40
42
|
await fs.writeFile(filePath, JSON.stringify(value, null, 2), "utf-8");
|
|
41
43
|
}
|
|
44
|
+
function compactionResult(messages, compacted = true) {
|
|
45
|
+
return {
|
|
46
|
+
messages,
|
|
47
|
+
result: {
|
|
48
|
+
compacted,
|
|
49
|
+
...(compacted ? {} : { reason: "too_few_messages" }),
|
|
50
|
+
originalCount: compacted ? 6 : messages.length,
|
|
51
|
+
newCount: messages.length,
|
|
52
|
+
tokensBeforeEstimate: 180_000,
|
|
53
|
+
tokensAfterEstimate: compacted ? 2_000 : 180_000,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const providerModels = MODELS.filter((model, index, models) => models.findIndex((candidate) => candidate.provider === model.provider) === index).map((model) => ({ provider: model.provider, model: model.id }));
|
|
42
58
|
beforeEach(async () => {
|
|
43
59
|
originalHome = process.env.HOME;
|
|
44
60
|
tmpHome = await fs.mkdtemp(path.join(os.tmpdir(), "agent-session-home-"));
|
|
@@ -47,13 +63,29 @@ beforeEach(async () => {
|
|
|
47
63
|
shouldCompactMock.mockReset();
|
|
48
64
|
compactMock.mockReset();
|
|
49
65
|
agentLoopMock.mockReset();
|
|
50
|
-
|
|
51
|
-
anthropic
|
|
52
|
-
|
|
53
|
-
|
|
66
|
+
const authProviders = [
|
|
67
|
+
"anthropic",
|
|
68
|
+
"openai",
|
|
69
|
+
"sakana",
|
|
70
|
+
"xai",
|
|
71
|
+
"gemini",
|
|
72
|
+
"moonshot",
|
|
73
|
+
"glm",
|
|
74
|
+
"minimax",
|
|
75
|
+
"xiaomi",
|
|
76
|
+
"xiaomi-credits",
|
|
77
|
+
"deepseek",
|
|
78
|
+
"openrouter",
|
|
79
|
+
];
|
|
80
|
+
await writeJson(path.join(tmpHome, ".ezcoder", "auth.json"), Object.fromEntries(authProviders.map((provider) => [
|
|
81
|
+
provider,
|
|
82
|
+
{
|
|
83
|
+
accessToken: `test-${provider}-token`,
|
|
84
|
+
refreshToken: `test-${provider}-refresh`,
|
|
54
85
|
expiresAt: Date.now() + 3_600_000,
|
|
86
|
+
...(provider === "openai" ? { accountId: "chatgpt-account" } : {}),
|
|
55
87
|
},
|
|
56
|
-
|
|
88
|
+
])));
|
|
57
89
|
await writeJson(path.join(tmpHome, ".ezcoder", "settings.json"), {
|
|
58
90
|
autoCompact: true,
|
|
59
91
|
compactThreshold: 0.1,
|
|
@@ -100,15 +132,108 @@ describe("AgentSession worker auto-compaction", () => {
|
|
|
100
132
|
await session.initialize();
|
|
101
133
|
await session.prompt("Do worker task");
|
|
102
134
|
await session.dispose();
|
|
103
|
-
expect(shouldCompactMock).toHaveBeenCalledWith(expect.arrayContaining([{ role: "user", content: "Do worker task" }]), expect.any(Number), 0.1, undefined
|
|
135
|
+
expect(shouldCompactMock).toHaveBeenCalledWith(expect.arrayContaining([{ role: "user", content: "Do worker task" }]), expect.any(Number), 0.1, undefined);
|
|
104
136
|
expect(compactMock).toHaveBeenCalledWith(expect.arrayContaining([{ role: "user", content: "Do worker task" }]), expect.objectContaining({
|
|
105
137
|
provider: "anthropic",
|
|
106
138
|
model: "claude-test",
|
|
107
|
-
apiKey: "test-
|
|
139
|
+
apiKey: "test-anthropic-token",
|
|
108
140
|
}));
|
|
109
141
|
expect(agentLoopMock).toHaveBeenCalledWith(expect.arrayContaining([
|
|
110
142
|
{ role: "user", content: "[compacted worker context]\n\nDo worker task" },
|
|
111
143
|
]), expect.objectContaining({ provider: "anthropic", model: "claude-test" }));
|
|
144
|
+
}, 15_000);
|
|
145
|
+
});
|
|
146
|
+
describe("AgentSession stale tool-output pruning", () => {
|
|
147
|
+
it("stubs superseded reads in-place during the pre-step transform", async () => {
|
|
148
|
+
shouldCompactMock.mockReturnValue(false);
|
|
149
|
+
let capturedMessages = [];
|
|
150
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
151
|
+
messages.push({
|
|
152
|
+
role: "assistant",
|
|
153
|
+
content: [
|
|
154
|
+
{ type: "tool_call", id: "old-read", name: "read", args: { file_path: "src/a.ts" } },
|
|
155
|
+
],
|
|
156
|
+
}, {
|
|
157
|
+
role: "tool",
|
|
158
|
+
content: [{ type: "tool_result", toolCallId: "old-read", content: "x".repeat(150_000) }],
|
|
159
|
+
}, { role: "user", content: "turn 2" }, {
|
|
160
|
+
role: "assistant",
|
|
161
|
+
content: [
|
|
162
|
+
{ type: "tool_call", id: "new-read", name: "read", args: { file_path: "src/a.ts" } },
|
|
163
|
+
],
|
|
164
|
+
}, {
|
|
165
|
+
role: "tool",
|
|
166
|
+
content: [{ type: "tool_result", toolCallId: "new-read", content: "fresh" }],
|
|
167
|
+
}, { role: "user", content: "turn 3" });
|
|
168
|
+
capturedMessages = await options.transformContext(messages, { pendingMessages: [] });
|
|
169
|
+
yield { type: "agent_done" };
|
|
170
|
+
});
|
|
171
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
172
|
+
const session = new AgentSession({
|
|
173
|
+
provider: "anthropic",
|
|
174
|
+
model: "claude-test",
|
|
175
|
+
cwd: tmpProject,
|
|
176
|
+
systemPrompt: "system prompt",
|
|
177
|
+
transient: true,
|
|
178
|
+
});
|
|
179
|
+
await session.initialize();
|
|
180
|
+
await session.prompt("prune me");
|
|
181
|
+
await session.dispose();
|
|
182
|
+
const results = capturedMessages
|
|
183
|
+
.filter((msg) => msg.role === "tool")
|
|
184
|
+
.flatMap((msg) => (Array.isArray(msg.content) ? msg.content : []));
|
|
185
|
+
const oldRead = results.find((r) => r.type === "tool_result" && r.toolCallId === "old-read");
|
|
186
|
+
const newRead = results.find((r) => r.type === "tool_result" && r.toolCallId === "new-read");
|
|
187
|
+
expect(oldRead?.content).toContain("superseded by a newer read");
|
|
188
|
+
expect(newRead?.content).toBe("fresh");
|
|
189
|
+
expect(compactMock).not.toHaveBeenCalled();
|
|
190
|
+
});
|
|
191
|
+
it("discards the turn's provider usage after a prune so freed tokens defer compaction", async () => {
|
|
192
|
+
shouldCompactMock.mockReturnValue(false);
|
|
193
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
194
|
+
messages.push({
|
|
195
|
+
role: "assistant",
|
|
196
|
+
content: [
|
|
197
|
+
{ type: "tool_call", id: "old-read", name: "read", args: { file_path: "src/a.ts" } },
|
|
198
|
+
],
|
|
199
|
+
}, {
|
|
200
|
+
role: "tool",
|
|
201
|
+
content: [{ type: "tool_result", toolCallId: "old-read", content: "x".repeat(150_000) }],
|
|
202
|
+
}, { role: "user", content: "turn 2" }, {
|
|
203
|
+
role: "assistant",
|
|
204
|
+
content: [
|
|
205
|
+
{ type: "tool_call", id: "new-read", name: "read", args: { file_path: "src/a.ts" } },
|
|
206
|
+
],
|
|
207
|
+
}, {
|
|
208
|
+
role: "tool",
|
|
209
|
+
content: [{ type: "tool_result", toolCallId: "new-read", content: "fresh" }],
|
|
210
|
+
}, { role: "user", content: "turn 3" });
|
|
211
|
+
// Usage counted the soon-to-be-pruned 150k-char read. After the prune
|
|
212
|
+
// it must NOT reach shouldCompact — estimation on the pruned history
|
|
213
|
+
// takes over instead.
|
|
214
|
+
await options.transformContext(messages, {
|
|
215
|
+
usage: { inputTokens: 180_000, outputTokens: 500 },
|
|
216
|
+
pendingMessages: [],
|
|
217
|
+
});
|
|
218
|
+
yield { type: "agent_done" };
|
|
219
|
+
});
|
|
220
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
221
|
+
const session = new AgentSession({
|
|
222
|
+
provider: "anthropic",
|
|
223
|
+
model: "claude-test",
|
|
224
|
+
cwd: tmpProject,
|
|
225
|
+
systemPrompt: "system prompt",
|
|
226
|
+
transient: true,
|
|
227
|
+
});
|
|
228
|
+
await session.initialize();
|
|
229
|
+
await session.prompt("prune usage fallback");
|
|
230
|
+
await session.dispose();
|
|
231
|
+
// The transform after the in-loop mutation is the last shouldCompact call;
|
|
232
|
+
// its actualTokens argument must be an estimate of the pruned history,
|
|
233
|
+
// far below the stale 180k usage figure.
|
|
234
|
+
const lastCall = shouldCompactMock.mock.calls.at(-1);
|
|
235
|
+
const actualTokens = lastCall[3];
|
|
236
|
+
expect(actualTokens).toBeLessThan(50_000);
|
|
112
237
|
});
|
|
113
238
|
});
|
|
114
239
|
describe("AgentSession overflow recovery", () => {
|
|
@@ -138,11 +263,14 @@ describe("AgentSession overflow recovery", () => {
|
|
|
138
263
|
let forceResult;
|
|
139
264
|
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
140
265
|
// Non-force pre-call invocation must pass through untouched.
|
|
141
|
-
const passthrough = await options.transformContext(messages);
|
|
266
|
+
const passthrough = await options.transformContext(messages, { pendingMessages: [] });
|
|
142
267
|
expect(passthrough).toBe(messages);
|
|
143
268
|
expect(compactMock).not.toHaveBeenCalled();
|
|
144
269
|
// Force invocation (overflow) must compact and return the smaller array.
|
|
145
|
-
forceResult = await options.transformContext(messages, {
|
|
270
|
+
forceResult = await options.transformContext(messages, {
|
|
271
|
+
force: true,
|
|
272
|
+
pendingMessages: [],
|
|
273
|
+
});
|
|
146
274
|
yield { type: "agent_done" };
|
|
147
275
|
});
|
|
148
276
|
const { AgentSession } = await import("./agent-session.js");
|
|
@@ -160,8 +288,308 @@ describe("AgentSession overflow recovery", () => {
|
|
|
160
288
|
expect(forceResult).toEqual(compactedMessages);
|
|
161
289
|
});
|
|
162
290
|
});
|
|
291
|
+
describe("AgentSession mid-turn compaction", () => {
|
|
292
|
+
it("compacts non-forced in-flight history exactly at the configured 80% boundary", async () => {
|
|
293
|
+
await writeJson(path.join(tmpHome, ".ezcoder", "settings.json"), {
|
|
294
|
+
autoCompact: true,
|
|
295
|
+
compactThreshold: 0.8,
|
|
296
|
+
});
|
|
297
|
+
const pendingMessage = {
|
|
298
|
+
role: "tool",
|
|
299
|
+
content: [
|
|
300
|
+
{
|
|
301
|
+
type: "tool_result",
|
|
302
|
+
toolCallId: "t1",
|
|
303
|
+
content: "pending tool output ".repeat(8),
|
|
304
|
+
},
|
|
305
|
+
],
|
|
306
|
+
};
|
|
307
|
+
const usage = {
|
|
308
|
+
inputTokens: 159_900,
|
|
309
|
+
cacheRead: 30,
|
|
310
|
+
cacheWrite: 20,
|
|
311
|
+
outputTokens: 40,
|
|
312
|
+
};
|
|
313
|
+
shouldCompactMock.mockImplementation((_messages, contextWindow, threshold, actualTokens) => actualTokens !== undefined && actualTokens >= Math.ceil(contextWindow * threshold));
|
|
314
|
+
const compactedMessages = [
|
|
315
|
+
{ role: "system", content: "system prompt" },
|
|
316
|
+
{ role: "user", content: "[compacted in-flight history]" },
|
|
317
|
+
];
|
|
318
|
+
compactMock.mockResolvedValue(compactionResult(compactedMessages));
|
|
319
|
+
let transformed;
|
|
320
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
321
|
+
messages.push({
|
|
322
|
+
role: "assistant",
|
|
323
|
+
content: [{ type: "tool_call", id: "t1", name: "read", args: {} }],
|
|
324
|
+
});
|
|
325
|
+
messages.push(pendingMessage);
|
|
326
|
+
transformed = await options.transformContext(messages, {
|
|
327
|
+
usage,
|
|
328
|
+
pendingMessages: [pendingMessage],
|
|
329
|
+
});
|
|
330
|
+
yield { type: "agent_done" };
|
|
331
|
+
});
|
|
332
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
333
|
+
const session = new AgentSession({
|
|
334
|
+
provider: "anthropic",
|
|
335
|
+
model: "claude-test",
|
|
336
|
+
cwd: tmpProject,
|
|
337
|
+
systemPrompt: "system prompt",
|
|
338
|
+
transient: true,
|
|
339
|
+
});
|
|
340
|
+
await session.initialize();
|
|
341
|
+
await session.prompt("run a tool loop");
|
|
342
|
+
await session.dispose();
|
|
343
|
+
// Computed after the run: the anchored usage sample calibrates the token
|
|
344
|
+
// estimator, so the pending-message estimate must use the same calibrated
|
|
345
|
+
// ratio the transform used when it called shouldCompact.
|
|
346
|
+
const expectedActiveTokens = usage.inputTokens +
|
|
347
|
+
usage.cacheRead +
|
|
348
|
+
usage.cacheWrite +
|
|
349
|
+
usage.outputTokens +
|
|
350
|
+
estimateConversationTokens([pendingMessage]);
|
|
351
|
+
expect(expectedActiveTokens).toBeGreaterThanOrEqual(160_000);
|
|
352
|
+
expect(transformed).toEqual(compactedMessages);
|
|
353
|
+
expect(shouldCompactMock).toHaveBeenCalledWith(expect.any(Array), 200_000, 0.8, expectedActiveTokens);
|
|
354
|
+
expect(compactMock.mock.calls.at(-1)?.[0]).toContainEqual(pendingMessage);
|
|
355
|
+
});
|
|
356
|
+
it("reuses authoritative usage for the first context check of the next prompt", async () => {
|
|
357
|
+
const usage = { inputTokens: 120_000, outputTokens: 100 };
|
|
358
|
+
shouldCompactMock.mockImplementation((_messages, contextWindow, threshold, actualTokens) => actualTokens !== undefined && actualTokens >= Math.ceil(contextWindow * threshold));
|
|
359
|
+
compactMock.mockResolvedValue(compactionResult([
|
|
360
|
+
{ role: "system", content: "system prompt" },
|
|
361
|
+
{ role: "user", content: "[compacted across prompts]" },
|
|
362
|
+
]));
|
|
363
|
+
let run = 0;
|
|
364
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
365
|
+
run += 1;
|
|
366
|
+
await options.transformContext(messages, { pendingMessages: [] });
|
|
367
|
+
if (run === 1) {
|
|
368
|
+
messages.push({ role: "assistant", content: "first response" });
|
|
369
|
+
yield {
|
|
370
|
+
type: "turn_end",
|
|
371
|
+
turn: 1,
|
|
372
|
+
stopReason: "end_turn",
|
|
373
|
+
usage,
|
|
374
|
+
timing: {
|
|
375
|
+
startedAt: 1,
|
|
376
|
+
completedAt: 2,
|
|
377
|
+
providerDurationMs: 1,
|
|
378
|
+
},
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
yield { type: "agent_done" };
|
|
382
|
+
});
|
|
383
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
384
|
+
const session = new AgentSession({
|
|
385
|
+
provider: "anthropic",
|
|
386
|
+
model: "claude-fable-5",
|
|
387
|
+
cwd: tmpProject,
|
|
388
|
+
systemPrompt: "system prompt",
|
|
389
|
+
transient: true,
|
|
390
|
+
});
|
|
391
|
+
await session.initialize();
|
|
392
|
+
await session.prompt("first prompt");
|
|
393
|
+
await session.prompt("second prompt");
|
|
394
|
+
await session.dispose();
|
|
395
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
396
|
+
expect(shouldCompactMock.mock.calls.some((call) => typeof call[3] === "number" && call[3] >= usage.inputTokens + usage.outputTokens)).toBe(true);
|
|
397
|
+
});
|
|
398
|
+
it.each(providerModels)("$provider uses the same normalized usage formula", async ({ provider, model }) => {
|
|
399
|
+
await writeJson(path.join(tmpHome, ".ezcoder", "settings.json"), {
|
|
400
|
+
autoCompact: true,
|
|
401
|
+
compactThreshold: 0.8,
|
|
402
|
+
});
|
|
403
|
+
shouldCompactMock.mockReturnValue(false);
|
|
404
|
+
const usage = {
|
|
405
|
+
inputTokens: 100,
|
|
406
|
+
cacheRead: 30,
|
|
407
|
+
cacheWrite: 20,
|
|
408
|
+
outputTokens: 40,
|
|
409
|
+
};
|
|
410
|
+
const expectedActiveTokens = 190;
|
|
411
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
412
|
+
await options.transformContext(messages, {
|
|
413
|
+
usage,
|
|
414
|
+
pendingMessages: [],
|
|
415
|
+
});
|
|
416
|
+
yield { type: "agent_done" };
|
|
417
|
+
});
|
|
418
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
419
|
+
const session = new AgentSession({
|
|
420
|
+
provider: provider,
|
|
421
|
+
model,
|
|
422
|
+
cwd: tmpProject,
|
|
423
|
+
systemPrompt: "system prompt",
|
|
424
|
+
transient: true,
|
|
425
|
+
});
|
|
426
|
+
await session.initialize();
|
|
427
|
+
await session.prompt("test normalized usage");
|
|
428
|
+
await session.dispose();
|
|
429
|
+
expect(shouldCompactMock.mock.calls.some((call) => call[3] === expectedActiveTokens)).toBe(true);
|
|
430
|
+
}, 15_000);
|
|
431
|
+
it("honors a custom threshold during a non-forced transform", async () => {
|
|
432
|
+
await writeJson(path.join(tmpHome, ".ezcoder", "settings.json"), {
|
|
433
|
+
autoCompact: true,
|
|
434
|
+
compactThreshold: 0.65,
|
|
435
|
+
});
|
|
436
|
+
shouldCompactMock.mockReturnValue(false);
|
|
437
|
+
const usage = { inputTokens: 1_000, outputTokens: 100 };
|
|
438
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
439
|
+
await options.transformContext(messages, { usage, pendingMessages: [] });
|
|
440
|
+
yield { type: "agent_done" };
|
|
441
|
+
});
|
|
442
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
443
|
+
const session = new AgentSession({
|
|
444
|
+
provider: "anthropic",
|
|
445
|
+
model: "claude-test",
|
|
446
|
+
cwd: tmpProject,
|
|
447
|
+
systemPrompt: "system prompt",
|
|
448
|
+
transient: true,
|
|
449
|
+
});
|
|
450
|
+
await session.initialize();
|
|
451
|
+
await session.prompt("custom threshold");
|
|
452
|
+
await session.dispose();
|
|
453
|
+
expect(shouldCompactMock).toHaveBeenCalledWith(expect.any(Array), 200_000, 0.65, 1_100);
|
|
454
|
+
});
|
|
455
|
+
it("honors autoCompact false for non-forced calls but force bypasses settings and cooldown", async () => {
|
|
456
|
+
await writeJson(path.join(tmpHome, ".ezcoder", "settings.json"), {
|
|
457
|
+
autoCompact: false,
|
|
458
|
+
compactThreshold: 0.8,
|
|
459
|
+
});
|
|
460
|
+
const compactedMessages = [
|
|
461
|
+
{ role: "system", content: "system prompt" },
|
|
462
|
+
{ role: "user", content: "[forced compaction]" },
|
|
463
|
+
];
|
|
464
|
+
compactMock
|
|
465
|
+
.mockResolvedValueOnce(compactionResult([
|
|
466
|
+
{ role: "system", content: "system prompt" },
|
|
467
|
+
{ role: "user", content: "too little history" },
|
|
468
|
+
{ role: "assistant", content: "reply" },
|
|
469
|
+
], false))
|
|
470
|
+
.mockResolvedValueOnce(compactionResult(compactedMessages));
|
|
471
|
+
let nonForcedResult;
|
|
472
|
+
let forcedResult;
|
|
473
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
474
|
+
nonForcedResult = await options.transformContext(messages, { pendingMessages: [] });
|
|
475
|
+
await options.transformContext(messages, { force: true, pendingMessages: [] });
|
|
476
|
+
forcedResult = await options.transformContext(messages, {
|
|
477
|
+
force: true,
|
|
478
|
+
pendingMessages: [],
|
|
479
|
+
});
|
|
480
|
+
yield { type: "agent_done" };
|
|
481
|
+
});
|
|
482
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
483
|
+
const session = new AgentSession({
|
|
484
|
+
provider: "anthropic",
|
|
485
|
+
model: "claude-test",
|
|
486
|
+
cwd: tmpProject,
|
|
487
|
+
systemPrompt: "system prompt",
|
|
488
|
+
transient: true,
|
|
489
|
+
});
|
|
490
|
+
await session.initialize();
|
|
491
|
+
await session.prompt("force despite settings");
|
|
492
|
+
await session.dispose();
|
|
493
|
+
expect(nonForcedResult).toBeDefined();
|
|
494
|
+
expect(shouldCompactMock).not.toHaveBeenCalled();
|
|
495
|
+
expect(compactMock).toHaveBeenCalledTimes(2);
|
|
496
|
+
expect(forcedResult).toEqual(compactedMessages);
|
|
497
|
+
});
|
|
498
|
+
it("cools down after a non-forced no-op instead of retrying every tool step", async () => {
|
|
499
|
+
shouldCompactMock.mockImplementation((_messages, _contextWindow, _threshold, actualTokens) => actualTokens !== undefined);
|
|
500
|
+
compactMock.mockImplementation(async (messages) => compactionResult([...messages], false));
|
|
501
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
502
|
+
const transformOptions = {
|
|
503
|
+
usage: { inputTokens: 180_000, outputTokens: 1_000 },
|
|
504
|
+
pendingMessages: [],
|
|
505
|
+
};
|
|
506
|
+
await options.transformContext(messages, transformOptions);
|
|
507
|
+
await options.transformContext(messages, transformOptions);
|
|
508
|
+
yield { type: "agent_done" };
|
|
509
|
+
});
|
|
510
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
511
|
+
const session = new AgentSession({
|
|
512
|
+
provider: "anthropic",
|
|
513
|
+
model: "claude-test",
|
|
514
|
+
cwd: tmpProject,
|
|
515
|
+
systemPrompt: "system prompt",
|
|
516
|
+
transient: true,
|
|
517
|
+
});
|
|
518
|
+
await session.initialize();
|
|
519
|
+
await session.prompt("no-op cooldown");
|
|
520
|
+
await session.dispose();
|
|
521
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
522
|
+
});
|
|
523
|
+
it("cools down after a failed proactive summary", async () => {
|
|
524
|
+
shouldCompactMock.mockImplementation((_messages, _contextWindow, _threshold, actualTokens) => actualTokens !== undefined);
|
|
525
|
+
compactMock.mockRejectedValue(new Error("summary unavailable"));
|
|
526
|
+
agentLoopMock.mockImplementation(async function* (messages, options) {
|
|
527
|
+
const transformOptions = {
|
|
528
|
+
usage: { inputTokens: 180_000, outputTokens: 1_000 },
|
|
529
|
+
pendingMessages: [],
|
|
530
|
+
};
|
|
531
|
+
await options.transformContext(messages, transformOptions);
|
|
532
|
+
await options.transformContext(messages, transformOptions);
|
|
533
|
+
yield { type: "agent_done" };
|
|
534
|
+
});
|
|
535
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
536
|
+
const session = new AgentSession({
|
|
537
|
+
provider: "anthropic",
|
|
538
|
+
model: "claude-test",
|
|
539
|
+
cwd: tmpProject,
|
|
540
|
+
systemPrompt: "system prompt",
|
|
541
|
+
transient: true,
|
|
542
|
+
});
|
|
543
|
+
await session.initialize();
|
|
544
|
+
await session.prompt("failure cooldown");
|
|
545
|
+
await session.dispose();
|
|
546
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
547
|
+
});
|
|
548
|
+
it("continues the run and cools down across prompts after pre-run compaction fails", async () => {
|
|
549
|
+
shouldCompactMock.mockReturnValue(true);
|
|
550
|
+
compactMock.mockRejectedValue(new Error("summary unavailable"));
|
|
551
|
+
agentLoopMock.mockImplementation(async function* () {
|
|
552
|
+
yield { type: "agent_done" };
|
|
553
|
+
});
|
|
554
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
555
|
+
const session = new AgentSession({
|
|
556
|
+
provider: "anthropic",
|
|
557
|
+
model: "claude-test",
|
|
558
|
+
cwd: tmpProject,
|
|
559
|
+
systemPrompt: "system prompt",
|
|
560
|
+
transient: true,
|
|
561
|
+
});
|
|
562
|
+
await session.initialize();
|
|
563
|
+
await session.prompt("first prompt");
|
|
564
|
+
await session.prompt("second prompt");
|
|
565
|
+
await session.dispose();
|
|
566
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
567
|
+
expect(agentLoopMock).toHaveBeenCalledTimes(2);
|
|
568
|
+
});
|
|
569
|
+
it("cools down across prompts after pre-run compaction is a no-op", async () => {
|
|
570
|
+
shouldCompactMock.mockReturnValue(true);
|
|
571
|
+
compactMock.mockImplementation(async (messages) => compactionResult([...messages], false));
|
|
572
|
+
agentLoopMock.mockImplementation(async function* () {
|
|
573
|
+
yield { type: "agent_done" };
|
|
574
|
+
});
|
|
575
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
576
|
+
const session = new AgentSession({
|
|
577
|
+
provider: "anthropic",
|
|
578
|
+
model: "claude-test",
|
|
579
|
+
cwd: tmpProject,
|
|
580
|
+
systemPrompt: "system prompt",
|
|
581
|
+
transient: true,
|
|
582
|
+
});
|
|
583
|
+
await session.initialize();
|
|
584
|
+
await session.prompt("first prompt");
|
|
585
|
+
await session.prompt("second prompt");
|
|
586
|
+
await session.dispose();
|
|
587
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
588
|
+
expect(agentLoopMock).toHaveBeenCalledTimes(2);
|
|
589
|
+
});
|
|
590
|
+
});
|
|
163
591
|
/** Every .jsonl under the ezcoder session store — must stay empty for
|
|
164
|
-
*
|
|
592
|
+
* transient sessions (Nolan chat/autopilot and subagent spawns). */
|
|
165
593
|
async function listSessionFiles() {
|
|
166
594
|
const sessionsDir = path.join(tmpHome, ".ezcoder", "sessions");
|
|
167
595
|
const out = [];
|
|
@@ -249,4 +677,87 @@ describe("transient sessions never leak to the session store", () => {
|
|
|
249
677
|
expect(await listSessionFiles()).toEqual([]);
|
|
250
678
|
});
|
|
251
679
|
});
|
|
680
|
+
describe("load-time auto-compaction deferral (deferLoadCompaction)", () => {
|
|
681
|
+
// Regression: resuming an over-context session ran a summary LLM call (30s
|
|
682
|
+
// timeout) inline in loadExistingSession — inside initialize(), which the
|
|
683
|
+
// ezcoder-app sidecar's readiness (waitForReady → the whole webview) is gated on.
|
|
684
|
+
// A slow/hanging summary call froze the window for the full timeout. With
|
|
685
|
+
// deferLoadCompaction the resume returns immediately and runLoop()'s
|
|
686
|
+
// pre-run auto-compaction handles it on the first prompt.
|
|
687
|
+
/** Create + persist a real session file, returning its path. */
|
|
688
|
+
async function persistSession() {
|
|
689
|
+
shouldCompactMock.mockReturnValue(false);
|
|
690
|
+
agentLoopMock.mockImplementation(async function* (messages) {
|
|
691
|
+
messages.push({ role: "assistant", content: "first reply" });
|
|
692
|
+
yield { type: "agent_done" };
|
|
693
|
+
});
|
|
694
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
695
|
+
const session = new AgentSession({
|
|
696
|
+
provider: "anthropic",
|
|
697
|
+
model: "claude-test",
|
|
698
|
+
cwd: tmpProject,
|
|
699
|
+
systemPrompt: "system prompt",
|
|
700
|
+
});
|
|
701
|
+
await session.initialize();
|
|
702
|
+
await session.prompt("hello");
|
|
703
|
+
await session.dispose();
|
|
704
|
+
const files = await listSessionFiles();
|
|
705
|
+
expect(files.length).toBeGreaterThan(0);
|
|
706
|
+
return files[0];
|
|
707
|
+
}
|
|
708
|
+
const compactedResult = {
|
|
709
|
+
messages: [
|
|
710
|
+
{ role: "system", content: "system prompt" },
|
|
711
|
+
{ role: "user", content: "[compacted]" },
|
|
712
|
+
],
|
|
713
|
+
result: {
|
|
714
|
+
compacted: true,
|
|
715
|
+
originalCount: 3,
|
|
716
|
+
newCount: 2,
|
|
717
|
+
tokensBeforeEstimate: 500_000,
|
|
718
|
+
tokensAfterEstimate: 2_000,
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
it("defers compaction out of initialize() and runs it on the first prompt", async () => {
|
|
722
|
+
const sessionPath = await persistSession();
|
|
723
|
+
shouldCompactMock.mockReturnValue(true);
|
|
724
|
+
compactMock.mockResolvedValue(compactedResult);
|
|
725
|
+
agentLoopMock.mockImplementation(async function* (messages) {
|
|
726
|
+
messages.push({ role: "assistant", content: "resumed reply" });
|
|
727
|
+
yield { type: "agent_done" };
|
|
728
|
+
});
|
|
729
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
730
|
+
const resumed = new AgentSession({
|
|
731
|
+
provider: "anthropic",
|
|
732
|
+
model: "claude-test",
|
|
733
|
+
cwd: tmpProject,
|
|
734
|
+
systemPrompt: "system prompt",
|
|
735
|
+
sessionId: sessionPath,
|
|
736
|
+
deferLoadCompaction: true,
|
|
737
|
+
});
|
|
738
|
+
await resumed.initialize();
|
|
739
|
+
// Readiness path must NOT have paid for a summary LLM call.
|
|
740
|
+
expect(compactMock).not.toHaveBeenCalled();
|
|
741
|
+
// First prompt triggers runLoop()'s existing pre-run auto-compaction.
|
|
742
|
+
await resumed.prompt("continue");
|
|
743
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
744
|
+
await resumed.dispose();
|
|
745
|
+
});
|
|
746
|
+
it("still compacts inline during initialize() without the flag (CLI resume)", async () => {
|
|
747
|
+
const sessionPath = await persistSession();
|
|
748
|
+
shouldCompactMock.mockReturnValue(true);
|
|
749
|
+
compactMock.mockResolvedValue(compactedResult);
|
|
750
|
+
const { AgentSession } = await import("./agent-session.js");
|
|
751
|
+
const resumed = new AgentSession({
|
|
752
|
+
provider: "anthropic",
|
|
753
|
+
model: "claude-test",
|
|
754
|
+
cwd: tmpProject,
|
|
755
|
+
systemPrompt: "system prompt",
|
|
756
|
+
sessionId: sessionPath,
|
|
757
|
+
});
|
|
758
|
+
await resumed.initialize();
|
|
759
|
+
expect(compactMock).toHaveBeenCalledTimes(1);
|
|
760
|
+
await resumed.dispose();
|
|
761
|
+
});
|
|
762
|
+
});
|
|
252
763
|
//# sourceMappingURL=agent-session-compaction.test.js.map
|