Skip to content

Commit b26540b

Browse files
committed
feat: add walkthrough as subagent
1 parent edb0aa2 commit b26540b

File tree

25 files changed

+499
-15
lines changed

25 files changed

+499
-15
lines changed

.pochi/agents/walkthroughs.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
---
2+
name: walkthroughs
3+
description: Create or update walkthrough summaries for a task when the user requests a walkthrough or project recap.
4+
tools: readFile, writeToFile, listFiles
5+
---
6+
7+
You are a walkthrough authoring agent. Your job is to produce a concise Markdown walkthrough that helps a new reader get up to speed with the work completed in a task.
8+
9+
When invoked:
10+
1. Identify the task id and target path. If the prompt provides an absolute path, use it; otherwise use `pochi/walkthroughs/$taskId.md`.
11+
2. Review the conversation and tool outputs for requirements and actual changes.
12+
3. Summarize key work items, notable file changes, and any commands run.
13+
4. Include outcomes and any remaining follow-ups or known gaps.
14+
5. If references to screenshots or recordings are available, include them as links or notes.
15+
6. Write the walkthrough to the target path using `writeToFile`.
16+
17+
Output rules:
18+
- Write Markdown to the target file only (use `writeToFile`).
19+
- After finishing, call `attemptCompletion` with: "Walkthrough created at <path>".
20+
- Do not include any other response text outside the tool call.
21+
- If you need to check whether the walkthrough file exists, try `readFile` on the target path; if it fails, proceed as a new file.
22+
- Prefer short sections with bullets over long paragraphs.
23+
- Keep it concise and actionable.
24+
- If an existing walkthrough file is present, append an "Update" section rather than overwriting.

packages/common/src/base/constants.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ export const KnownTags = [
77
] as const;
88
export const CompactTaskMinTokens = 50_000;
99

10+
// Built-in agent names
11+
export const WALKTHROUGH_AGENT_NAME = "walkthroughs";
12+
export const EXPLORE_AGENT_NAME = "explore";
13+
export const DEBUGGER_AGENT_NAME = "debugger";
14+
1015
export const WorkspaceWorkflowPathSegments = [".pochi", "workflows"];
1116

1217
export const DefaultContextWindow = 100_000;

packages/common/src/base/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ export { prompts } from "./prompts";
77

88
export { SocialLinks } from "./social";
99
export * as constants from "./constants";
10+
export {
11+
WALKTHROUGH_AGENT_NAME,
12+
EXPLORE_AGENT_NAME,
13+
DEBUGGER_AGENT_NAME,
14+
} from "./constants";
1015

1116
export {
1217
Environment,

packages/common/src/vscode-webui-bridge/webview-stub.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,9 @@ const VSCodeHostStub = {
173173
openExternal: (_uri: string): Promise<void> => {
174174
return Promise.resolve();
175175
},
176+
checkFileExists: async (_path: string): Promise<boolean> => {
177+
return Promise.resolve(false);
178+
},
176179
readMinionId: async () => {
177180
return Promise.resolve(null);
178181
},

packages/common/src/vscode-webui-bridge/webview.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,13 @@ export interface VSCodeHostApi {
223223
*/
224224
openExternal(uri: string): Promise<void>;
225225

226+
/**
227+
* Checks if a file exists at the given path.
228+
* @param path - The file path to check.
229+
* @returns A promise that resolves to true if the file exists, false otherwise.
230+
*/
231+
checkFileExists(path: string): Promise<boolean>;
232+
226233
/**
227234
* Saves a checkpoint with the given message.
228235
* @param message - The message to save as a checkpoint.

packages/livekit/src/chat/middlewares/new-task-middleware.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import type {
33
LanguageModelV2StreamPart,
44
} from "@ai-sdk/provider";
55
import { safeParseJSON } from "@ai-sdk/provider-utils";
6+
import { WALKTHROUGH_AGENT_NAME } from "@getpochi/common";
67
import { type CustomAgent, newTaskInputSchema } from "@getpochi/tools";
78
import type { Store } from "@livestore/livestore";
89
import { InvalidToolInputError } from "ai";
10+
import { makeMessagesQuery } from "../../livestore/default-queries";
911
import { events } from "../../livestore/default-schema";
1012

1113
export function createNewTaskMiddleware(
@@ -14,6 +16,17 @@ export function createNewTaskMiddleware(
1416
parentTaskId: string,
1517
customAgents?: CustomAgent[],
1618
): LanguageModelV2Middleware {
19+
const builtinAgentDefaults: Record<
20+
string,
21+
{
22+
inheritParentMessages?: boolean;
23+
}
24+
> = {
25+
[WALKTHROUGH_AGENT_NAME]: {
26+
inheritParentMessages: true,
27+
},
28+
};
29+
1730
return {
1831
middlewareVersion: "v2",
1932
transformParams: async ({ params }) => {
@@ -95,13 +108,25 @@ export function createNewTaskMiddleware(
95108
args._meta = {
96109
uid,
97110
};
111+
const agentType = args.agentType ?? "";
112+
const shouldInheritParentMessages =
113+
args.inheritParentMessages ??
114+
builtinAgentDefaults[agentType]?.inheritParentMessages ??
115+
false;
116+
const parentMessages = shouldInheritParentMessages
117+
? store.query(makeMessagesQuery(parentTaskId)).map((x) => ({
118+
...x.data,
119+
id: crypto.randomUUID(),
120+
}))
121+
: [];
98122
store.commit(
99123
events.taskInited({
100124
id: uid,
101125
cwd,
102126
parentId: parentTaskId,
103127
createdAt: new Date(),
104128
initMessages: [
129+
...parentMessages,
105130
{
106131
id: crypto.randomUUID(),
107132
role: "user",
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function getWalkthroughPath(taskId: string): string {
2+
return `pochi/walkthroughs/${taskId}.md`;
3+
}

packages/livekit/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export type {
1111

1212
export const StoreBlobProtocol = "store-blob:";
1313
export { processContentOutput, fileToUri } from "./store-blob";
14+
export { getWalkthroughPath } from "./chat/walkthrough";

packages/tools/src/new-task.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ export const inputSchema = z.object({
6464
.string()
6565
.optional()
6666
.describe("The type of the specialized agent to use for the task."),
67+
inheritParentMessages: z
68+
.boolean()
69+
.optional()
70+
.describe("Whether to inherit the parent task's messages."),
6771
_meta: z
6872
.object({
6973
uid: z.string().describe("A unique identifier for the task."),

packages/vscode-webui/src/components/message/message-list.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export const MessageList: React.FC<{
4747
showLoader?: boolean;
4848
forkTask?: (commitId: string, messageId?: string) => Promise<void>;
4949
hideCheckPoint?: boolean;
50+
taskId?: string;
5051
}> = ({
5152
messages: renderMessages,
5253
isLoading,
@@ -58,6 +59,7 @@ export const MessageList: React.FC<{
5859
showLoader = true,
5960
forkTask,
6061
hideCheckPoint,
62+
taskId,
6163
}) => {
6264
const [debouncedIsLoading, setDebouncedIsLoading] = useDebounceState(
6365
isLoading,
@@ -139,6 +141,7 @@ export const MessageList: React.FC<{
139141
hideCheckPoint={hideCheckPoint}
140142
latestCheckpoint={latestCheckpoint}
141143
lastCheckpointInMessage={lastCheckpointInMessage}
144+
taskId={taskId}
142145
/>
143146
))}
144147
</div>
@@ -200,6 +203,7 @@ function Part({
200203
hideCheckPoint,
201204
latestCheckpoint,
202205
lastCheckpointInMessage,
206+
taskId,
203207
}: {
204208
role: Message["role"];
205209
partIndex: number;
@@ -212,6 +216,7 @@ function Part({
212216
hideCheckPoint?: boolean;
213217
latestCheckpoint: string | null;
214218
lastCheckpointInMessage: string | undefined;
219+
taskId?: string;
215220
}) {
216221
const paddingClass = partIndex === 0 ? "" : "mt-2";
217222
if (part.type === "text") {
@@ -261,6 +266,7 @@ function Part({
261266
isLoading={isLoading}
262267
changes={getToolCallCheckpoint(part, messages)}
263268
messages={messages}
269+
taskId={taskId}
264270
/>
265271
);
266272
}

0 commit comments

Comments
 (0)