@caupulican/pi-adaptative 0.81.2 → 0.81.3
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/CHANGELOG.md +15 -0
- package/dist/core/agent-session.d.ts +1 -23
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +117 -131
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/background-lane-controller.d.ts +2 -0
- package/dist/core/background-lane-controller.d.ts.map +1 -1
- package/dist/core/background-lane-controller.js +3 -0
- package/dist/core/background-lane-controller.js.map +1 -1
- package/dist/core/billing-failover-controller.d.ts +31 -0
- package/dist/core/billing-failover-controller.d.ts.map +1 -0
- package/dist/core/billing-failover-controller.js +72 -0
- package/dist/core/billing-failover-controller.js.map +1 -0
- package/dist/core/billing-failover.d.ts +25 -0
- package/dist/core/billing-failover.d.ts.map +1 -0
- package/dist/core/billing-failover.js +21 -0
- package/dist/core/billing-failover.js.map +1 -0
- package/dist/core/context-pipeline.d.ts +1 -0
- package/dist/core/context-pipeline.d.ts.map +1 -1
- package/dist/core/context-pipeline.js +51 -14
- package/dist/core/context-pipeline.js.map +1 -1
- package/dist/core/model-router/config-diagnostics.d.ts +1 -1
- package/dist/core/model-router/config-diagnostics.d.ts.map +1 -1
- package/dist/core/model-router/config-diagnostics.js +29 -1
- package/dist/core/model-router/config-diagnostics.js.map +1 -1
- package/dist/core/model-router/fitness-gate.d.ts +23 -0
- package/dist/core/model-router/fitness-gate.d.ts.map +1 -0
- package/dist/core/model-router/fitness-gate.js +33 -0
- package/dist/core/model-router/fitness-gate.js.map +1 -0
- package/dist/core/model-router/status.d.ts +25 -1
- package/dist/core/model-router/status.d.ts.map +1 -1
- package/dist/core/model-router/status.js +27 -4
- package/dist/core/model-router/status.js.map +1 -1
- package/dist/core/model-router-controller.d.ts +10 -1
- package/dist/core/model-router-controller.d.ts.map +1 -1
- package/dist/core/model-router-controller.js +108 -27
- package/dist/core/model-router-controller.js.map +1 -1
- package/dist/core/runtime-builder.d.ts +16 -0
- package/dist/core/runtime-builder.d.ts.map +1 -1
- package/dist/core/runtime-builder.js +88 -1
- package/dist/core/runtime-builder.js.map +1 -1
- package/dist/core/scout-controller.d.ts +44 -0
- package/dist/core/scout-controller.d.ts.map +1 -0
- package/dist/core/scout-controller.js +165 -0
- package/dist/core/scout-controller.js.map +1 -0
- package/dist/core/settings-manager.d.ts +16 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +35 -3
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/tools/context-scout.d.ts +19 -0
- package/dist/core/tools/context-scout.d.ts.map +1 -0
- package/dist/core/tools/context-scout.js +47 -0
- package/dist/core/tools/context-scout.js.map +1 -0
- package/dist/core/tools/index.d.ts +1 -0
- package/dist/core/tools/index.d.ts.map +1 -1
- package/dist/core/tools/index.js +1 -0
- package/dist/core/tools/index.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/settings-selector.js +77 -7
- package/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/dist/modes/interactive/local-model-commands.d.ts +5 -2
- package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
- package/dist/modes/interactive/local-model-commands.js +115 -1
- package/dist/modes/interactive/local-model-commands.js.map +1 -1
- package/docs/index.md +1 -0
- package/docs/providers.md +2 -0
- package/docs/scout.md +71 -0
- package/docs/settings.md +14 -3
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/sdk/12-full-control.ts +1 -1
- package/npm-shrinkwrap.json +12 -12
- package/package.json +4 -4
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { Agent } from "@caupulican/pi-agent-core";
|
|
2
|
+
export const SCOUT_SYSTEM_PROMPT = `You are a repository scout. You explore a codebase with read-only tools and return compact evidence. You do NOT solve tasks, write code, or modify anything.
|
|
3
|
+
|
|
4
|
+
TOOLS: read (file contents), grep (search file contents), find (locate files by glob/name).
|
|
5
|
+
- Issue INDEPENDENT tool calls in parallel in the same turn whenever possible.
|
|
6
|
+
- Prefer grep/find to narrow, then read only the decisive regions.
|
|
7
|
+
|
|
8
|
+
OUTPUT CONTRACT: when you have enough evidence (or hit your turn budget), reply with:
|
|
9
|
+
<final_answer>
|
|
10
|
+
<one-to-three-sentence summary of what you found>
|
|
11
|
+
path/to/file.ts:START-END
|
|
12
|
+
path/to/other.ts:START-END
|
|
13
|
+
</final_answer>
|
|
14
|
+
- Every path MUST be one you actually observed via a tool result this run.
|
|
15
|
+
- Line ranges point at the decisive code, not whole files.
|
|
16
|
+
- If you found nothing relevant, say so inside <final_answer> with zero citations.
|
|
17
|
+
Turn budget: {MAX_TURNS} turns. Be fast; be precise; cite, don't paste.`;
|
|
18
|
+
const MAX_TURNS_DEFAULT = 8;
|
|
19
|
+
const MAX_TURNS_HARD_CAP = 12;
|
|
20
|
+
const MAX_OUTPUT_TOKENS = 30_000;
|
|
21
|
+
const READ_ONLY_TOOL_NAMES = new Set(["read", "grep", "find"]);
|
|
22
|
+
export class ScoutController {
|
|
23
|
+
deps;
|
|
24
|
+
constructor(deps) {
|
|
25
|
+
this.deps = deps;
|
|
26
|
+
}
|
|
27
|
+
async run(query, maxTurns = MAX_TURNS_DEFAULT) {
|
|
28
|
+
const turnLimit = clampTurns(maxTurns);
|
|
29
|
+
const unavailable = emptyResult();
|
|
30
|
+
if (this.deps.signal?.aborted) {
|
|
31
|
+
return { ...unavailable, failure: "aborted" };
|
|
32
|
+
}
|
|
33
|
+
let modelResolution;
|
|
34
|
+
try {
|
|
35
|
+
modelResolution = await this.deps.resolveScoutModel();
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
return { ...unavailable, failure: error instanceof Error ? error.message : String(error) };
|
|
39
|
+
}
|
|
40
|
+
if ("failure" in modelResolution) {
|
|
41
|
+
return { ...unavailable, failure: modelResolution.failure };
|
|
42
|
+
}
|
|
43
|
+
let turnsUsed = 0;
|
|
44
|
+
let truncated = false;
|
|
45
|
+
let lastAssistantText = "";
|
|
46
|
+
let outputTokens = 0;
|
|
47
|
+
const agent = new Agent({
|
|
48
|
+
initialState: {
|
|
49
|
+
model: modelResolution.model,
|
|
50
|
+
systemPrompt: SCOUT_SYSTEM_PROMPT.replace("{MAX_TURNS}", String(turnLimit)),
|
|
51
|
+
tools: this.deps
|
|
52
|
+
.buildReadOnlyTools(this.deps.getCwd())
|
|
53
|
+
.filter((tool) => READ_ONLY_TOOL_NAMES.has(tool.name)),
|
|
54
|
+
},
|
|
55
|
+
streamFn: this.deps.streamFn,
|
|
56
|
+
getApiKey: () => modelResolution.apiKey,
|
|
57
|
+
maxStallTurns: turnLimit,
|
|
58
|
+
});
|
|
59
|
+
const unsubscribe = agent.subscribe((event) => {
|
|
60
|
+
if (event.type !== "message_end" || event.message.role !== "assistant") {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
turnsUsed += 1;
|
|
64
|
+
lastAssistantText = assistantText(event.message);
|
|
65
|
+
outputTokens += event.message.usage?.totalTokens ?? 0;
|
|
66
|
+
this.deps.onEvent?.({ type: "scout_turn", detail: `turn ${turnsUsed}` });
|
|
67
|
+
if (hasFinalAnswer(lastAssistantText)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (turnsUsed >= turnLimit || outputTokens >= MAX_OUTPUT_TOKENS) {
|
|
71
|
+
truncated = true;
|
|
72
|
+
agent.abort();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
const abortScout = () => {
|
|
76
|
+
truncated = true;
|
|
77
|
+
agent.abort();
|
|
78
|
+
};
|
|
79
|
+
this.deps.signal?.addEventListener("abort", abortScout, { once: true });
|
|
80
|
+
let runFailure;
|
|
81
|
+
try {
|
|
82
|
+
await agent.prompt(query);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (!truncated && !this.deps.signal?.aborted) {
|
|
86
|
+
runFailure = error instanceof Error ? error.message : String(error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
unsubscribe();
|
|
91
|
+
this.deps.signal?.removeEventListener("abort", abortScout);
|
|
92
|
+
}
|
|
93
|
+
const result = parseScoutAnswer(lastAssistantText, (path) => this.deps.fileExists(path), (path) => this.deps.countLines(path));
|
|
94
|
+
const finalResult = {
|
|
95
|
+
...result,
|
|
96
|
+
truncated: truncated || !hasFinalAnswer(lastAssistantText),
|
|
97
|
+
turnsUsed,
|
|
98
|
+
failure: this.deps.signal?.aborted ? "aborted" : runFailure,
|
|
99
|
+
};
|
|
100
|
+
this.deps.onEvent?.({ type: "scout_end", detail: finalResult.failure ?? "ok" });
|
|
101
|
+
return finalResult;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export function parseScoutAnswer(text, fileExists, countLines) {
|
|
105
|
+
const finalAnswer = /<final_answer>([\s\S]*?)<\/final_answer>/i.exec(text)?.[1] ?? text;
|
|
106
|
+
const summaryLines = [];
|
|
107
|
+
const citations = [];
|
|
108
|
+
for (const rawLine of finalAnswer.split(/\r?\n/)) {
|
|
109
|
+
const line = rawLine.trim();
|
|
110
|
+
if (!line)
|
|
111
|
+
continue;
|
|
112
|
+
const citation = parseCitationLine(line, fileExists, countLines);
|
|
113
|
+
if (citation) {
|
|
114
|
+
citations.push(citation);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
summaryLines.push(line);
|
|
118
|
+
}
|
|
119
|
+
const droppedCitations = citations.filter((citation) => !citation.valid).length;
|
|
120
|
+
return {
|
|
121
|
+
summary: summaryLines.join("\n"),
|
|
122
|
+
citations,
|
|
123
|
+
droppedCitations,
|
|
124
|
+
unreliable: citations.length > 0 && droppedCitations / citations.length > 0.5,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function parseCitationLine(line, fileExists, countLines) {
|
|
128
|
+
const match = /^(.+?):(\d+)(?:-(\d+))?$/.exec(line);
|
|
129
|
+
if (!match)
|
|
130
|
+
return undefined;
|
|
131
|
+
const path = match[1].trim();
|
|
132
|
+
const start = Number.parseInt(match[2], 10);
|
|
133
|
+
const end = Number.parseInt(match[3] ?? match[2], 10);
|
|
134
|
+
const lineCount = countLines(path);
|
|
135
|
+
const valid = fileExists(path) &&
|
|
136
|
+
Number.isFinite(start) &&
|
|
137
|
+
Number.isFinite(end) &&
|
|
138
|
+
start >= 1 &&
|
|
139
|
+
end >= start &&
|
|
140
|
+
(lineCount === undefined || end <= lineCount);
|
|
141
|
+
return { path, start, end, valid };
|
|
142
|
+
}
|
|
143
|
+
function clampTurns(maxTurns) {
|
|
144
|
+
if (!Number.isFinite(maxTurns))
|
|
145
|
+
return MAX_TURNS_DEFAULT;
|
|
146
|
+
return Math.min(MAX_TURNS_HARD_CAP, Math.max(1, Math.trunc(maxTurns)));
|
|
147
|
+
}
|
|
148
|
+
function emptyResult() {
|
|
149
|
+
return { summary: "", citations: [], droppedCitations: 0, unreliable: false, truncated: false, turnsUsed: 0 };
|
|
150
|
+
}
|
|
151
|
+
function hasFinalAnswer(text) {
|
|
152
|
+
return /<final_answer>[\s\S]*?<\/final_answer>/i.test(text);
|
|
153
|
+
}
|
|
154
|
+
function assistantText(message) {
|
|
155
|
+
if (!Array.isArray(message.content))
|
|
156
|
+
return "";
|
|
157
|
+
return message.content
|
|
158
|
+
.filter((content) => Boolean(content &&
|
|
159
|
+
typeof content === "object" &&
|
|
160
|
+
content.type === "text" &&
|
|
161
|
+
typeof content.text === "string"))
|
|
162
|
+
.map((content) => content.text)
|
|
163
|
+
.join("\n");
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=scout-controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scout-controller.js","sourceRoot":"","sources":["../../src/core/scout-controller.ts"],"names":[],"mappings":"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","sourcesContent":["import { Agent, type AgentTool, type StreamFn } from \"@caupulican/pi-agent-core\";\nimport type { Model } from \"@caupulican/pi-ai\";\n\nexport const SCOUT_SYSTEM_PROMPT = `You are a repository scout. You explore a codebase with read-only tools and return compact evidence. You do NOT solve tasks, write code, or modify anything.\n\nTOOLS: read (file contents), grep (search file contents), find (locate files by glob/name).\n- Issue INDEPENDENT tool calls in parallel in the same turn whenever possible.\n- Prefer grep/find to narrow, then read only the decisive regions.\n\nOUTPUT CONTRACT: when you have enough evidence (or hit your turn budget), reply with:\n<final_answer>\n<one-to-three-sentence summary of what you found>\npath/to/file.ts:START-END\npath/to/other.ts:START-END\n</final_answer>\n- Every path MUST be one you actually observed via a tool result this run.\n- Line ranges point at the decisive code, not whole files.\n- If you found nothing relevant, say so inside <final_answer> with zero citations.\nTurn budget: {MAX_TURNS} turns. Be fast; be precise; cite, don't paste.`;\n\nexport interface ScoutCitation {\n\tpath: string;\n\tstart: number;\n\tend: number;\n\tvalid: boolean;\n}\n\nexport interface ScoutRunResult {\n\tsummary: string;\n\tcitations: ScoutCitation[];\n\tdroppedCitations: number;\n\tunreliable: boolean;\n\ttruncated: boolean;\n\tturnsUsed: number;\n\tfailure?: string;\n}\n\nexport interface ScoutControllerDeps {\n\tresolveScoutModel(): Promise<\n\t\t{ model: Model<any>; apiKey?: string; headers?: Record<string, string> } | { failure: string }\n\t>;\n\tgetCwd(): string;\n\tbuildReadOnlyTools(cwd: string): AgentTool<any>[];\n\tstreamFn?: StreamFn;\n\tfileExists(path: string): boolean;\n\tcountLines(path: string): number | undefined;\n\tonEvent?(event: { type: \"scout_turn\" | \"scout_end\"; detail: string }): void;\n\tsignal?: AbortSignal;\n}\n\nconst MAX_TURNS_DEFAULT = 8;\nconst MAX_TURNS_HARD_CAP = 12;\nconst MAX_OUTPUT_TOKENS = 30_000;\nconst READ_ONLY_TOOL_NAMES = new Set([\"read\", \"grep\", \"find\"]);\n\nexport class ScoutController {\n\tprivate readonly deps: ScoutControllerDeps;\n\n\tconstructor(deps: ScoutControllerDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tasync run(query: string, maxTurns = MAX_TURNS_DEFAULT): Promise<ScoutRunResult> {\n\t\tconst turnLimit = clampTurns(maxTurns);\n\t\tconst unavailable = emptyResult();\n\t\tif (this.deps.signal?.aborted) {\n\t\t\treturn { ...unavailable, failure: \"aborted\" };\n\t\t}\n\n\t\tlet modelResolution: Awaited<ReturnType<ScoutControllerDeps[\"resolveScoutModel\"]>>;\n\t\ttry {\n\t\t\tmodelResolution = await this.deps.resolveScoutModel();\n\t\t} catch (error) {\n\t\t\treturn { ...unavailable, failure: error instanceof Error ? error.message : String(error) };\n\t\t}\n\t\tif (\"failure\" in modelResolution) {\n\t\t\treturn { ...unavailable, failure: modelResolution.failure };\n\t\t}\n\n\t\tlet turnsUsed = 0;\n\t\tlet truncated = false;\n\t\tlet lastAssistantText = \"\";\n\t\tlet outputTokens = 0;\n\t\tconst agent = new Agent({\n\t\t\tinitialState: {\n\t\t\t\tmodel: modelResolution.model,\n\t\t\t\tsystemPrompt: SCOUT_SYSTEM_PROMPT.replace(\"{MAX_TURNS}\", String(turnLimit)),\n\t\t\t\ttools: this.deps\n\t\t\t\t\t.buildReadOnlyTools(this.deps.getCwd())\n\t\t\t\t\t.filter((tool) => READ_ONLY_TOOL_NAMES.has(tool.name)),\n\t\t\t},\n\t\t\tstreamFn: this.deps.streamFn,\n\t\t\tgetApiKey: () => modelResolution.apiKey,\n\t\t\tmaxStallTurns: turnLimit,\n\t\t});\n\n\t\tconst unsubscribe = agent.subscribe((event) => {\n\t\t\tif (event.type !== \"message_end\" || event.message.role !== \"assistant\") {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tturnsUsed += 1;\n\t\t\tlastAssistantText = assistantText(event.message);\n\t\t\toutputTokens += event.message.usage?.totalTokens ?? 0;\n\t\t\tthis.deps.onEvent?.({ type: \"scout_turn\", detail: `turn ${turnsUsed}` });\n\t\t\tif (hasFinalAnswer(lastAssistantText)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (turnsUsed >= turnLimit || outputTokens >= MAX_OUTPUT_TOKENS) {\n\t\t\t\ttruncated = true;\n\t\t\t\tagent.abort();\n\t\t\t}\n\t\t});\n\n\t\tconst abortScout = (): void => {\n\t\t\ttruncated = true;\n\t\t\tagent.abort();\n\t\t};\n\t\tthis.deps.signal?.addEventListener(\"abort\", abortScout, { once: true });\n\t\tlet runFailure: string | undefined;\n\t\ttry {\n\t\t\tawait agent.prompt(query);\n\t\t} catch (error) {\n\t\t\tif (!truncated && !this.deps.signal?.aborted) {\n\t\t\t\trunFailure = error instanceof Error ? error.message : String(error);\n\t\t\t}\n\t\t} finally {\n\t\t\tunsubscribe();\n\t\t\tthis.deps.signal?.removeEventListener(\"abort\", abortScout);\n\t\t}\n\n\t\tconst result = parseScoutAnswer(\n\t\t\tlastAssistantText,\n\t\t\t(path) => this.deps.fileExists(path),\n\t\t\t(path) => this.deps.countLines(path),\n\t\t);\n\t\tconst finalResult = {\n\t\t\t...result,\n\t\t\ttruncated: truncated || !hasFinalAnswer(lastAssistantText),\n\t\t\tturnsUsed,\n\t\t\tfailure: this.deps.signal?.aborted ? \"aborted\" : runFailure,\n\t\t};\n\t\tthis.deps.onEvent?.({ type: \"scout_end\", detail: finalResult.failure ?? \"ok\" });\n\t\treturn finalResult;\n\t}\n}\n\nexport function parseScoutAnswer(\n\ttext: string,\n\tfileExists: (path: string) => boolean,\n\tcountLines: (path: string) => number | undefined,\n): Omit<ScoutRunResult, \"truncated\" | \"turnsUsed\" | \"failure\"> {\n\tconst finalAnswer = /<final_answer>([\\s\\S]*?)<\\/final_answer>/i.exec(text)?.[1] ?? text;\n\tconst summaryLines: string[] = [];\n\tconst citations: ScoutCitation[] = [];\n\n\tfor (const rawLine of finalAnswer.split(/\\r?\\n/)) {\n\t\tconst line = rawLine.trim();\n\t\tif (!line) continue;\n\t\tconst citation = parseCitationLine(line, fileExists, countLines);\n\t\tif (citation) {\n\t\t\tcitations.push(citation);\n\t\t\tcontinue;\n\t\t}\n\t\tsummaryLines.push(line);\n\t}\n\n\tconst droppedCitations = citations.filter((citation) => !citation.valid).length;\n\treturn {\n\t\tsummary: summaryLines.join(\"\\n\"),\n\t\tcitations,\n\t\tdroppedCitations,\n\t\tunreliable: citations.length > 0 && droppedCitations / citations.length > 0.5,\n\t};\n}\n\nfunction parseCitationLine(\n\tline: string,\n\tfileExists: (path: string) => boolean,\n\tcountLines: (path: string) => number | undefined,\n): ScoutCitation | undefined {\n\tconst match = /^(.+?):(\\d+)(?:-(\\d+))?$/.exec(line);\n\tif (!match) return undefined;\n\tconst path = match[1].trim();\n\tconst start = Number.parseInt(match[2], 10);\n\tconst end = Number.parseInt(match[3] ?? match[2], 10);\n\tconst lineCount = countLines(path);\n\tconst valid =\n\t\tfileExists(path) &&\n\t\tNumber.isFinite(start) &&\n\t\tNumber.isFinite(end) &&\n\t\tstart >= 1 &&\n\t\tend >= start &&\n\t\t(lineCount === undefined || end <= lineCount);\n\treturn { path, start, end, valid };\n}\n\nfunction clampTurns(maxTurns: number): number {\n\tif (!Number.isFinite(maxTurns)) return MAX_TURNS_DEFAULT;\n\treturn Math.min(MAX_TURNS_HARD_CAP, Math.max(1, Math.trunc(maxTurns)));\n}\n\nfunction emptyResult(): ScoutRunResult {\n\treturn { summary: \"\", citations: [], droppedCitations: 0, unreliable: false, truncated: false, turnsUsed: 0 };\n}\n\nfunction hasFinalAnswer(text: string): boolean {\n\treturn /<final_answer>[\\s\\S]*?<\\/final_answer>/i.test(text);\n}\n\nfunction assistantText(message: { content: unknown }): string {\n\tif (!Array.isArray(message.content)) return \"\";\n\treturn message.content\n\t\t.filter((content): content is { type: \"text\"; text: string } =>\n\t\t\tBoolean(\n\t\t\t\tcontent &&\n\t\t\t\t\ttypeof content === \"object\" &&\n\t\t\t\t\t(content as { type?: unknown }).type === \"text\" &&\n\t\t\t\t\ttypeof (content as { text?: unknown }).text === \"string\",\n\t\t\t),\n\t\t)\n\t\t.map((content) => content.text)\n\t\t.join(\"\\n\");\n}\n"]}
|
|
@@ -8,6 +8,10 @@ export interface CompactionSettings {
|
|
|
8
8
|
triggerPercent?: number;
|
|
9
9
|
model?: string;
|
|
10
10
|
}
|
|
11
|
+
export interface ScoutSettings {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
model?: string;
|
|
14
|
+
}
|
|
11
15
|
export interface SemanticMemoryGcSettings {
|
|
12
16
|
enabled?: boolean;
|
|
13
17
|
preserveRecentPages?: number;
|
|
@@ -139,6 +143,9 @@ export interface AutonomySettings {
|
|
|
139
143
|
goalAutoContinue?: boolean;
|
|
140
144
|
goalAutoContinueDelayMs?: number;
|
|
141
145
|
}
|
|
146
|
+
export interface FailoverSettings {
|
|
147
|
+
subscriptionHop?: boolean;
|
|
148
|
+
}
|
|
142
149
|
export interface ModelRouterSettings {
|
|
143
150
|
enabled?: boolean;
|
|
144
151
|
cheapModel?: string;
|
|
@@ -146,6 +153,7 @@ export interface ModelRouterSettings {
|
|
|
146
153
|
expensiveModel?: string;
|
|
147
154
|
learningModel?: string;
|
|
148
155
|
judgeEnabled?: boolean;
|
|
156
|
+
fitnessGate?: boolean;
|
|
149
157
|
judgeModel?: string;
|
|
150
158
|
executorModel?: string;
|
|
151
159
|
cheapThinking?: ThinkingLevel;
|
|
@@ -270,6 +278,7 @@ export interface Settings {
|
|
|
270
278
|
/** Resource catalog directory (round resource management): the folder pi installs/updates/backs up from. */
|
|
271
279
|
catalogDir?: string;
|
|
272
280
|
compaction?: CompactionSettings;
|
|
281
|
+
scout?: ScoutSettings;
|
|
273
282
|
/** Proactive per-turn cost guard (#34). */
|
|
274
283
|
costGuard?: {
|
|
275
284
|
maxTurnUsd?: number;
|
|
@@ -322,6 +331,7 @@ export interface Settings {
|
|
|
322
331
|
modelCapability?: ModelCapabilitySettings;
|
|
323
332
|
toolkit?: ToolkitSettings;
|
|
324
333
|
modelRouter?: ModelRouterSettings;
|
|
334
|
+
failover?: FailoverSettings;
|
|
325
335
|
autoLearn?: AutoLearnSettings;
|
|
326
336
|
sessionDir?: string;
|
|
327
337
|
httpIdleTimeoutMs?: number;
|
|
@@ -550,6 +560,7 @@ export declare class SettingsManager {
|
|
|
550
560
|
maxTurnUsd: number;
|
|
551
561
|
action: "warn" | "downgrade";
|
|
552
562
|
};
|
|
563
|
+
getFailoverSettings(): Required<FailoverSettings>;
|
|
553
564
|
private getProfileModelRouterSettings;
|
|
554
565
|
getModelRouterSettings(): {
|
|
555
566
|
enabled: boolean;
|
|
@@ -560,6 +571,7 @@ export declare class SettingsManager {
|
|
|
560
571
|
judgeEnabled: boolean;
|
|
561
572
|
judgeModel?: string;
|
|
562
573
|
executorModel?: string;
|
|
574
|
+
fitnessGate: boolean;
|
|
563
575
|
cheapThinking?: ThinkingLevel;
|
|
564
576
|
mediumThinking?: ThinkingLevel;
|
|
565
577
|
expensiveThinking?: ThinkingLevel;
|
|
@@ -575,6 +587,10 @@ export declare class SettingsManager {
|
|
|
575
587
|
keepRecentTokens: number;
|
|
576
588
|
triggerPercent: number;
|
|
577
589
|
};
|
|
590
|
+
getScoutSettings(): {
|
|
591
|
+
enabled: boolean;
|
|
592
|
+
model: string;
|
|
593
|
+
};
|
|
578
594
|
getContextGcSettings(): {
|
|
579
595
|
enabled: boolean;
|
|
580
596
|
preserveRecentMessages: number;
|