@d3ara1n/pi-scout 0.1.0 → 0.1.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d3ara1n/pi-scout",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Per-turn side agent decision framework for pi — uses a cheap model to select skills and route models before each conversation turn",
5
5
  "main": "src/index.ts",
6
6
  "keywords": [
package/src/index.ts CHANGED
@@ -198,21 +198,21 @@ export default function scoutExtension(pi: ExtensionAPI) {
198
198
  ? (rolesApi.findRoleByModel(`${currentModel.provider}/${currentModel.id}`) ?? "unknown")
199
199
  : "unknown";
200
200
 
201
- // 3. Call side agent
202
- const scoutSystemPrompt = buildScoutSystemPrompt(config);
201
+ // 3. Build roles list
203
202
  const visibleRoles = rolesApi.getVisibleRoles();
204
203
  const rolesList = Object.entries(visibleRoles)
205
204
  .map(([name, cfg]: [string, any]) => `- ${name}: ${cfg.description ?? "(no description)"}${cfg.model ? ` (model: ${cfg.model})` : " (current model)"}`)
206
205
  .join("\n");
206
+
207
+ // 4. Call side agent
208
+ const scoutSystemPrompt = buildScoutSystemPrompt(config, skillsList, rolesList);
207
209
  const decision = await callSideAgent(
208
210
  sideResolved.model,
209
211
  sideResolved.apiKey,
210
212
  sideResolved.headers,
211
213
  scoutSystemPrompt,
212
214
  event.prompt,
213
- skillsList,
214
215
  currentRole,
215
- rolesList,
216
216
  );
217
217
 
218
218
  lastDecision = decision;
@@ -6,31 +6,34 @@ import type { ScoutConfig } from "./types.ts";
6
6
 
7
7
  /**
8
8
  * Build the user message for the side agent.
9
+ * Only contains per-turn variable content — stable data lives in the system prompt
10
+ * so it can be prompt-cached across turns.
9
11
  */
10
12
  export function buildScoutUserMessage(
11
13
  userPrompt: string,
12
- skillsList: string,
13
14
  currentRole: string,
14
- rolesList: string,
15
15
  ): string {
16
16
  return [
17
- `User prompt:`,
18
- userPrompt,
19
- ``,
20
- `Available skills:`,
21
- skillsList || "(none)",
22
- ``,
23
17
  `Current role: ${currentRole}`,
24
18
  ``,
25
- `Available roles:`,
26
- rolesList,
19
+ `User prompt:`,
20
+ userPrompt,
27
21
  ].join("\n");
28
22
  }
29
23
 
30
24
  /**
31
25
  * Build the system prompt for the side agent.
26
+ *
27
+ * Stable per-session data (skills, roles) is embedded here rather than in the
28
+ * user message so that the entire system prompt forms a large, cacheable prefix.
29
+ * This is critical for Anthropic which requires a 1024-token minimum for
30
+ * prompt caching to activate.
32
31
  */
33
- export function buildScoutSystemPrompt(config: ScoutConfig): string {
32
+ export function buildScoutSystemPrompt(
33
+ config: ScoutConfig,
34
+ skillsList: string,
35
+ rolesList: string,
36
+ ): string {
34
37
  const parts: string[] = [];
35
38
 
36
39
  parts.push(`You are a scout. Analyze the user's request and decide which skills and model role to use.`);
@@ -59,5 +62,13 @@ export function buildScoutSystemPrompt(config: ScoutConfig): string {
59
62
  parts.push(`- IMPORTANT: skill routing is disabled. Always return skills: [].`);
60
63
  }
61
64
 
65
+ parts.push(``);
66
+ parts.push(`## Available Skills`);
67
+ parts.push(skillsList || "(none)");
68
+
69
+ parts.push(``);
70
+ parts.push(`## Available Roles`);
71
+ parts.push(rolesList);
72
+
62
73
  return parts.join("\n");
63
74
  }
package/src/side-agent.ts CHANGED
@@ -21,9 +21,8 @@ interface SideAgentContext {
21
21
  * @param sideModel - The Model instance to use (from pi-model-roles "side" role)
22
22
  * @param apiKey - API key for the side model
23
23
  * @param headers - Custom headers for the side model
24
- * @param systemPrompt - Scout system prompt
24
+ * @param systemPrompt - Scout system prompt (includes skills/roles for cache friendliness)
25
25
  * @param userPrompt - The user's original prompt text
26
- * @param skillsList - Formatted list of available skills for the prompt
27
26
  * @param currentRole - Current active role name
28
27
  * @returns Parsed ScoutDecision, or a safe fallback on error
29
28
  */
@@ -33,9 +32,7 @@ export async function callSideAgent(
33
32
  headers: Record<string, string> | undefined,
34
33
  systemPrompt: string,
35
34
  userPrompt: string,
36
- skillsList: string,
37
35
  currentRole: string,
38
- rolesList: string,
39
36
  ): Promise<ScoutDecision> {
40
37
  const fallback: ScoutDecision = { skills: [], role: null, reasoning: "side agent error" };
41
38
 
@@ -44,13 +41,14 @@ export async function callSideAgent(
44
41
  messages: [
45
42
  {
46
43
  role: "user",
47
- content: buildScoutUserMessage(userPrompt, skillsList, currentRole, rolesList),
44
+ content: buildScoutUserMessage(userPrompt, currentRole),
48
45
  },
49
46
  ],
50
47
  };
51
48
 
52
49
  const options: Record<string, any> = {
53
50
  maxTokens: 256,
51
+ cacheRetention: "short",
54
52
  };
55
53
 
56
54
  if (apiKey) options.apiKey = apiKey;
package/src/types.ts CHANGED
@@ -29,7 +29,7 @@ export interface ScoutDecision {
29
29
 
30
30
  export const DEFAULT_CONFIG: ScoutConfig = {
31
31
  enabled: true,
32
- sideAgentRole: "fast",
32
+ sideAgentRole: "utility",
33
33
  maxSelectedSkills: 5,
34
34
  modules: {
35
35
  skillRouter: true,