ultimate-pi 0.16.0 → 0.18.0
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/.agents/skills/harness-context/SKILL.md +13 -6
- package/.agents/skills/harness-debate-plan/SKILL.md +37 -20
- package/.agents/skills/harness-eval/SKILL.md +6 -21
- package/.agents/skills/harness-governor/SKILL.md +4 -3
- package/.agents/skills/harness-orchestration/SKILL.md +39 -51
- package/.agents/skills/harness-plan/SKILL.md +23 -12
- package/.agents/skills/harness-review/SKILL.md +52 -0
- package/.agents/skills/harness-sentrux-setup/SKILL.md +13 -1
- package/.agents/skills/harness-steer/SKILL.md +14 -0
- package/.pi/agents/harness/adversary.md +3 -10
- package/.pi/agents/harness/evaluator.md +3 -12
- package/.pi/agents/harness/executor.md +12 -14
- package/.pi/agents/harness/planning/decompose.md +7 -4
- package/.pi/agents/harness/planning/hypothesis-validator.md +2 -0
- package/.pi/agents/harness/planning/hypothesis.md +4 -2
- package/.pi/agents/harness/planning/implementation-researcher.md +1 -1
- package/.pi/agents/harness/planning/plan-adversary.md +2 -0
- package/.pi/agents/harness/planning/plan-evaluator.md +2 -0
- package/.pi/agents/harness/planning/plan-synthesizer.md +25 -0
- package/.pi/agents/harness/planning/planning-context.md +48 -0
- package/.pi/agents/harness/planning/review-integrator.md +2 -0
- package/.pi/agents/harness/planning/scout-graphify.md +3 -1
- package/.pi/agents/harness/planning/scout-semantic.md +3 -1
- package/.pi/agents/harness/planning/scout-structure.md +3 -1
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +2 -0
- package/.pi/agents/harness/sentrux-steward.md +51 -0
- package/.pi/extensions/00-posthog-network-bootstrap.ts +11 -0
- package/.pi/extensions/harness-debate-tools.ts +12 -3
- package/.pi/extensions/harness-live-widget.ts +27 -1
- package/.pi/extensions/harness-plan-approval.ts +62 -56
- package/.pi/extensions/harness-run-context.ts +553 -84
- package/.pi/extensions/harness-subagent-submit.ts +43 -33
- package/.pi/extensions/harness-telemetry.ts +29 -4
- package/.pi/extensions/lib/debate-bus-core.ts +15 -9
- package/.pi/extensions/lib/harness-artifact-gate.ts +182 -0
- package/.pi/extensions/lib/harness-posthog.ts +9 -5
- package/.pi/extensions/lib/harness-spawn-topology.ts +188 -0
- package/.pi/extensions/lib/harness-subagent-auth.ts +105 -19
- package/.pi/extensions/lib/harness-subagent-policy.ts +37 -19
- package/.pi/extensions/lib/harness-subagent-precheck.ts +35 -9
- package/.pi/extensions/lib/harness-subagent-submit-pipeline.ts +66 -2
- package/.pi/extensions/lib/harness-subagent-submit-registry.ts +21 -3
- package/.pi/extensions/lib/harness-subagents-bridge.ts +91 -28
- package/.pi/extensions/lib/harness-subprocess-bootstrap.ts +73 -0
- package/.pi/extensions/lib/plan-approval/create-plan.ts +2 -3
- package/.pi/extensions/lib/plan-approval/resolve-disk.ts +102 -0
- package/.pi/extensions/lib/plan-approval/schema.ts +22 -8
- package/.pi/extensions/lib/plan-approval/types.ts +1 -1
- package/.pi/extensions/lib/plan-approval/validate.ts +2 -2
- package/.pi/extensions/lib/plan-approval-readiness.ts +241 -0
- package/.pi/extensions/lib/plan-debate-eligibility.ts +67 -7
- package/.pi/extensions/lib/plan-debate-focus.ts +21 -9
- package/.pi/extensions/lib/plan-debate-gate.ts +101 -17
- package/.pi/extensions/lib/plan-debate-lanes.ts +57 -3
- package/.pi/extensions/lib/plan-debate-round-status.ts +18 -7
- package/.pi/extensions/lib/plan-messenger.ts +4 -0
- package/.pi/extensions/lib/plan-review-gate.ts +59 -0
- package/.pi/extensions/lib/posthog-client.ts +76 -0
- package/.pi/extensions/policy-gate.ts +24 -19
- package/.pi/extensions/trace-recorder.ts +1 -0
- package/.pi/harness/agents.manifest.json +24 -16
- package/.pi/harness/corpus/cron.example +8 -0
- package/.pi/harness/corpus/graphify-kb-updater.config.json +159 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.env.template +4 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.service +17 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.timer +11 -0
- package/.pi/harness/docs/adrs/0001-harness-constitution.md +2 -1
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +7 -6
- package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +6 -1
- package/.pi/harness/docs/adrs/0031-harness-run-context.md +1 -1
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -0
- package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +3 -3
- package/.pi/harness/docs/adrs/0036-implementation-research-and-selective-debate.md +8 -5
- package/.pi/harness/docs/adrs/0039-harness-post-run-review-gate.md +47 -0
- package/.pi/harness/docs/adrs/0040-practice-grounded-orchestration.md +40 -0
- package/.pi/harness/docs/adrs/0041-intelligent-planning-reconnaissance.md +39 -0
- package/.pi/harness/docs/adrs/0042-agent-native-orchestration.md +35 -0
- package/.pi/harness/docs/adrs/0043-path-first-harness-tools.md +38 -0
- package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +36 -0
- package/.pi/harness/docs/adrs/README.md +10 -0
- package/.pi/harness/docs/graphify-kb-updater-runbook.md +157 -0
- package/.pi/harness/docs/practice-map.md +110 -0
- package/.pi/harness/env.harness.template +5 -3
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med-fast/artifacts/implementation-research.yaml +28 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med-fast/artifacts/review-round-consolidated.yaml +25 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med-fast/plan-packet.yaml +196 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med-fast/plan-review.md +14 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med-fast/research-brief.yaml +62 -0
- package/.pi/harness/evals/smoke/sentrux-stub.json +1 -1
- package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +43 -17
- package/.pi/harness/specs/README.md +1 -1
- package/.pi/harness/specs/harness-run-context.schema.json +11 -0
- package/.pi/harness/specs/harness-spawn-context.schema.json +14 -0
- package/.pi/harness/specs/plan-execution-plan.schema.json +39 -1
- package/.pi/harness/specs/plan-packet.schema.json +4 -0
- package/.pi/harness/specs/plan-phase-status.schema.json +17 -0
- package/.pi/harness/specs/plan-phase-waiver.schema.json +25 -0
- package/.pi/harness/specs/plan-planning-context.schema.json +50 -0
- package/.pi/harness/specs/plan-review-round-draft.schema.json +1 -1
- package/.pi/harness/specs/repair-brief.schema.json +45 -0
- package/.pi/harness/specs/review-outcome.schema.json +46 -0
- package/.pi/harness/specs/sentrux-manifest-proposal.schema.json +80 -0
- package/.pi/harness/specs/sentrux-signal.schema.json +43 -0
- package/.pi/harness/specs/steer-state.schema.json +20 -0
- package/.pi/lib/harness-context-mode-policy.ts +256 -0
- package/.pi/lib/harness-repair-brief.ts +145 -0
- package/.pi/lib/harness-run-context.ts +591 -32
- package/.pi/lib/harness-ui-state.ts +87 -9
- package/.pi/model-router.example.json +13 -4
- package/.pi/prompts/harness-auto.md +9 -9
- package/.pi/prompts/harness-critic.md +3 -30
- package/.pi/prompts/harness-eval.md +4 -37
- package/.pi/prompts/harness-plan.md +139 -57
- package/.pi/prompts/harness-review.md +150 -15
- package/.pi/prompts/harness-run.md +62 -10
- package/.pi/prompts/harness-sentrux-steward.md +55 -0
- package/.pi/prompts/harness-setup.md +4 -4
- package/.pi/prompts/harness-steer.md +30 -0
- package/.pi/scripts/graphify-kb-updater.mjs +358 -0
- package/.pi/scripts/harness-generate-model-router.mjs +118 -36
- package/.pi/scripts/harness-model-router-routing.test.mjs +97 -0
- package/.pi/scripts/harness-sync-model-router.mjs +15 -2
- package/.pi/scripts/harness-verify.mjs +51 -6
- package/.pi/scripts/harness-web-policy-guard.mjs +68 -0
- package/.pi/scripts/validate-plan-dag.mjs +3 -3
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +22 -0
- package/package.json +5 -4
- package/vendor/pi-model-router/UPSTREAM_PIN.md +3 -1
- package/vendor/pi-model-router/extensions/commands.ts +4 -4
- package/vendor/pi-model-router/extensions/index.ts +21 -0
- package/vendor/pi-model-router/extensions/provider.ts +130 -79
- package/vendor/pi-model-router/extensions/routing.ts +148 -0
- package/vendor/pi-model-router/extensions/state.ts +3 -0
- package/vendor/pi-model-router/extensions/types.ts +9 -0
- package/vendor/pi-model-router/extensions/ui.ts +16 -2
- package/.pi/prompts/git-sync.md +0 -124
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Unit tests for session-locked pi-model-router routing (no LLM).
|
|
4
|
+
* Run: npx tsx .pi/scripts/harness-model-router-routing.test.mjs
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { join, dirname } from "node:path";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import {
|
|
12
|
+
decideSessionLock,
|
|
13
|
+
applyThinkingToDecision,
|
|
14
|
+
buildRoutingDecision,
|
|
15
|
+
decideRouting,
|
|
16
|
+
} from "../../vendor/pi-model-router/extensions/routing.js";
|
|
17
|
+
|
|
18
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
19
|
+
|
|
20
|
+
const sampleProfile = {
|
|
21
|
+
high: { model: "openai/gpt-5.5", thinking: "high" },
|
|
22
|
+
medium: { model: "openai/gpt-5.5", thinking: "medium" },
|
|
23
|
+
low: { model: "openai/gpt-5.5", thinking: "low" },
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const planningContext = {
|
|
27
|
+
systemPrompt: "You are a harness architect. Design tradeoffs and migration strategy.",
|
|
28
|
+
messages: [
|
|
29
|
+
{
|
|
30
|
+
role: "user",
|
|
31
|
+
content:
|
|
32
|
+
"Plan a multi-phase refactor across modules with architecture review.",
|
|
33
|
+
timestamp: 1,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const shortContext = {
|
|
39
|
+
systemPrompt: "Summarize briefly.",
|
|
40
|
+
messages: [{ role: "user", content: "changelog", timestamp: 1 }],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const lockHigh = decideSessionLock(
|
|
44
|
+
planningContext,
|
|
45
|
+
"auto",
|
|
46
|
+
sampleProfile,
|
|
47
|
+
undefined,
|
|
48
|
+
undefined,
|
|
49
|
+
0.5,
|
|
50
|
+
[{ matches: "changelog", tier: "low" }],
|
|
51
|
+
);
|
|
52
|
+
assert.equal(lockHigh.tier, "high", "planning prompt locks high tier");
|
|
53
|
+
|
|
54
|
+
const lockLow = decideSessionLock(shortContext, "auto", sampleProfile);
|
|
55
|
+
assert.equal(lockLow.tier, "low", "short summary locks low tier");
|
|
56
|
+
|
|
57
|
+
const locked = buildRoutingDecision(
|
|
58
|
+
"auto",
|
|
59
|
+
sampleProfile,
|
|
60
|
+
lockHigh.tier,
|
|
61
|
+
"planning",
|
|
62
|
+
lockHigh.reasoning,
|
|
63
|
+
);
|
|
64
|
+
const thinkingTurn = decideRouting(
|
|
65
|
+
{
|
|
66
|
+
...planningContext,
|
|
67
|
+
messages: [
|
|
68
|
+
...planningContext.messages,
|
|
69
|
+
{ role: "user", content: "changelog only", timestamp: 2 },
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
"auto",
|
|
73
|
+
sampleProfile,
|
|
74
|
+
locked,
|
|
75
|
+
);
|
|
76
|
+
const merged = applyThinkingToDecision(locked, thinkingTurn, sampleProfile);
|
|
77
|
+
assert.equal(merged.targetLabel, locked.targetLabel, "model stays locked");
|
|
78
|
+
assert.equal(merged.tier, thinkingTurn.tier, "thinking tier follows turn");
|
|
79
|
+
assert.equal(merged.thinking, "low", "low thinking from turn tier config");
|
|
80
|
+
|
|
81
|
+
const examplePath = join(ROOT, ".pi", "model-router.example.json");
|
|
82
|
+
const example = JSON.parse(readFileSync(examplePath, "utf8"));
|
|
83
|
+
for (const [name, profile] of Object.entries(example.profiles ?? {})) {
|
|
84
|
+
const { high, medium, low } = profile;
|
|
85
|
+
assert.equal(
|
|
86
|
+
high.model,
|
|
87
|
+
medium.model,
|
|
88
|
+
`example profile ${name}: medium/high same model`,
|
|
89
|
+
);
|
|
90
|
+
assert.equal(
|
|
91
|
+
medium.model,
|
|
92
|
+
low.model,
|
|
93
|
+
`example profile ${name}: low/medium same model`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log("harness-model-router-routing.test: PASS");
|
|
@@ -29,11 +29,24 @@ function saveSettings(settingsPath, data) {
|
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function readDefaultRouterProfile(configPath) {
|
|
33
|
+
if (!existsSync(configPath)) return "auto";
|
|
34
|
+
try {
|
|
35
|
+
const data = JSON.parse(readFileSync(configPath, "utf8"));
|
|
36
|
+
const profile =
|
|
37
|
+
typeof data.defaultProfile === "string" ? data.defaultProfile.trim() : "";
|
|
38
|
+
return profile || "auto";
|
|
39
|
+
} catch {
|
|
40
|
+
return "auto";
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
function main() {
|
|
33
45
|
const root = process.cwd();
|
|
34
46
|
const configPath = join(root, ".pi", "model-router.json");
|
|
35
47
|
const settingsPath = join(root, ".pi", "settings.json");
|
|
36
48
|
const hasConfig = existsSync(configPath);
|
|
49
|
+
const defaultRouterProfile = readDefaultRouterProfile(configPath);
|
|
37
50
|
|
|
38
51
|
const settings = loadSettings(settingsPath);
|
|
39
52
|
if (!settings) {
|
|
@@ -67,14 +80,14 @@ function main() {
|
|
|
67
80
|
|
|
68
81
|
if (noProjectDefault) {
|
|
69
82
|
settings.defaultProvider = "router";
|
|
70
|
-
settings.defaultModel =
|
|
83
|
+
settings.defaultModel = defaultRouterProfile;
|
|
71
84
|
changed = true;
|
|
72
85
|
}
|
|
73
86
|
|
|
74
87
|
if (changed) {
|
|
75
88
|
saveSettings(settingsPath, settings);
|
|
76
89
|
console.log(
|
|
77
|
-
|
|
90
|
+
`✓ Router defaults set (\`router\` / \`${defaultRouterProfile}\`) — run /reload in pi when ready`,
|
|
78
91
|
);
|
|
79
92
|
} else {
|
|
80
93
|
console.log("[harness-model-router] Defaults unchanged (user set defaultProvider)");
|
|
@@ -23,6 +23,8 @@ const REQUIRED_SCHEMAS = [
|
|
|
23
23
|
"eval-verdict.schema.json",
|
|
24
24
|
"harness-spawn-context.schema.json",
|
|
25
25
|
"harness-turn.schema.json",
|
|
26
|
+
"sentrux-manifest-proposal.schema.json",
|
|
27
|
+
"sentrux-signal.schema.json",
|
|
26
28
|
];
|
|
27
29
|
|
|
28
30
|
const REQUIRED_ADRS = [
|
|
@@ -39,6 +41,7 @@ const REQUIRED_ADRS = [
|
|
|
39
41
|
"0032-harness-command-orchestration.md",
|
|
40
42
|
"0037-subagent-submit-tools.md",
|
|
41
43
|
"0038-budget-telemetry-only.md",
|
|
44
|
+
"0040-practice-grounded-orchestration.md",
|
|
42
45
|
];
|
|
43
46
|
|
|
44
47
|
const REQUIRED_EXTENSIONS = [
|
|
@@ -145,6 +148,34 @@ async function checkSentruxRules() {
|
|
|
145
148
|
ok(".sentrux/rules.toml present");
|
|
146
149
|
}
|
|
147
150
|
|
|
151
|
+
async function checkModelRouterThinkingOnly() {
|
|
152
|
+
const path = join(ROOT, ".pi", "model-router.json");
|
|
153
|
+
if (!(await fileExists(path))) {
|
|
154
|
+
ok("model-router.json absent (skip thinking-only tier check)");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
let raw;
|
|
158
|
+
try {
|
|
159
|
+
raw = JSON.parse(await readFile(path, "utf-8"));
|
|
160
|
+
} catch {
|
|
161
|
+
fail("invalid .pi/model-router.json");
|
|
162
|
+
}
|
|
163
|
+
const profiles = raw.profiles ?? {};
|
|
164
|
+
for (const [name, profile] of Object.entries(profiles)) {
|
|
165
|
+
const high = profile?.high?.model;
|
|
166
|
+
const medium = profile?.medium?.model;
|
|
167
|
+
const low = profile?.low?.model;
|
|
168
|
+
if (
|
|
169
|
+
!(high && medium && low && high === medium && medium === low)
|
|
170
|
+
) {
|
|
171
|
+
fail(
|
|
172
|
+
`model-router profile "${name}" must use the same model on high/medium/low (thinking-only tiers)`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
ok("model-router.json thinking-only (same model per profile)");
|
|
177
|
+
}
|
|
178
|
+
|
|
148
179
|
async function checkSentruxGate() {
|
|
149
180
|
await checkSentruxRules();
|
|
150
181
|
|
|
@@ -152,13 +183,21 @@ async function checkSentruxGate() {
|
|
|
152
183
|
ok("Sentrux MCP stub gate skipped (HARNESS_SENTRUX_REQUIRED not set)");
|
|
153
184
|
return;
|
|
154
185
|
}
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
186
|
+
const runDir = process.env.HARNESS_RUN_DIR?.trim();
|
|
187
|
+
const runSignalPath = runDir
|
|
188
|
+
? join(runDir, "artifacts", "sentrux-signal.yaml")
|
|
189
|
+
: null;
|
|
190
|
+
if (runSignalPath && (await fileExists(runSignalPath))) {
|
|
191
|
+
ok(`Sentrux run signal present (${runSignalPath})`);
|
|
192
|
+
} else {
|
|
193
|
+
const stubPath = join(ROOT, ".pi", "harness", "evals", "smoke", "sentrux-stub.json");
|
|
194
|
+
if (!(await fileExists(stubPath))) {
|
|
195
|
+
fail(
|
|
196
|
+
"HARNESS_SENTRUX_REQUIRED=true but no artifacts/sentrux-signal.yaml (set HARNESS_RUN_DIR) and .pi/harness/evals/smoke/sentrux-stub.json missing",
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
ok("Sentrux stub present (run signal absent — prefer artifacts/sentrux-signal.yaml from /harness-run)");
|
|
160
200
|
}
|
|
161
|
-
ok("Sentrux stub present");
|
|
162
201
|
|
|
163
202
|
const { code, out } = await runNodeScript(
|
|
164
203
|
join(ROOT, ".pi", "scripts", "sentrux-rules-sync.mjs"),
|
|
@@ -262,6 +301,11 @@ async function main() {
|
|
|
262
301
|
if (!policyGateSrc.includes('pi.on("tool_call", async (event, ctx)')) {
|
|
263
302
|
fail("policy-gate tool_call must receive ctx for run context");
|
|
264
303
|
}
|
|
304
|
+
if (!policyGateSrc.includes("evaluateContextModeMutation")) {
|
|
305
|
+
fail(
|
|
306
|
+
"policy-gate.ts must evaluate context-mode execute tools via evaluateContextModeMutation",
|
|
307
|
+
);
|
|
308
|
+
}
|
|
265
309
|
ok("policy-gate plan-phase writes");
|
|
266
310
|
|
|
267
311
|
const runCtxFixture = join(SMOKE, "run-context.fixture.json");
|
|
@@ -288,6 +332,7 @@ async function main() {
|
|
|
288
332
|
ok("test-diff-golden.json");
|
|
289
333
|
|
|
290
334
|
await checkSentruxGate();
|
|
335
|
+
await checkModelRouterThinkingOnly();
|
|
291
336
|
|
|
292
337
|
if (!(await fileExists(AGENTS_MANIFEST))) {
|
|
293
338
|
fail(
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Package-wide web-policy guard.
|
|
3
|
+
* Rejects raw HTTP shell/client paths unless they are in approved harness/API
|
|
4
|
+
* abstraction files. This is a static smoke guard; runtime blocking remains in
|
|
5
|
+
* .pi/extensions/harness-web-guard.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
9
|
+
import { join, relative, resolve } from "node:path";
|
|
10
|
+
|
|
11
|
+
const ROOT = resolve(new URL("../..", import.meta.url).pathname);
|
|
12
|
+
const ALLOWED_FILES = new Set([
|
|
13
|
+
".pi/extensions/harness-web-guard.ts",
|
|
14
|
+
".pi/extensions/harness-web-tools.ts",
|
|
15
|
+
".pi/extensions/lib/harness-web/run-cli.ts",
|
|
16
|
+
".pi/extensions/harness-run-context.ts",
|
|
17
|
+
".pi/extensions/lib/ask-user/schema.ts",
|
|
18
|
+
".pi/scripts/harness-web.py",
|
|
19
|
+
".pi/scripts/harness-web-search.md",
|
|
20
|
+
".pi/scripts/harness-web-policy-guard.mjs",
|
|
21
|
+
".agents/skills/scrapling-web/SKILL.md",
|
|
22
|
+
".pi/scripts/harness-cli-verify.sh",
|
|
23
|
+
".pi/scripts/harness_web/output.py",
|
|
24
|
+
"AGENTS.md",
|
|
25
|
+
]);
|
|
26
|
+
const SKIP_DIRS = new Set([".git", "node_modules", "vendor", "graphify-out", "graphify-books-out", ".web", ".cocoindex_code", ".agents", ".cursor", "raw"]);
|
|
27
|
+
const TEXT_EXTS = new Set([".js", ".mjs", ".ts", ".tsx", ".json", ".yaml", ".yml", ".py", ".sh", ".toml", ".example", ".template"]);
|
|
28
|
+
const NEEDLES = [
|
|
29
|
+
{ name: "raw curl/wget URL", re: /\b(?:curl|wget)\b[^\n]*https?:\/\//i },
|
|
30
|
+
{ name: "raw Node HTTP client", re: /\b(?:fetch|request|get)\s*\(\s*[`'"]https?:\/\//i },
|
|
31
|
+
{ name: "raw Python HTTP client", re: /\brequests\.(?:get|post|put|delete|head)\s*\(\s*[`'"]https?:\/\//i },
|
|
32
|
+
{ name: "Firecrawl path", re: /\bfirecrawl\b/i },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function ext(path) {
|
|
36
|
+
const i = path.lastIndexOf(".");
|
|
37
|
+
return i >= 0 ? path.slice(i) : "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function walk(dir, out = []) {
|
|
41
|
+
for (const name of readdirSync(dir)) {
|
|
42
|
+
const abs = join(dir, name);
|
|
43
|
+
const r = relative(ROOT, abs);
|
|
44
|
+
if (SKIP_DIRS.has(name) || r.startsWith(".pi/harness/runs/") || r.startsWith(".pi/agents/") || r.startsWith(".pi/skills/") || r.startsWith(".pi/prompts/") || r.startsWith(".pi/harness/docs/")) continue;
|
|
45
|
+
const st = statSync(abs);
|
|
46
|
+
if (st.isDirectory()) walk(abs, out);
|
|
47
|
+
else if (TEXT_EXTS.has(ext(name)) || name.includes("template") || name.includes("example")) out.push(abs);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const hits = [];
|
|
53
|
+
for (const abs of walk(ROOT)) {
|
|
54
|
+
const r = relative(ROOT, abs);
|
|
55
|
+
if (ALLOWED_FILES.has(r)) continue;
|
|
56
|
+
let text = "";
|
|
57
|
+
try { text = readFileSync(abs, "utf8"); } catch { continue; }
|
|
58
|
+
for (const needle of NEEDLES) {
|
|
59
|
+
const m = text.match(needle.re);
|
|
60
|
+
if (m) hits.push({ file: r, rule: needle.name, match: m[0].slice(0, 140) });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (hits.length) {
|
|
65
|
+
console.error(JSON.stringify({ ok: false, hits }, null, 2));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
console.log(JSON.stringify({ ok: true, scanned_root: ROOT }, null, 2));
|
|
@@ -12,9 +12,9 @@ import { readYamlFile, writeYamlFile } from "../lib/harness-yaml.mjs";
|
|
|
12
12
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
13
13
|
|
|
14
14
|
const MINIMUMS = {
|
|
15
|
-
low: { phases: 2, work_items:
|
|
16
|
-
med: { phases: 3, work_items:
|
|
17
|
-
high: { phases: 4, work_items:
|
|
15
|
+
low: { phases: 2, work_items: 2, acceptance_checks: 3, risks: 0 },
|
|
16
|
+
med: { phases: 3, work_items: 4, acceptance_checks: 5, risks: 3 },
|
|
17
|
+
high: { phases: 4, work_items: 6, acceptance_checks: 8, risks: 3 },
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
function fail(msg) {
|
package/AGENTS.md
CHANGED
|
@@ -10,6 +10,7 @@ Created: 2026-05-14
|
|
|
10
10
|
- ./raw/ → Source documents for graphify ingestion
|
|
11
11
|
- docs/adr/ → Repo-level Architectural Decision Records
|
|
12
12
|
- .pi/harness/docs/adrs/ → Harness ADRs (team-shared; [index](.pi/harness/docs/adrs/README.md))
|
|
13
|
+
- .pi/harness/docs/practice-map.md → Phase → practice → agent spawn topology for `/harness-plan`, `/harness-run`, `/harness-review`
|
|
13
14
|
- .pi/skills/ → Agent skills
|
|
14
15
|
- .pi/agents/ → Specialized agents
|
|
15
16
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,28 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.18.0] — 2026-05-23
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **Harness:** Agent-native plan/review workflows (ADR 0042–0044) — path-first `approve_plan` / `create_plan` / `submit_*`, lakes on execution plans, `parallel_probes` debate, `review-outcome.yaml` routing, `/harness-steer` repair loop, `merge_harness_yaml`, and `harness_synthesize_repair_brief`.
|
|
12
|
+
- **Harness:** Practice map, planning-context and plan-synthesizer agents, PostHog bootstrap, graphify KB updater corpus, and Sentrux steward prompt.
|
|
13
|
+
|
|
14
|
+
### ✅ Tests
|
|
15
|
+
|
|
16
|
+
- Add harness tool-payload, post-run routing, subprocess bootstrap, artifact gate, and topology tests; extend smoke plan fixture for parallel probes.
|
|
17
|
+
|
|
18
|
+
## [v0.17.0] — 2026-05-22
|
|
19
|
+
|
|
20
|
+
### ✨ Features
|
|
21
|
+
|
|
22
|
+
- **Model router:** Session-locked model SKU at start (initial prompt + system prompt); per-turn routing adjusts thinking tier only; subagents lock from agent `systemPrompt` complexity.
|
|
23
|
+
- **Harness:** Thinking-only profile shape in generator/verify; plan review gate, debate eligibility, and smoke fixture updates.
|
|
24
|
+
|
|
25
|
+
### ✅ Tests
|
|
26
|
+
|
|
27
|
+
- Add `harness-model-router-routing` and plan-debate eligibility coverage.
|
|
28
|
+
|
|
7
29
|
## [v0.16.0] — 2026-05-19
|
|
8
30
|
|
|
9
31
|
### ✨ Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Ultimate AI coding harness for pi.dev — extensible skills, Obsidian wiki knowledge layer, compressed context, deterministic output",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@earendil-works/pi-coding-agent": "*"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
77
|
-
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/custom-system-prompt.ts .pi/lib/harness-run-context.ts .pi/lib/harness-ui-state.ts .pi/extensions/harness-run-context.ts .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/harness-plan-approval.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/lib/plan-approval/types.ts .pi/extensions/lib/plan-approval/schema.ts .pi/extensions/lib/plan-approval/validate.ts .pi/extensions/lib/plan-approval/format-plan.ts .pi/extensions/lib/plan-approval/dialog.ts .pi/extensions/lib/plan-approval/render.ts .pi/extensions/lib/plan-approval/create-plan.ts .pi/extensions/harness-subagents.ts .pi/extensions/lib/harness-subagents-bridge.ts .pi/extensions/lib/harness-cocoindex-refresh.ts .pi/extensions/lib/harness-subagent-auth.ts .pi/extensions/lib/harness-subagent-policy.ts .pi/extensions/lib/harness-subagent-precheck.ts .pi/extensions/lib/harness-spawn-budget.ts .pi/extensions/lib/spawn-policy.ts vendor/pi-subagents/src/agents.ts vendor/pi-subagents/src/subagents.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/policy-gate.ts .pi/extensions/budget-guard.ts .pi/extensions/debate-orchestrator.ts .pi/extensions/harness-debate-tools.ts .pi/extensions/lib/debate-bus-core.ts .pi/extensions/lib/debate-bus-state.ts .pi/extensions/lib/plan-debate-gate.ts .pi/extensions/lib/plan-debate-id.ts .pi/extensions/lib/plan-messenger.ts .pi/extensions/lib/plan-debate-envelope.ts .pi/extensions/lib/plan-review-integrator-rules.ts .pi/extensions/lib/plan-scope-guard.ts .pi/extensions/lib/plan-debate-write-guard.ts .pi/extensions/lib/plan-debate-lane.ts .pi/extensions/lib/plan-debate-round-status.ts .pi/extensions/harness-live-widget.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/harness-web-tools.ts .pi/extensions/harness-web-guard.ts .pi/extensions/lib/harness-web/run-cli.ts",
|
|
77
|
+
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/custom-system-prompt.ts .pi/lib/harness-run-context.ts .pi/lib/harness-context-mode-policy.ts .pi/lib/harness-ui-state.ts .pi/extensions/harness-run-context.ts .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/00-posthog-network-bootstrap.ts .pi/extensions/lib/posthog-client.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/harness-plan-approval.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/lib/plan-approval/types.ts .pi/extensions/lib/plan-approval/schema.ts .pi/extensions/lib/plan-approval/validate.ts .pi/extensions/lib/plan-approval/format-plan.ts .pi/extensions/lib/plan-approval/dialog.ts .pi/extensions/lib/plan-approval/render.ts .pi/extensions/lib/plan-approval/create-plan.ts .pi/extensions/harness-subagents.ts .pi/extensions/lib/harness-subagents-bridge.ts .pi/extensions/lib/harness-cocoindex-refresh.ts .pi/extensions/lib/harness-subagent-auth.ts .pi/extensions/lib/harness-subagent-policy.ts .pi/extensions/lib/harness-subagent-precheck.ts .pi/extensions/lib/harness-spawn-budget.ts .pi/extensions/lib/spawn-policy.ts vendor/pi-subagents/src/agents.ts vendor/pi-subagents/src/subagents.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/policy-gate.ts .pi/extensions/budget-guard.ts .pi/extensions/debate-orchestrator.ts .pi/extensions/harness-debate-tools.ts .pi/extensions/lib/debate-bus-core.ts .pi/extensions/lib/debate-bus-state.ts .pi/extensions/lib/plan-debate-gate.ts .pi/extensions/lib/plan-debate-id.ts .pi/extensions/lib/plan-messenger.ts .pi/extensions/lib/plan-debate-envelope.ts .pi/extensions/lib/plan-review-integrator-rules.ts .pi/extensions/lib/plan-scope-guard.ts .pi/extensions/lib/plan-debate-write-guard.ts .pi/extensions/lib/plan-debate-lane.ts .pi/extensions/lib/plan-debate-round-status.ts .pi/extensions/harness-live-widget.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/harness-web-tools.ts .pi/extensions/harness-web-guard.ts .pi/extensions/lib/harness-web/run-cli.ts",
|
|
78
78
|
"vendor:sync-router": "bash .pi/scripts/vendor-sync-pi-model-router.sh",
|
|
79
79
|
"vendor:sync-vcc": "bash .pi/scripts/vendor-sync-pi-vcc.sh",
|
|
80
80
|
"vendor:sync-subagents": "bash .pi/scripts/vendor-sync-pi-subagents.sh",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"format": "biome format --write",
|
|
85
85
|
"format:check": "biome format",
|
|
86
86
|
"prepare": "lefthook install",
|
|
87
|
-
"test": "node --test test/harness-verify.test.mjs test/harness-ask-user.test.mjs test/harness-subagents-loader.test.mjs test/harness-subagent-precheck.test.mjs test/sentrux-rules-sync.test.mjs test/harness-budget-guard.test.mjs && node .pi/harness/evals/smoke/smoke-harness-plan.mjs --fixture && npx -y tsx --test test/harness-vcc-settings.test.ts test/harness-live-widget-status.test.ts test/harness-plan-phase-policy.test.mjs test/harness-subagent-policy.test.mjs test/harness-spawn-budget.test.mjs test/harness-spawn-parse.test.mjs test/harness-schema-validate.test.mjs test/harness-turn-routing.test.mjs test/harness-budget-enforce.test.mjs test/harness-submit-policy.test.mjs test/plan-approval-format.test.mjs test/plan-approval-dialog.test.mjs test/plan-approval-sync.test.mjs test/plan-create-plan.test.mjs test/plan-review-format.test.mjs test/debate-plan-phase.test.mjs test/plan-debate-eligibility.test.mjs test/plan-messenger-gate.test.mjs test/plan-debate-lane-apply.test.mjs",
|
|
87
|
+
"test": "node --test test/harness-verify.test.mjs test/posthog-client.test.mjs test/harness-ask-user.test.mjs test/harness-subagents-loader.test.mjs test/harness-subagent-precheck.test.mjs test/sentrux-rules-sync.test.mjs test/harness-budget-guard.test.mjs && node .pi/harness/evals/smoke/smoke-harness-plan.mjs --fixture && npx -y tsx --test test/harness-vcc-settings.test.ts test/harness-run-context-postrun.test.mjs test/harness-tool-payload.test.mjs test/harness-live-widget-status.test.ts test/harness-plan-phase-policy.test.mjs test/harness-context-mode-policy.test.mjs test/harness-subprocess-bootstrap.test.mjs test/harness-subagent-policy.test.mjs test/harness-subagent-precheck-topology.test.mjs test/plan-approval-readiness.test.mjs test/harness-spawn-budget.test.mjs test/harness-spawn-parse.test.mjs test/harness-schema-validate.test.mjs test/harness-turn-routing.test.mjs test/harness-budget-enforce.test.mjs test/harness-submit-policy.test.mjs test/plan-approval-format.test.mjs test/plan-approval-dialog.test.mjs test/plan-approval-sync.test.mjs test/plan-create-plan.test.mjs test/plan-review-format.test.mjs test/debate-plan-phase.test.mjs test/plan-debate-eligibility.test.mjs test/plan-messenger-gate.test.mjs test/plan-debate-lane-apply.test.mjs",
|
|
88
88
|
"test:vcc": "npx -y tsx --test vendor/pi-vcc/tests/*.test.ts",
|
|
89
89
|
"harness:sentrux-bootstrap": "node .pi/scripts/harness-sentrux-bootstrap.mjs",
|
|
90
90
|
"harness:sentrux-sync": "node .pi/scripts/sentrux-rules-sync.mjs --force",
|
|
@@ -108,7 +108,8 @@
|
|
|
108
108
|
"croner": "^9.0.0",
|
|
109
109
|
"jimp": "^1.6.1",
|
|
110
110
|
"nanoid": "^5.1.5",
|
|
111
|
-
"posthog-node": "^5.30.6"
|
|
111
|
+
"posthog-node": "^5.30.6",
|
|
112
|
+
"undici": "^7.16.0"
|
|
112
113
|
},
|
|
113
114
|
"overrides": {
|
|
114
115
|
"@mariozechner/pi-agent-core": "npm:@earendil-works/pi-agent-core@0.74.1",
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
- **Repository:** https://github.com/yeliu84/pi-model-router
|
|
4
4
|
- **License:** MIT (`LICENSE` in this tree)
|
|
5
5
|
- **Pinned upstream commit:** `8c60095da0e753c242c4be9bb617b85f4dd3255c`
|
|
6
|
-
- **Local changes:**
|
|
6
|
+
- **Local changes:**
|
|
7
|
+
- TypeScript imports in `extensions/*.ts` use `@earendil-works/*`; relative imports end in `.js` for NodeNext; `package.json` peerDependencies list `@earendil-works/*`.
|
|
8
|
+
- **Session-locked routing (ultimate-pi harness):** one concrete model per session/profile, chosen from initial prompt + system prompt complexity; per-turn routing adjusts **thinking tier only** (`sessionLock` persisted in `router-state`). Harness generator emits the same `model` on high/medium/low tiers.
|
|
7
9
|
|
|
8
10
|
**Refresh upstream:** run `npm run vendor:sync-router` from ultimate-pi root (updates this file with the latest commit SHA).
|
|
@@ -58,12 +58,12 @@ export const registerCommands = (
|
|
|
58
58
|
const SUBCOMMAND_DETAILS = [
|
|
59
59
|
{ name: 'status', desc: 'Show current router status' },
|
|
60
60
|
{ name: 'profile', desc: 'Switch to a different router profile' },
|
|
61
|
-
{ name: 'pin', desc: 'Pin
|
|
61
|
+
{ name: 'pin', desc: 'Pin thinking tier for a profile (model stays session-locked)' },
|
|
62
62
|
{ name: 'thinking', desc: 'Override thinking level for a tier or profile' },
|
|
63
63
|
{ name: 'disable', desc: 'Disable the router and restore last model' },
|
|
64
64
|
{
|
|
65
65
|
name: 'fix',
|
|
66
|
-
desc: 'Correct
|
|
66
|
+
desc: 'Correct last thinking tier and pin it (model stays session-locked)',
|
|
67
67
|
},
|
|
68
68
|
{ name: 'widget', desc: 'Toggle the router status widget' },
|
|
69
69
|
{ name: 'debug', desc: 'Toggle or clear router debug history' },
|
|
@@ -676,10 +676,10 @@ export const registerCommands = (
|
|
|
676
676
|
'Router Subcommands:',
|
|
677
677
|
' status Show current status, profile, pin, cost, and last decision.',
|
|
678
678
|
' profile [name] Switch to a profile (enables router if off). Lists available if no name.',
|
|
679
|
-
' pin [profile] <tier|auto>
|
|
679
|
+
' pin [profile] <tier|auto> Pin thinking tier (high|medium|low); model stays locked for the session.',
|
|
680
680
|
' thinking [prof] [tier] <lv> Override thinking level for a profile/tier (off|minimal|...|xhigh|auto).',
|
|
681
681
|
' disable Disable the router and restore the last used non-router model.',
|
|
682
|
-
' fix <tier> Correct
|
|
682
|
+
' fix <tier> Correct last thinking tier and pin it for the current profile.',
|
|
683
683
|
' widget <on|off|toggle> Control the persistent status widget visibility.',
|
|
684
684
|
' debug <on|off|show|clear> Control routing debug logging to notifications and history.',
|
|
685
685
|
' reload Hot-reload the configuration JSON from .pi/model-router.json.',
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type RouterThinkingByProfile,
|
|
11
11
|
type RouterTier,
|
|
12
12
|
type CustomSessionEntry,
|
|
13
|
+
type SessionLock,
|
|
13
14
|
} from './types.js';
|
|
14
15
|
import {
|
|
15
16
|
FALLBACK_CONFIG,
|
|
@@ -47,6 +48,7 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
47
48
|
let lastPersistedSnapshot: string | undefined;
|
|
48
49
|
let isInitialized = false;
|
|
49
50
|
let isInternalModelSwitch = false;
|
|
51
|
+
let sessionLock: SessionLock | undefined;
|
|
50
52
|
|
|
51
53
|
const setModelInternally = async (
|
|
52
54
|
model: NonNullable<ExtensionContext['model']>,
|
|
@@ -94,6 +96,7 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
94
96
|
lastDecision,
|
|
95
97
|
lastNonRouterModel,
|
|
96
98
|
accumulatedCost,
|
|
99
|
+
sessionLock,
|
|
97
100
|
);
|
|
98
101
|
const snapshot = JSON.stringify({
|
|
99
102
|
...state,
|
|
@@ -127,6 +130,7 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
127
130
|
accumulatedCost,
|
|
128
131
|
widgetEnabled,
|
|
129
132
|
currentConfig,
|
|
133
|
+
sessionLock,
|
|
130
134
|
),
|
|
131
135
|
reloadConfig: (
|
|
132
136
|
ctx?: ExtensionContext,
|
|
@@ -204,6 +208,7 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
204
208
|
}
|
|
205
209
|
selectedProfile = resolvedProfile;
|
|
206
210
|
routerEnabled = true;
|
|
211
|
+
sessionLock = undefined;
|
|
207
212
|
persistState();
|
|
208
213
|
actions.updateStatus(ctx);
|
|
209
214
|
return true;
|
|
@@ -253,6 +258,12 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
253
258
|
set accumulatedCost(v) {
|
|
254
259
|
accumulatedCost = v;
|
|
255
260
|
},
|
|
261
|
+
get sessionLock() {
|
|
262
|
+
return sessionLock;
|
|
263
|
+
},
|
|
264
|
+
set sessionLock(v) {
|
|
265
|
+
sessionLock = v;
|
|
266
|
+
},
|
|
256
267
|
},
|
|
257
268
|
{
|
|
258
269
|
persistState,
|
|
@@ -290,6 +301,7 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
290
301
|
? `${ctx.model.provider}/${ctx.model.id}`
|
|
291
302
|
: lastNonRouterModel;
|
|
292
303
|
lastDecision = undefined;
|
|
304
|
+
sessionLock = undefined;
|
|
293
305
|
|
|
294
306
|
const entries = ctx.sessionManager.getBranch() as CustomSessionEntry[];
|
|
295
307
|
const savedState = entries
|
|
@@ -322,6 +334,12 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
322
334
|
: [];
|
|
323
335
|
lastNonRouterModel = savedState.lastNonRouterModel ?? lastNonRouterModel;
|
|
324
336
|
accumulatedCost = savedState.accumulatedCost ?? 0;
|
|
337
|
+
if (
|
|
338
|
+
savedState.sessionLock &&
|
|
339
|
+
savedState.sessionLock.profile === selectedProfile
|
|
340
|
+
) {
|
|
341
|
+
sessionLock = { ...savedState.sessionLock };
|
|
342
|
+
}
|
|
325
343
|
}
|
|
326
344
|
|
|
327
345
|
await actions.ensureValidActiveRouterProfile(ctx);
|
|
@@ -432,6 +450,9 @@ const routerExtension = (pi: ExtensionAPI) => {
|
|
|
432
450
|
}
|
|
433
451
|
|
|
434
452
|
routerEnabled = true;
|
|
453
|
+
if (selectedProfile !== profileName) {
|
|
454
|
+
sessionLock = undefined;
|
|
455
|
+
}
|
|
435
456
|
selectedProfile = profileName;
|
|
436
457
|
} else {
|
|
437
458
|
routerEnabled = false;
|