@d3ara1n/pi-scout 1.0.2 → 1.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.
@@ -10,14 +10,15 @@
10
10
  */
11
11
 
12
12
  /** Match pi's entire skills section: intro paragraph + XML block. */
13
- const SKILLS_SECTION_RE = /\n\nThe following skills provide specialized instructions[\s\S]*?<\/available_skills>/;
13
+ const SKILLS_SECTION_RE =
14
+ /\n\nThe following skills provide specialized instructions[\s\S]*?<\/available_skills>/;
14
15
 
15
16
  /** Track skill names already shown to the LLM in this session. */
16
17
  let shownSkills: Set<string> = new Set();
17
18
 
18
19
  /** Reset the cache — called on session_start. */
19
20
  export function resetSkillCache(): void {
20
- shownSkills = new Set();
21
+ shownSkills = new Set();
21
22
  }
22
23
 
23
24
  /**
@@ -33,51 +34,53 @@ export function resetSkillCache(): void {
33
34
  * @returns Modified system prompt
34
35
  */
35
36
  export function filterSkillsBlock(
36
- systemPrompt: string,
37
- selectedSkills: string[],
38
- allSkills: Array<{ name: string; description: string; filePath: string }>,
37
+ systemPrompt: string,
38
+ selectedSkills: string[],
39
+ allSkills: Array<{ name: string; description: string; filePath: string }>,
39
40
  ): string {
40
- if (selectedSkills.length === 0) {
41
- return systemPrompt.replace(SKILLS_SECTION_RE, "");
42
- }
41
+ if (selectedSkills.length === 0) {
42
+ return systemPrompt.replace(SKILLS_SECTION_RE, "");
43
+ }
43
44
 
44
- const skillMap = new Map(allSkills.map((s) => [s.name, s]));
45
- const entries: string[] = [];
46
- const newlyShown: string[] = [];
45
+ const skillMap = new Map(allSkills.map((s) => [s.name, s]));
46
+ const entries: string[] = [];
47
+ const newlyShown: string[] = [];
47
48
 
48
- for (const name of selectedSkills) {
49
- const skill = skillMap.get(name);
50
- if (!skill) continue;
49
+ for (const name of selectedSkills) {
50
+ const skill = skillMap.get(name);
51
+ if (!skill) continue;
51
52
 
52
- if (shownSkills.has(name)) {
53
- // Already introduced — compact form
54
- entries.push(` <skill name="${esc(skill.name)}" location="${esc(skill.filePath)}" />`);
55
- } else {
56
- // First time — include description
57
- entries.push(` <skill name="${esc(skill.name)}" location="${esc(skill.filePath)}">${esc(skill.description)}</skill>`);
58
- newlyShown.push(name);
59
- }
60
- }
53
+ if (shownSkills.has(name)) {
54
+ // Already introduced — compact form
55
+ entries.push(` <skill name="${esc(skill.name)}" location="${esc(skill.filePath)}" />`);
56
+ } else {
57
+ // First time — include description
58
+ entries.push(
59
+ ` <skill name="${esc(skill.name)}" location="${esc(skill.filePath)}">${esc(skill.description)}</skill>`,
60
+ );
61
+ newlyShown.push(name);
62
+ }
63
+ }
61
64
 
62
- if (entries.length === 0) {
63
- return systemPrompt.replace(SKILLS_SECTION_RE, "");
64
- }
65
+ if (entries.length === 0) {
66
+ return systemPrompt.replace(SKILLS_SECTION_RE, "");
67
+ }
65
68
 
66
- // Update cache
67
- for (const name of newlyShown) {
68
- shownSkills.add(name);
69
- }
69
+ // Update cache
70
+ for (const name of newlyShown) {
71
+ shownSkills.add(name);
72
+ }
70
73
 
71
- const compact = `\n\nActive skills (use \`read\` to load a skill's file):\n<available_skills>\n${entries.join("\n")}\n</available_skills>`;
74
+ const compact = `\n\nActive skills (use \`read\` to load a skill's file):\n<available_skills>\n${entries.join("\n")}\n</available_skills>`;
72
75
 
73
- return systemPrompt.replace(SKILLS_SECTION_RE, compact);
76
+ return systemPrompt.replace(SKILLS_SECTION_RE, compact);
74
77
  }
75
78
 
76
79
  function esc(str: string): string {
77
- return str
78
- .replace(/&/g, "&amp;")
79
- .replace(/</g, "&lt;")
80
- .replace(/>/g, "&gt;")
81
- .replace(/"/g, "&quot;")
82
- .replace(/'/g, "&apos;");
80
+ return str
81
+ .replace(/&/g, "&amp;")
82
+ .replace(/</g, "&lt;")
83
+ .replace(/>/g, "&gt;")
84
+ .replace(/"/g, "&quot;")
85
+ .replace(/'/g, "&apos;");
83
86
  }
package/src/types.ts CHANGED
@@ -14,10 +14,35 @@ export interface ScoutConfig {
14
14
  modules: {
15
15
  skillRouter: boolean;
16
16
  modelRouter: boolean;
17
+ /** Short-circuit layer: skip the side LLM on high-confidence prompts */
18
+ shortCircuit: boolean;
17
19
  };
20
+ /** Tuning for the short-circuit module */
21
+ shortCircuit: ShortCircuitConfig;
18
22
  }
19
23
 
20
- /** Decision returned by the side agent. */
24
+ /**
25
+ * Tuning for the short-circuit module.
26
+ *
27
+ * The short-circuit layer lets scout skip the side model entirely on
28
+ * trivial acknowledgments ("好的" / "ok" / "はい"). A trivial ack means
29
+ * "no skills, don't switch models" — both module answers are certain, so
30
+ * skipping the side model is always safe, even with model-router on.
31
+ * Ambiguous prompts always fall through to the side model, so there is no
32
+ * quality loss on hard cases.
33
+ */
34
+ export interface ShortCircuitConfig {
35
+ /** Enable the trivial-acknowledgment rule */
36
+ trivialAck: boolean;
37
+ /** Max prompt length (chars) for the trivial-ack rule. Longer prompts are
38
+ * never short-circuited as acks even if they begin with an ack word. */
39
+ maxAckLength: number;
40
+ /** Additional ack phrases merged on top of the built-in 中/英/日/韓 table.
41
+ * Provide raw strings; they are normalized (trimmed, lowercased) at runtime. */
42
+ ackPhrases: string[];
43
+ }
44
+
45
+ /** Decision returned by the side agent or the short-circuit layer. */
21
46
  export interface ScoutDecision {
22
47
  /** Selected skill names */
23
48
  skills: string[];
@@ -25,6 +50,8 @@ export interface ScoutDecision {
25
50
  role: string | null;
26
51
  /** Brief reasoning */
27
52
  reasoning: string;
53
+ /** Where the decision came from — controls status-bar presentation */
54
+ source?: "side-agent" | "short-circuit";
28
55
  }
29
56
 
30
57
  export const DEFAULT_CONFIG: ScoutConfig = {
@@ -34,5 +61,11 @@ export const DEFAULT_CONFIG: ScoutConfig = {
34
61
  modules: {
35
62
  skillRouter: true,
36
63
  modelRouter: false,
64
+ shortCircuit: true,
65
+ },
66
+ shortCircuit: {
67
+ trivialAck: true,
68
+ maxAckLength: 12,
69
+ ackPhrases: [],
37
70
  },
38
71
  };