Skip to content

Commit ddb341b

Browse files
author
littlekirkycode
committed
feat(code): add "clear and continue from plan" option in plan mode
Adds "Yes, clear history and continue from plan" option when exiting plan mode. Clears conversation history and re-injects the approved plan as the first prompt. Useful when context window is filling up during planning — lets the agent start fresh with just the plan.
1 parent a0c1945 commit ddb341b

File tree

6 files changed

+116
-2
lines changed

6 files changed

+116
-2
lines changed

apps/code/src/renderer/features/sessions/service/service.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ import {
2525
} from "@features/sessions/stores/sessionStore";
2626
import { useSettingsStore } from "@features/settings/stores/settingsStore";
2727
import { taskViewedApi } from "@features/sidebar/hooks/useTaskViewed";
28+
import { workspaceApi } from "@features/workspace/hooks/useWorkspace";
29+
import { POSTHOG_NOTIFICATIONS } from "@posthog/agent/acp-extensions";
2830
import { DEFAULT_GATEWAY_MODEL } from "@posthog/agent/gateway-models";
2931
import { getIsOnline } from "@renderer/stores/connectivityStore";
3032
import { trpcClient } from "@renderer/trpc/client";
@@ -116,6 +118,8 @@ export class SessionService {
116118
>();
117119
/** AbortController for the current in-flight preview session start */
118120
private previewAbort: AbortController | null = null;
121+
/** Pending plan text to inject after session clear, keyed by taskRunId */
122+
private pendingPlanContinuations = new Map<string, string>();
119123
/** Active cloud task watchers, keyed by taskId */
120124
private cloudTaskWatchers = new Map<
121125
string,
@@ -858,8 +862,35 @@ export class SessionService {
858862

859863
taskViewedApi.markActivity(session.taskId);
860864

861-
// Process queued messages after turn completes - send all as one prompt
862-
if (hasQueuedMessages) {
865+
// Clear-and-continue from plan takes priority over queued messages
866+
const pendingPlan = this.pendingPlanContinuations.get(taskRunId);
867+
if (pendingPlan) {
868+
this.pendingPlanContinuations.delete(taskRunId);
869+
const { taskId } = session;
870+
// Look up repoPath from workspace — session doesn't store it directly
871+
workspaceApi
872+
.get(taskId)
873+
.then((workspace) => {
874+
const repoPath = workspace?.worktreePath ?? workspace?.folderPath;
875+
if (repoPath) {
876+
return this.executeClearAndContinue(
877+
taskId,
878+
repoPath,
879+
pendingPlan,
880+
);
881+
}
882+
log.error("Cannot clear-and-continue: no workspace for task", {
883+
taskId,
884+
});
885+
return undefined;
886+
})
887+
.catch((err) => {
888+
log.error("Failed to clear and continue from plan", {
889+
taskId,
890+
error: err,
891+
});
892+
});
893+
} else if (hasQueuedMessages) {
863894
setTimeout(() => {
864895
this.sendQueuedMessages(session.taskId).catch((err) => {
865896
log.error("Failed to send queued messages", {
@@ -914,6 +945,21 @@ export class SessionService {
914945
});
915946
}
916947
}
948+
949+
// Handle clear_and_continue — store plan text for post-turn injection
950+
// extNotification may double the underscore prefix, so match both forms
951+
if (
952+
"method" in msg &&
953+
(msg.method === POSTHOG_NOTIFICATIONS.CLEAR_AND_CONTINUE ||
954+
msg.method === `_${POSTHOG_NOTIFICATIONS.CLEAR_AND_CONTINUE}`) &&
955+
"params" in msg
956+
) {
957+
const params = msg.params as { plan?: string };
958+
if (params?.plan) {
959+
this.pendingPlanContinuations.set(taskRunId, params.plan);
960+
log.info("Plan continuation queued", { taskRunId });
961+
}
962+
}
917963
}
918964

919965
private handlePermissionRequest(
@@ -1754,6 +1800,22 @@ export class SessionService {
17541800
await this.reconnectInPlace(taskId, repoPath, null, true);
17551801
}
17561802

1803+
/**
1804+
* Clear conversation history and re-inject an approved plan as the first prompt.
1805+
* Used by the "clear and continue from plan" flow.
1806+
*/
1807+
private async executeClearAndContinue(
1808+
taskId: string,
1809+
repoPath: string,
1810+
plan: string,
1811+
): Promise<void> {
1812+
await this.resetSession(taskId, repoPath);
1813+
await this.sendPrompt(
1814+
taskId,
1815+
`Continue implementing this approved plan:\n\n${plan}`,
1816+
);
1817+
}
1818+
17571819
/**
17581820
* Cancel the current backend agent and reconnect under the same taskRunId.
17591821
* Does NOT remove the session from the store (avoids connect effect loop).

packages/agent/package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
"types": "./dist/agent.d.ts",
1313
"import": "./dist/agent.js"
1414
},
15+
"./acp-extensions": {
16+
"types": "./dist/acp-extensions.d.ts",
17+
"import": "./dist/acp-extensions.js"
18+
},
1519
"./gateway-models": {
1620
"types": "./dist/gateway-models.d.ts",
1721
"import": "./dist/gateway-models.js"

packages/agent/src/acp-extensions.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,7 @@ export const POSTHOG_NOTIFICATIONS = {
6464

6565
/** Marks a boundary for log compaction */
6666
COMPACT_BOUNDARY: "_posthog/compact_boundary",
67+
68+
/** Request to clear session history and continue from an approved plan */
69+
CLEAR_AND_CONTINUE: "_posthog/clear_and_continue",
6770
} as const;

packages/agent/src/adapters/claude/permissions/permission-handlers.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
RequestPermissionResponse,
44
} from "@agentclientprotocol/sdk";
55
import type { PermissionUpdate } from "@anthropic-ai/claude-agent-sdk";
6+
import { POSTHOG_NOTIFICATIONS } from "../../../acp-extensions";
67
import { text } from "../../../utils/acp-content";
78
import type { Logger } from "../../../utils/logger";
89
import { toolInfoFromToolUse } from "../conversion/tool-use-to-acp";
@@ -190,6 +191,44 @@ async function applyPlanApproval(
190191
};
191192
}
192193

194+
// Clear history and continue from plan — approve, switch mode, then signal renderer
195+
if (
196+
response.outcome?.outcome === "selected" &&
197+
response.outcome.optionId === "clearAndContinue"
198+
) {
199+
const planText = extractPlanText(updatedInput);
200+
201+
session.permissionMode = "default";
202+
await session.query.setPermissionMode("default");
203+
await context.client.sessionUpdate({
204+
sessionId: context.sessionId,
205+
update: {
206+
sessionUpdate: "current_mode_update",
207+
currentModeId: "default",
208+
},
209+
});
210+
await context.updateConfigOption("mode", "default");
211+
212+
// Signal the renderer to clear the session and re-inject the plan
213+
if (planText) {
214+
await context.client.extNotification(
215+
POSTHOG_NOTIFICATIONS.CLEAR_AND_CONTINUE,
216+
{
217+
sessionId: context.sessionId,
218+
plan: planText,
219+
},
220+
);
221+
}
222+
223+
return {
224+
behavior: "allow",
225+
updatedInput,
226+
updatedPermissions: context.suggestions ?? [
227+
{ type: "setMode", mode: "default", destination: "localSettings" },
228+
],
229+
};
230+
}
231+
193232
const customInput = (response._meta as Record<string, unknown> | undefined)
194233
?.customInput as string | undefined;
195234
const feedback = customInput?.trim();

packages/agent/src/adapters/claude/permissions/permission-options.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,11 @@ export function buildExitPlanModePermissionOptions(): PermissionOption[] {
103103
name: "Yes, and manually approve edits",
104104
optionId: "default",
105105
},
106+
{
107+
kind: "allow_once",
108+
name: "Yes, clear history and continue from plan",
109+
optionId: "clearAndContinue",
110+
},
106111
{
107112
kind: "reject_once",
108113
name: "No, and tell the agent what to do differently",

packages/agent/tsup.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ export default defineConfig([
7272
{
7373
entry: [
7474
"src/index.ts",
75+
"src/acp-extensions.ts",
7576
"src/agent.ts",
7677
"src/gateway-models.ts",
7778
"src/posthog-api.ts",

0 commit comments

Comments
 (0)