@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.
- package/README.md +27 -4
- package/package.json +1 -1
- package/src/config.ts +36 -40
- package/src/index.ts +332 -264
- package/src/model-switch.ts +17 -17
- package/src/scout-prompt.ts +64 -60
- package/src/short-circuit.test.ts +105 -0
- package/src/short-circuit.ts +170 -0
- package/src/side-agent.ts +65 -65
- package/src/skill-inject.ts +41 -38
- package/src/types.ts +34 -1
package/src/skill-inject.ts
CHANGED
|
@@ -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 =
|
|
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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
systemPrompt: string,
|
|
38
|
+
selectedSkills: string[],
|
|
39
|
+
allSkills: Array<{ name: string; description: string; filePath: string }>,
|
|
39
40
|
): string {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
if (selectedSkills.length === 0) {
|
|
42
|
+
return systemPrompt.replace(SKILLS_SECTION_RE, "");
|
|
43
|
+
}
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
const skillMap = new Map(allSkills.map((s) => [s.name, s]));
|
|
46
|
+
const entries: string[] = [];
|
|
47
|
+
const newlyShown: string[] = [];
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
for (const name of selectedSkills) {
|
|
50
|
+
const skill = skillMap.get(name);
|
|
51
|
+
if (!skill) continue;
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
if (entries.length === 0) {
|
|
66
|
+
return systemPrompt.replace(SKILLS_SECTION_RE, "");
|
|
67
|
+
}
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
// Update cache
|
|
70
|
+
for (const name of newlyShown) {
|
|
71
|
+
shownSkills.add(name);
|
|
72
|
+
}
|
|
70
73
|
|
|
71
|
-
|
|
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
|
-
|
|
76
|
+
return systemPrompt.replace(SKILLS_SECTION_RE, compact);
|
|
74
77
|
}
|
|
75
78
|
|
|
76
79
|
function esc(str: string): string {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
return str
|
|
81
|
+
.replace(/&/g, "&")
|
|
82
|
+
.replace(/</g, "<")
|
|
83
|
+
.replace(/>/g, ">")
|
|
84
|
+
.replace(/"/g, """)
|
|
85
|
+
.replace(/'/g, "'");
|
|
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
|
-
/**
|
|
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
|
};
|