ultimate-pi 0.18.1 → 0.19.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-debate-plan/SKILL.md +1 -1
- package/.agents/skills/harness-decisions/SKILL.md +1 -2
- package/.agents/skills/harness-governor/SKILL.md +6 -5
- package/.pi/PACKAGING.md +4 -4
- package/.pi/SYSTEM.md +54 -120
- package/.pi/agents/harness/incident-recorder.md +0 -1
- package/.pi/agents/harness/planning/decompose.md +0 -2
- package/.pi/agents/harness/planning/execution-plan-author.md +0 -2
- package/.pi/agents/harness/planning/hypothesis-validator.md +0 -2
- package/.pi/agents/harness/planning/hypothesis.md +0 -2
- package/.pi/agents/harness/planning/implementation-researcher.md +0 -2
- package/.pi/agents/harness/planning/plan-adversary.md +0 -2
- package/.pi/agents/harness/planning/plan-evaluator.md +1 -3
- package/.pi/agents/harness/planning/planning-context.md +0 -2
- package/.pi/agents/harness/planning/review-integrator.md +0 -2
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +0 -2
- package/.pi/agents/harness/planning/stack-researcher.md +0 -2
- package/.pi/agents/harness/reviewing/adversary.md +0 -2
- package/.pi/agents/harness/reviewing/evaluator.md +0 -2
- package/.pi/agents/harness/reviewing/tie-breaker.md +0 -2
- package/.pi/agents/harness/running/executor.md +0 -2
- package/.pi/agents/harness/sentrux-bootstrap.md +0 -1
- package/.pi/agents/harness/sentrux-steward.md +0 -2
- package/.pi/agents/harness/trace-librarian.md +0 -1
- package/.pi/extensions/00-posthog-network-bootstrap.ts +1 -1
- package/.pi/extensions/agt-kill-switch.ts +57 -0
- package/.pi/extensions/agt-prompt-guard.ts +32 -0
- package/.pi/extensions/custom-footer.ts +46 -145
- package/.pi/extensions/custom-header.ts +1 -1
- package/.pi/extensions/custom-system-prompt.ts +1 -1
- package/.pi/extensions/debate-orchestrator.ts +6 -6
- package/.pi/extensions/harness-ask-user.ts +7 -7
- package/.pi/extensions/harness-debate-tools.ts +26 -42
- package/.pi/extensions/harness-lens.ts +94 -0
- package/.pi/extensions/harness-plan-approval.ts +11 -11
- package/.pi/extensions/harness-run-context.ts +1070 -876
- package/.pi/extensions/harness-subagent-governance.ts +8 -0
- package/.pi/extensions/harness-subagent-submit.ts +34 -163
- package/.pi/extensions/harness-subagents.ts +3 -3
- package/.pi/extensions/harness-telemetry.ts +2 -2
- package/.pi/extensions/harness-web-tools.ts +2 -2
- package/.pi/extensions/policy-gate.ts +25 -5
- package/.pi/extensions/sentrux-rules-sync.ts +1 -1
- package/.pi/extensions/subagent-governance.ts +92 -0
- package/.pi/extensions/trace-recorder.ts +1 -1
- package/.pi/extensions/{ultimate-pi-vcc.ts → vcc-compaction.ts} +1 -1
- package/.pi/harness/README.md +6 -2
- package/.pi/harness/agents.manifest.json +22 -25
- package/.pi/harness/agents.policy.yaml +275 -0
- package/.pi/harness/docs/adrs/0030-inhouse-vcc-compaction.md +1 -1
- package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +1 -1
- package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
- package/.pi/harness/docs/adrs/0046-agt-policy-engine.md +51 -0
- package/.pi/harness/docs/adrs/0047-agt-layered-security.md +39 -0
- package/.pi/harness/docs/adrs/0048-tool-call-hook-order.md +25 -0
- package/.pi/harness/docs/adrs/0049-agents-policy-manifest.md +36 -0
- package/.pi/harness/docs/adrs/README.md +5 -0
- package/.pi/harness/evolution/README.md +1 -2
- package/.pi/harness/examples/agents.policy.project.yaml +19 -0
- package/.pi/harness/examples/policies/custom-deny-bash.yaml +9 -0
- package/.pi/harness/policies/bash-denylists.yaml +5 -0
- package/.pi/harness/policies/defaults.yaml +51 -0
- package/.pi/harness/policies/orchestrator.yaml +18 -0
- package/.pi/harness/policies/phases.yaml +10 -0
- package/.pi/harness/policies/roles.yaml +5 -0
- package/.pi/harness/policies/web-guard.yaml +5 -0
- package/.pi/harness/policies/workflow-sequences.yaml +9 -0
- package/.pi/harness/sentrux/architecture.manifest.json +26 -4
- package/.pi/harness/specs/observation.schema.json +2 -1
- package/.pi/lib/agents-policy.d.mts +70 -0
- package/.pi/lib/agents-policy.mjs +325 -0
- package/.pi/lib/agents-policy.ts +19 -0
- package/.pi/lib/agt/audit-run-sink.ts +52 -0
- package/.pi/lib/agt/build-evaluation-context.ts +285 -0
- package/.pi/lib/agt/config.ts +28 -0
- package/.pi/lib/agt/delegation.ts +69 -0
- package/.pi/lib/agt/evaluate-policy.ts +56 -0
- package/.pi/lib/agt/identity-registry.ts +41 -0
- package/.pi/lib/agt/index.ts +55 -0
- package/.pi/lib/agt/kill-switch-state.ts +11 -0
- package/.pi/lib/agt/legacy-evaluate.ts +101 -0
- package/.pi/lib/agt/policy-engine.ts +154 -0
- package/.pi/lib/agt/rings.ts +21 -0
- package/.pi/lib/agt/sre-hooks.ts +45 -0
- package/.pi/lib/agt/trust-run-store.ts +26 -0
- package/.pi/lib/agt/workflow-history.ts +29 -0
- package/.pi/lib/agt-governance-active.ts +14 -0
- package/.pi/lib/agt-tool-guard.ts +78 -0
- package/.pi/lib/ask-user/dialog.ts +314 -0
- package/.pi/{extensions/lib → lib}/debate-bus-core.ts +10 -10
- package/.pi/{extensions/lib → lib}/debate-bus-state.ts +1 -1
- package/.pi/{extensions/lib → lib}/extension-load-guard.ts +13 -2
- package/.pi/lib/harness-agt-tool-guard.ts +5 -0
- package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +1 -1
- package/.pi/lib/harness-debate-core-deps.ts +14 -0
- package/.pi/lib/harness-debate-workflow-deps.ts +43 -0
- package/.pi/lib/harness-lens/.gitattributes +1 -0
- package/.pi/lib/harness-lens/clients/edit-autopatch.ts +88 -0
- package/.pi/lib/harness-lens/clients/file-kinds.ts +380 -0
- package/.pi/lib/harness-lens/clients/file-time.ts +215 -0
- package/.pi/lib/harness-lens/clients/file-utils.ts +484 -0
- package/.pi/lib/harness-lens/clients/format-service.ts +276 -0
- package/.pi/lib/harness-lens/clients/formatters.ts +1000 -0
- package/.pi/lib/harness-lens/clients/git-guard.ts +31 -0
- package/.pi/lib/harness-lens/clients/indent-retarget.ts +90 -0
- package/.pi/lib/harness-lens/clients/installer/index.ts +2368 -0
- package/.pi/lib/harness-lens/clients/latency-logger.ts +80 -0
- package/.pi/lib/harness-lens/clients/lens-config.ts +43 -0
- package/.pi/lib/harness-lens/clients/lens-events.ts +164 -0
- package/.pi/lib/harness-lens/clients/lsp/aggregation.ts +91 -0
- package/.pi/lib/harness-lens/clients/lsp/client.ts +1466 -0
- package/.pi/lib/harness-lens/clients/lsp/config.ts +216 -0
- package/.pi/lib/harness-lens/clients/lsp/edits.ts +297 -0
- package/.pi/lib/harness-lens/clients/lsp/index.ts +1355 -0
- package/.pi/lib/harness-lens/clients/lsp/interactive-install.ts +424 -0
- package/.pi/lib/harness-lens/clients/lsp/language.ts +223 -0
- package/.pi/lib/harness-lens/clients/lsp/launch.ts +939 -0
- package/.pi/lib/harness-lens/clients/lsp/lsp-index.ts +11 -0
- package/.pi/lib/harness-lens/clients/lsp/path-utils.ts +12 -0
- package/.pi/lib/harness-lens/clients/lsp/server-strategies.ts +81 -0
- package/.pi/lib/harness-lens/clients/lsp/server.ts +1971 -0
- package/.pi/lib/harness-lens/clients/path-utils.ts +182 -0
- package/.pi/lib/harness-lens/clients/pipeline.ts +360 -0
- package/.pi/lib/harness-lens/clients/project-profile.ts +117 -0
- package/.pi/lib/harness-lens/clients/runtime-agent-end.ts +112 -0
- package/.pi/lib/harness-lens/clients/runtime-config.ts +33 -0
- package/.pi/lib/harness-lens/clients/runtime-coordinator.ts +186 -0
- package/.pi/lib/harness-lens/clients/runtime-tool-result.ts +171 -0
- package/.pi/lib/harness-lens/clients/safe-spawn.ts +339 -0
- package/.pi/lib/harness-lens/clients/secrets-scanner.ts +214 -0
- package/.pi/lib/harness-lens/clients/tool-policy.ts +2072 -0
- package/.pi/lib/harness-lens/clients/types.ts +59 -0
- package/.pi/lib/harness-lens/clients/widget-state.ts +283 -0
- package/.pi/lib/harness-lens/index.ts +532 -0
- package/.pi/lib/harness-lens/tools/lsp-diagnostics.ts +706 -0
- package/.pi/lib/harness-lens/tools/lsp-navigation.ts +1246 -0
- package/.pi/{extensions/lib → lib}/harness-posthog.ts +3 -0
- package/.pi/lib/harness-run-context-responses.ts +9 -0
- package/.pi/lib/harness-run-context.ts +0 -2
- package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +1 -0
- package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +1 -1
- package/.pi/lib/harness-subagent-auth.ts +51 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +10 -7
- package/.pi/{extensions/lib → lib}/harness-subagent-submit-pipeline.ts +3 -3
- package/.pi/lib/harness-subagent-submit-register.ts +163 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-submit-registry.ts +1 -37
- package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
- package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/create-plan.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/format-plan.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/plan-review.ts +162 -201
- package/.pi/{extensions/lib → lib}/plan-approval/render.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/resolve-disk.ts +2 -2
- package/.pi/{extensions/lib → lib}/plan-approval/types.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-approval/validate.ts +3 -3
- package/.pi/{extensions/lib → lib}/plan-debate-envelope.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-debate-gate.ts +1 -1
- package/.pi/{extensions/lib → lib}/plan-debate-lane.ts +1 -4
- package/.pi/{extensions/lib → lib}/plan-messenger.ts +1 -1
- package/.pi/prompts/harness-plan.md +1 -1
- package/.pi/prompts/harness-setup.md +37 -64
- package/.pi/scripts/README.md +2 -5
- package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
- package/.pi/scripts/harness-agents-manifest.mjs +60 -3
- package/.pi/scripts/harness-agt-doctor.ts +36 -0
- package/.pi/scripts/harness-cli-verify.sh +9 -2
- package/.pi/scripts/harness-verify.mjs +113 -39
- package/.pi/scripts/harness-web-policy-guard.mjs +2 -2
- package/.pi/scripts/validate-plan-dag.mjs +65 -74
- package/.pi/scripts/vendor-pi-vcc-settings.stub.ts +2 -2
- package/.pi/scripts/vendor-sync-pi-vcc.sh +1 -1
- package/.pi/skills/architecture/broker-domain/SKILL.md +65 -0
- package/.pi/skills/architecture/cqrs/SKILL.md +63 -0
- package/.pi/skills/architecture/event-driven/SKILL.md +60 -0
- package/.pi/skills/architecture/hexagonal-ports-adapters/SKILL.md +66 -0
- package/.pi/skills/architecture/layered/SKILL.md +68 -0
- package/.pi/skills/architecture/microkernel/SKILL.md +62 -0
- package/.pi/skills/architecture/microservices/SKILL.md +64 -0
- package/.pi/skills/architecture/modular-monolith/SKILL.md +65 -0
- package/.pi/skills/architecture/orchestration-driven-soa/SKILL.md +61 -0
- package/.pi/skills/architecture/pipeline/SKILL.md +63 -0
- package/.pi/skills/architecture/service-based/SKILL.md +64 -0
- package/.pi/skills/architecture/service-mesh/SKILL.md +60 -0
- package/.pi/skills/architecture/space-based/SKILL.md +60 -0
- package/.pi/skills/ast-grep/SKILL.md +40 -321
- package/.pi/skills/delivery/debugging-discipline/SKILL.md +36 -0
- package/.pi/skills/delivery/documentation-update/SKILL.md +33 -0
- package/.pi/skills/delivery/requirements-to-implementation/SKILL.md +34 -0
- package/.pi/skills/delivery/risk-based-verification/SKILL.md +43 -0
- package/.pi/skills/delivery/tradeoff-analysis/SKILL.md +34 -0
- package/.pi/skills/engineering/api-contract-design/SKILL.md +38 -0
- package/.pi/skills/engineering/cohesion-coupling/SKILL.md +43 -0
- package/.pi/skills/engineering/complexity-control/SKILL.md +31 -0
- package/.pi/skills/engineering/defensive-programming/SKILL.md +38 -0
- package/.pi/skills/engineering/dependency-management/SKILL.md +29 -0
- package/.pi/skills/engineering/domain-modeling/SKILL.md +32 -0
- package/.pi/skills/engineering/error-handling/SKILL.md +37 -0
- package/.pi/skills/engineering/legacy-code-seams/SKILL.md +35 -0
- package/.pi/skills/engineering/naming-and-intent/SKILL.md +29 -0
- package/.pi/skills/engineering/refactoring-safe-evolution/SKILL.md +35 -0
- package/.pi/skills/engineering/routine-function-design/SKILL.md +34 -0
- package/.pi/skills/engineering/small-change-discipline/SKILL.md +35 -0
- package/.pi/skills/lsp-navigation/SKILL.md +89 -0
- package/.pi/skills/quality/code-review-self-check/SKILL.md +35 -0
- package/.pi/skills/quality/privacy-data-handling/SKILL.md +26 -0
- package/.pi/skills/quality/security-review/SKILL.md +34 -0
- package/.pi/skills/quality/test-strategy/SKILL.md +33 -0
- package/.pi/skills/quality/testability-design/SKILL.md +33 -0
- package/.pi/skills/systems/concurrency-safety/SKILL.md +32 -0
- package/.pi/skills/systems/data-modeling-migrations/SKILL.md +31 -0
- package/.pi/skills/systems/observability-instrumentation/SKILL.md +32 -0
- package/.pi/skills/systems/performance-measurement/SKILL.md +35 -0
- package/.pi/skills/systems/reliability-design/SKILL.md +32 -0
- package/.sentrux/rules.toml +20 -4
- package/AGENTS.md +5 -0
- package/CHANGELOG.md +14 -0
- package/README.md +3 -12
- package/THIRD_PARTY_NOTICES.md +12 -21
- package/package.json +15 -7
- package/vendor/pi-subagents/src/agents.ts +45 -1
- package/vendor/pi-subagents/src/subagents.ts +866 -811
- package/vendor/pi-vcc/src/core/brief.ts +68 -99
- package/vendor/pi-vcc/src/core/settings.ts +2 -2
- package/.agents/skills/caveman/SKILL.md +0 -67
- package/.pi/agents/harness/meta-optimizer.md +0 -36
- package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
- package/.pi/extensions/lib/harness-subagent-auth.ts +0 -207
- package/.pi/extensions/lib/harness-subagent-policy.ts +0 -236
- package/.pi/extensions/pi-model-router-harness.ts +0 -42
- package/.pi/harness/evolution/meta-optimizer.mjs +0 -99
- package/.pi/harness/specs/router-tuning-proposal.schema.json +0 -114
- package/.pi/model-router.example.json +0 -36
- package/.pi/prompts/harness-critic.md +0 -10
- package/.pi/prompts/harness-eval.md +0 -10
- package/.pi/prompts/harness-router-tune.md +0 -52
- package/.pi/scripts/harness-generate-model-router.mjs +0 -327
- package/.pi/scripts/harness-model-router-routing.test.mjs +0 -97
- package/.pi/scripts/harness-sync-model-router.mjs +0 -97
- package/.pi/scripts/vendor-sync-pi-model-router.sh +0 -47
- package/vendor/pi-model-router/.prettierignore +0 -4
- package/vendor/pi-model-router/.prettierrc +0 -5
- package/vendor/pi-model-router/AGENTS.md +0 -39
- package/vendor/pi-model-router/LICENSE +0 -21
- package/vendor/pi-model-router/README.md +0 -99
- package/vendor/pi-model-router/UPSTREAM_PIN.md +0 -10
- package/vendor/pi-model-router/docs/ARCHITECTURE.md +0 -54
- package/vendor/pi-model-router/extensions/commands.ts +0 -720
- package/vendor/pi-model-router/extensions/config.ts +0 -348
- package/vendor/pi-model-router/extensions/constants.ts +0 -1
- package/vendor/pi-model-router/extensions/index.ts +0 -478
- package/vendor/pi-model-router/extensions/provider.ts +0 -580
- package/vendor/pi-model-router/extensions/routing.ts +0 -564
- package/vendor/pi-model-router/extensions/state.ts +0 -52
- package/vendor/pi-model-router/extensions/types.ts +0 -95
- package/vendor/pi-model-router/extensions/ui.ts +0 -144
- package/vendor/pi-model-router/model-router.example.json +0 -48
- package/vendor/pi-model-router/package.json +0 -48
- package/vendor/pi-model-router/tsconfig.json +0 -16
- /package/.pi/{prompts → harness/docs}/planning-rubrics.md +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/fallback.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/render.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/schema.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/types.ts +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/validate-core.mjs +0 -0
- /package/.pi/{extensions/lib → lib}/ask-user/validate.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-cocoindex-refresh.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-paths.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-spawn-budget.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-vcc-settings.ts +0 -0
- /package/.pi/{extensions/lib → lib}/harness-web/run-cli.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval/dialog.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval/schema.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-approval-readiness.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-eligibility.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-focus.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-id.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-lanes.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-round-status.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-debate-write-guard.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-review-gate.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-review-integrator-rules.ts +0 -0
- /package/.pi/{extensions/lib → lib}/plan-scope-guard.ts +0 -0
- /package/.pi/{extensions/lib → lib}/posthog-client.ts +0 -0
- /package/.pi/{extensions/lib → lib}/posthog-node.d.ts +0 -0
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"defaultProfile": "auto",
|
|
3
|
-
"debug": false,
|
|
4
|
-
"classifierModel": "openai/gpt-5.4-nano",
|
|
5
|
-
"phaseBias": 0.5,
|
|
6
|
-
"maxSessionBudget": 1.0,
|
|
7
|
-
"largeContextThreshold": 100000,
|
|
8
|
-
"rules": [
|
|
9
|
-
{
|
|
10
|
-
"matches": ["deploy", "production", "release"],
|
|
11
|
-
"tier": "high",
|
|
12
|
-
"reason": "Safety check for production tasks"
|
|
13
|
-
},
|
|
14
|
-
{ "matches": "changelog", "tier": "low" }
|
|
15
|
-
],
|
|
16
|
-
"profiles": {
|
|
17
|
-
"auto": {
|
|
18
|
-
"high": {
|
|
19
|
-
"model": "openai/gpt-5.5",
|
|
20
|
-
"thinking": "high",
|
|
21
|
-
"fallbacks": ["openai/gpt-5.4-nano"]
|
|
22
|
-
},
|
|
23
|
-
"medium": { "model": "openai/gpt-5.5", "thinking": "medium" },
|
|
24
|
-
"low": { "model": "openai/gpt-5.5", "thinking": "low" }
|
|
25
|
-
},
|
|
26
|
-
"opencode-go": {
|
|
27
|
-
"high": {
|
|
28
|
-
"model": "opencode-go/qwen3.6-plus",
|
|
29
|
-
"thinking": "high",
|
|
30
|
-
"fallbacks": ["opencode-go/deepseek-v4-flash"]
|
|
31
|
-
},
|
|
32
|
-
"medium": { "model": "opencode-go/qwen3.6-plus", "thinking": "medium" },
|
|
33
|
-
"low": { "model": "opencode-go/qwen3.6-plus", "thinking": "low" }
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Deprecated alias — use /harness-review (includes adversary phase)."
|
|
3
|
-
argument-hint: "[--run <run-id>] [--trace <trace-ref>] [--risk low|med|high]"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# harness-critic
|
|
7
|
-
|
|
8
|
-
**This command is deprecated.** Run **`/harness-review`** instead — Phase 4 runs `harness/reviewing/adversary` after benchmark and policy verdict pass (skip with `--quick`).
|
|
9
|
-
|
|
10
|
-
If you must continue this turn only: forward to `/harness-review` with the same `$ARGUMENTS` (omit `--quick` if you need adversary). Do not spawn adversary in isolation unless the user explicitly requested adversary-only review.
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "Deprecated alias — use /harness-review (post-run master orchestrator)."
|
|
3
|
-
argument-hint: "[--run <run-id>] [--quick] [--trace <trace-ref>]"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# harness-eval
|
|
7
|
-
|
|
8
|
-
**This command is deprecated.** Run **`/harness-review`** instead — it orchestrates deterministic gates, benchmark eval, policy verdict, and adversary review in one flow (ADR 0039).
|
|
9
|
-
|
|
10
|
-
If you must continue this turn only: forward all work to `/harness-review` with the same arguments (`$ARGUMENTS`). Do not spawn a separate benchmark-only pass unless the user explicitly asked for benchmark-only diagnostics.
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Propose model-router updates from eval evidence; apply only with explicit approval.
|
|
3
|
-
argument-hint: "--evidence <evidence.json> --candidate <candidate-router.json> [--proposal <out.json>]"
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# harness-router-tune
|
|
7
|
-
|
|
8
|
-
Orchestrator — scripts + `harness/meta-optimizer` spawn. **Never** write `.pi/model-router.json` directly.
|
|
9
|
-
|
|
10
|
-
## Step 0 — Parse arguments
|
|
11
|
-
|
|
12
|
-
- required: `--evidence <evidence.json>`, `--candidate <candidate-router.json>`
|
|
13
|
-
- optional: `--proposal <out.json>`
|
|
14
|
-
|
|
15
|
-
If missing required args:
|
|
16
|
-
|
|
17
|
-
`Usage: /harness-router-tune --evidence <path> --candidate <path> [--proposal <out.json>]`
|
|
18
|
-
|
|
19
|
-
## Orchestration (required)
|
|
20
|
-
|
|
21
|
-
1. Parent validates evidence paths exist.
|
|
22
|
-
2. Optionally spawn:
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
subagent({ agentScope: "both", agent: "harness/meta-optimizer", task: "mode: tune, evidence paths…" })
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
3. Parent runs proposal script:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
node .pi/harness/router/propose-router-tuning.mjs \
|
|
32
|
-
--evidence <evidence.json> \
|
|
33
|
-
--candidate <candidate-router.json> \
|
|
34
|
-
--proposal-out .pi/harness/router/proposals/<id>.json
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
4. `ask_user` approve / reject / edit (harness-decisions).
|
|
38
|
-
5. Apply only after approval:
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
node .pi/harness/router/apply-router-proposal.mjs \
|
|
42
|
-
--proposal .pi/harness/router/proposals/<id>.json \
|
|
43
|
-
--approve-by "<human>" \
|
|
44
|
-
--justification "<reason>" \
|
|
45
|
-
--write
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
## Completion
|
|
49
|
-
|
|
50
|
-
- `tuning_status`: `proposed`, `human_required`, or `rejected`
|
|
51
|
-
- Evidence gate summary
|
|
52
|
-
- Confirm `.pi/model-router.json` was not mutated without apply script
|
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Generate `.pi/model-router.json` from Pi's authenticated providers (auth.json + env),
|
|
4
|
-
* not from raw env-var heuristics alone.
|
|
5
|
-
*
|
|
6
|
-
* Uses @earendil-works/pi-coding-agent ModelRegistry.getAvailable() — same source as /login.
|
|
7
|
-
*
|
|
8
|
-
* Usage: node harness-generate-model-router.mjs [--force] [--dry-run]
|
|
9
|
-
* --force overwrite existing .pi/model-router.json
|
|
10
|
-
* --dry-run print JSON to stdout, do not write
|
|
11
|
-
*
|
|
12
|
-
* Requires @earendil-works/pi-coding-agent (peer of ultimate-pi; bundled with pi).
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
16
|
-
import { createRequire } from "node:module";
|
|
17
|
-
import { dirname, join } from "node:path";
|
|
18
|
-
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
|
-
|
|
20
|
-
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
const UP_PKG = join(SCRIPT_DIR, "..", "..");
|
|
22
|
-
const OUT_PATH = join(process.cwd(), ".pi", "model-router.json");
|
|
23
|
-
|
|
24
|
-
const PROVIDER_PRIORITY = [
|
|
25
|
-
"openai",
|
|
26
|
-
"opencode-go",
|
|
27
|
-
"anthropic",
|
|
28
|
-
"google",
|
|
29
|
-
"openrouter",
|
|
30
|
-
"groq",
|
|
31
|
-
"mistral",
|
|
32
|
-
"amazon",
|
|
33
|
-
];
|
|
34
|
-
|
|
35
|
-
/** Substring hints per tier (first match in available ids wins). */
|
|
36
|
-
const TIER_HINTS = {
|
|
37
|
-
high: [
|
|
38
|
-
"gpt-5.5-pro",
|
|
39
|
-
"deepseek-v4-pro",
|
|
40
|
-
"gpt-5.4-pro",
|
|
41
|
-
"claude-opus",
|
|
42
|
-
"sonnet-4",
|
|
43
|
-
"gemini-2.5-pro",
|
|
44
|
-
"pro",
|
|
45
|
-
],
|
|
46
|
-
medium: [
|
|
47
|
-
"gpt-5.5",
|
|
48
|
-
"qwen3.6-plus",
|
|
49
|
-
"kimi-k2.6",
|
|
50
|
-
"gpt-5.4",
|
|
51
|
-
"claude-sonnet",
|
|
52
|
-
"gemini-flash",
|
|
53
|
-
"plus",
|
|
54
|
-
],
|
|
55
|
-
low: [
|
|
56
|
-
"deepseek-v4-flash",
|
|
57
|
-
"gpt-5.4-nano",
|
|
58
|
-
"haiku",
|
|
59
|
-
"flash-lite",
|
|
60
|
-
"flash",
|
|
61
|
-
"mini",
|
|
62
|
-
],
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
function fail(msg) {
|
|
66
|
-
console.error(`harness-generate-model-router: ${msg}`);
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async function loadPiCodingAgent() {
|
|
71
|
-
const scopes = ["@earendil-works", "@mariozechner"];
|
|
72
|
-
const agentRoots = scopes.flatMap((scope) => [
|
|
73
|
-
join(UP_PKG, "node_modules", scope, "pi-coding-agent"),
|
|
74
|
-
join(UP_PKG, ".pi", "npm", "node_modules", scope, "pi-coding-agent"),
|
|
75
|
-
]);
|
|
76
|
-
for (const root of agentRoots) {
|
|
77
|
-
const entry = join(root, "dist", "index.js");
|
|
78
|
-
if (existsSync(entry)) {
|
|
79
|
-
return import(pathToFileURL(entry).href);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
for (const spec of ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"]) {
|
|
83
|
-
for (const base of [UP_PKG, process.cwd()]) {
|
|
84
|
-
try {
|
|
85
|
-
const req = createRequire(join(base, "package.json"));
|
|
86
|
-
return req(spec);
|
|
87
|
-
} catch {
|
|
88
|
-
/* try next */
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
fail(
|
|
93
|
-
"@earendil-works/pi-coding-agent not found (install pi or npm i in ultimate-pi). Peer: @earendil-works/pi-coding-agent",
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
function canonicalRef(provider, modelId) {
|
|
98
|
-
return `${provider}/${modelId}`;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function pickTierModel(models, tier) {
|
|
102
|
-
const hints = TIER_HINTS[tier];
|
|
103
|
-
for (const hint of hints) {
|
|
104
|
-
const exact = models.find((m) => m.id === hint);
|
|
105
|
-
if (exact) return canonicalRef(exact.provider, exact.id);
|
|
106
|
-
}
|
|
107
|
-
for (const hint of hints) {
|
|
108
|
-
const match = models.find((m) => m.id.includes(hint));
|
|
109
|
-
if (match) return canonicalRef(match.provider, match.id);
|
|
110
|
-
}
|
|
111
|
-
if (models.length === 0) return null;
|
|
112
|
-
if (tier === "high") {
|
|
113
|
-
const reasoning = models.find((m) => m.reasoning);
|
|
114
|
-
if (reasoning) return canonicalRef(reasoning.provider, reasoning.id);
|
|
115
|
-
}
|
|
116
|
-
if (tier === "low") {
|
|
117
|
-
return canonicalRef(models[models.length - 1].provider, models[models.length - 1].id);
|
|
118
|
-
}
|
|
119
|
-
return canonicalRef(models[0].provider, models[0].id);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function modelsForProvider(available, provider) {
|
|
123
|
-
return available.filter((m) => m.provider === provider);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function choosePrimaryProvider(available) {
|
|
127
|
-
const byProvider = new Map();
|
|
128
|
-
for (const m of available) {
|
|
129
|
-
if (!byProvider.has(m.provider)) byProvider.set(m.provider, []);
|
|
130
|
-
byProvider.get(m.provider).push(m);
|
|
131
|
-
}
|
|
132
|
-
for (const p of PROVIDER_PRIORITY) {
|
|
133
|
-
if (byProvider.has(p)) return { provider: p, models: byProvider.get(p) };
|
|
134
|
-
}
|
|
135
|
-
const first = [...byProvider.keys()].sort()[0];
|
|
136
|
-
return { provider: first, models: byProvider.get(first) ?? [] };
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function buildFallbacks(available, primaryProvider, highModel) {
|
|
140
|
-
const fallbacks = [];
|
|
141
|
-
for (const p of ["anthropic", "google", "openai", "opencode-go"]) {
|
|
142
|
-
if (p === primaryProvider) continue;
|
|
143
|
-
const alt = available.filter((m) => m.provider === p);
|
|
144
|
-
if (alt.length === 0) continue;
|
|
145
|
-
const ref = pickTierModel(alt, "medium");
|
|
146
|
-
if (ref && ref !== highModel) fallbacks.push(ref);
|
|
147
|
-
}
|
|
148
|
-
return fallbacks.slice(0, 3);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Session-locked router: one model SKU per profile; tiers vary thinking only. */
|
|
152
|
-
function buildRoutedProfile(available, provider) {
|
|
153
|
-
const models = modelsForProvider(available, provider);
|
|
154
|
-
if (models.length === 0) return null;
|
|
155
|
-
const sku =
|
|
156
|
-
pickTierModel(models, "medium") ??
|
|
157
|
-
pickTierModel(models, "high") ??
|
|
158
|
-
pickTierModel(models, "low");
|
|
159
|
-
if (!sku) return null;
|
|
160
|
-
const fallbacks = buildFallbacks(available, provider, sku);
|
|
161
|
-
const high = { model: sku, thinking: "high" };
|
|
162
|
-
if (fallbacks.length) high.fallbacks = fallbacks;
|
|
163
|
-
return {
|
|
164
|
-
high,
|
|
165
|
-
medium: { model: sku, thinking: "medium" },
|
|
166
|
-
low: { model: sku, thinking: "low" },
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function addCheapDeepProfiles(profiles, available, provider) {
|
|
171
|
-
const models = modelsForProvider(available, provider);
|
|
172
|
-
if (models.length === 0) return;
|
|
173
|
-
const sku =
|
|
174
|
-
pickTierModel(models, "medium") ??
|
|
175
|
-
pickTierModel(models, "high") ??
|
|
176
|
-
pickTierModel(models, "low");
|
|
177
|
-
if (!sku) return;
|
|
178
|
-
const fallbacks = buildFallbacks(available, provider, sku);
|
|
179
|
-
const deepHigh = { model: sku, thinking: "xhigh" };
|
|
180
|
-
if (fallbacks.length) deepHigh.fallbacks = fallbacks;
|
|
181
|
-
profiles.cheap = {
|
|
182
|
-
high: { model: sku, thinking: "low" },
|
|
183
|
-
medium: { model: sku, thinking: "off" },
|
|
184
|
-
low: { model: sku, thinking: "off" },
|
|
185
|
-
};
|
|
186
|
-
profiles.deep = {
|
|
187
|
-
high: deepHigh,
|
|
188
|
-
medium: { model: sku, thinking: "medium" },
|
|
189
|
-
low: { model: sku, thinking: "low" },
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
function resolveClassifierModel(available) {
|
|
194
|
-
const openaiModels = modelsForProvider(available, "openai");
|
|
195
|
-
if (openaiModels.length > 0) {
|
|
196
|
-
return (
|
|
197
|
-
pickTierModel(openaiModels, "low") ??
|
|
198
|
-
canonicalRef(openaiModels[openaiModels.length - 1].provider, openaiModels[openaiModels.length - 1].id)
|
|
199
|
-
);
|
|
200
|
-
}
|
|
201
|
-
const { models } = choosePrimaryProvider(available);
|
|
202
|
-
return pickTierModel(models, "medium");
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
/** OpenAI-backed default profile name exposed as `router/auto`. */
|
|
206
|
-
const OPENAI_PROFILE_NAME = "auto";
|
|
207
|
-
|
|
208
|
-
function routerProfileName(provider) {
|
|
209
|
-
return provider === "openai" ? OPENAI_PROFILE_NAME : provider;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
function resolveDefaultProfile(profiles) {
|
|
213
|
-
if (profiles[OPENAI_PROFILE_NAME]) return OPENAI_PROFILE_NAME;
|
|
214
|
-
if (profiles["opencode-go"]) return "opencode-go";
|
|
215
|
-
return (
|
|
216
|
-
Object.keys(profiles).find((name) => name !== "cheap" && name !== "deep") ??
|
|
217
|
-
OPENAI_PROFILE_NAME
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
async function main() {
|
|
222
|
-
const force = process.argv.includes("--force");
|
|
223
|
-
const dryRun = process.argv.includes("--dry-run");
|
|
224
|
-
|
|
225
|
-
if (existsSync(OUT_PATH) && !force) {
|
|
226
|
-
console.log(
|
|
227
|
-
"✓ .pi/model-router.json already exists — preserving (use --force to regenerate)",
|
|
228
|
-
);
|
|
229
|
-
process.exit(0);
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
const { AuthStorage, ModelRegistry } = await loadPiCodingAgent();
|
|
233
|
-
const authStorage = AuthStorage.create();
|
|
234
|
-
const modelRegistry = ModelRegistry.create(authStorage);
|
|
235
|
-
const available = await modelRegistry.getAvailable();
|
|
236
|
-
|
|
237
|
-
if (available.length === 0) {
|
|
238
|
-
console.log(
|
|
239
|
-
"✗ No authenticated Pi providers — skip model-router.json",
|
|
240
|
-
);
|
|
241
|
-
console.log(
|
|
242
|
-
" Log in inside pi: /login (or set API keys in ~/.pi/agent/auth.json)",
|
|
243
|
-
);
|
|
244
|
-
const providers = authStorage.list();
|
|
245
|
-
if (providers.length > 0) {
|
|
246
|
-
console.log(
|
|
247
|
-
` Stored providers in auth.json (may need refresh): ${providers.join(", ")}`,
|
|
248
|
-
);
|
|
249
|
-
}
|
|
250
|
-
process.exit(0);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const profiles = {};
|
|
254
|
-
for (const provider of ["openai", "opencode-go"]) {
|
|
255
|
-
const profile = buildRoutedProfile(available, provider);
|
|
256
|
-
if (profile) profiles[routerProfileName(provider)] = profile;
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (Object.keys(profiles).length === 0) {
|
|
260
|
-
const { provider: primaryProvider, models: primaryModels } =
|
|
261
|
-
choosePrimaryProvider(available);
|
|
262
|
-
const profile = buildRoutedProfile(available, primaryProvider);
|
|
263
|
-
if (!profile) {
|
|
264
|
-
fail("could not assign tier models from available registry");
|
|
265
|
-
}
|
|
266
|
-
profiles[primaryProvider] = profile;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
const cheapDeepSource = profiles["opencode-go"]
|
|
270
|
-
? "opencode-go"
|
|
271
|
-
: resolveDefaultProfile(profiles);
|
|
272
|
-
addCheapDeepProfiles(profiles, available, cheapDeepSource);
|
|
273
|
-
|
|
274
|
-
const defaultProfile = resolveDefaultProfile(profiles);
|
|
275
|
-
const classifierModel = resolveClassifierModel(available);
|
|
276
|
-
if (!classifierModel) {
|
|
277
|
-
fail("could not assign classifier model from available registry");
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
const config = {
|
|
281
|
-
defaultProfile,
|
|
282
|
-
debug: false,
|
|
283
|
-
classifierModel,
|
|
284
|
-
phaseBias: 0.5,
|
|
285
|
-
maxSessionBudget: 1.0,
|
|
286
|
-
largeContextThreshold: 100000,
|
|
287
|
-
rules: [
|
|
288
|
-
{
|
|
289
|
-
matches: ["deploy", "production", "release"],
|
|
290
|
-
tier: "high",
|
|
291
|
-
reason: "Safety check for production tasks",
|
|
292
|
-
},
|
|
293
|
-
{ matches: "changelog", tier: "low" },
|
|
294
|
-
],
|
|
295
|
-
profiles,
|
|
296
|
-
};
|
|
297
|
-
|
|
298
|
-
const json = `${JSON.stringify(config, null, 2)}\n`;
|
|
299
|
-
const providerSet = [...new Set(available.map((m) => m.provider))].sort();
|
|
300
|
-
const autoProfile = profiles[OPENAI_PROFILE_NAME];
|
|
301
|
-
const opencodeProfile = profiles["opencode-go"];
|
|
302
|
-
|
|
303
|
-
if (dryRun) {
|
|
304
|
-
process.stdout.write(json);
|
|
305
|
-
process.exit(0);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
mkdirSync(dirname(OUT_PATH), { recursive: true });
|
|
309
|
-
writeFileSync(OUT_PATH, json, "utf8");
|
|
310
|
-
|
|
311
|
-
console.log("✓ Generated .pi/model-router.json from Pi authenticated providers:");
|
|
312
|
-
console.log(` Default profile: ${defaultProfile}`);
|
|
313
|
-
console.log(` Classifier: ${classifierModel}`);
|
|
314
|
-
console.log(` Authenticated providers: ${providerSet.join(", ")}`);
|
|
315
|
-
console.log(` Available models: ${available.length}`);
|
|
316
|
-
if (autoProfile) {
|
|
317
|
-
console.log(` auto (openai) high: ${autoProfile.high.model}`);
|
|
318
|
-
}
|
|
319
|
-
if (opencodeProfile) {
|
|
320
|
-
console.log(` opencode-go high: ${opencodeProfile.high.model}`);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
main().catch((err) => {
|
|
325
|
-
console.error(err);
|
|
326
|
-
process.exit(1);
|
|
327
|
-
});
|
|
@@ -1,97 +0,0 @@
|
|
|
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");
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* After `.pi/model-router.json` exists, set sensible Pi defaults (`router` / `auto`)
|
|
4
|
-
* when the project has no `defaultProvider`. Does **not** add/remove npm packages
|
|
5
|
-
* — model routing ships vendored inside ultimate-pi.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
9
|
-
import { join, dirname } from "node:path";
|
|
10
|
-
|
|
11
|
-
function loadSettings(settingsPath) {
|
|
12
|
-
if (!existsSync(settingsPath)) {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
try {
|
|
16
|
-
return JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
17
|
-
} catch {
|
|
18
|
-
console.error("[harness-model-router] Invalid JSON:", settingsPath);
|
|
19
|
-
process.exit(1);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function saveSettings(settingsPath, data) {
|
|
24
|
-
mkdirSync(dirname(settingsPath), { recursive: true });
|
|
25
|
-
writeFileSync(
|
|
26
|
-
settingsPath,
|
|
27
|
-
`${JSON.stringify(data, null, "\t")}\n`,
|
|
28
|
-
"utf8",
|
|
29
|
-
);
|
|
30
|
-
}
|
|
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
|
-
|
|
44
|
-
function main() {
|
|
45
|
-
const root = process.cwd();
|
|
46
|
-
const configPath = join(root, ".pi", "model-router.json");
|
|
47
|
-
const settingsPath = join(root, ".pi", "settings.json");
|
|
48
|
-
const hasConfig = existsSync(configPath);
|
|
49
|
-
const defaultRouterProfile = readDefaultRouterProfile(configPath);
|
|
50
|
-
|
|
51
|
-
const settings = loadSettings(settingsPath);
|
|
52
|
-
if (!settings) {
|
|
53
|
-
console.warn(
|
|
54
|
-
"[harness-model-router] No .pi/settings.json — skipping (merge Step 3 first)",
|
|
55
|
-
);
|
|
56
|
-
process.exit(0);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let changed = false;
|
|
60
|
-
|
|
61
|
-
if (!hasConfig) {
|
|
62
|
-
if (settings.defaultProvider === "router") {
|
|
63
|
-
delete settings.defaultProvider;
|
|
64
|
-
delete settings.defaultModel;
|
|
65
|
-
changed = true;
|
|
66
|
-
}
|
|
67
|
-
if (changed) {
|
|
68
|
-
saveSettings(settingsPath, settings);
|
|
69
|
-
console.warn(
|
|
70
|
-
"⚠ No .pi/model-router.json — cleared router defaultProvider if present",
|
|
71
|
-
);
|
|
72
|
-
} else {
|
|
73
|
-
console.log("[harness-model-router] No config file; nothing to do");
|
|
74
|
-
}
|
|
75
|
-
process.exit(0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const noProjectDefault =
|
|
79
|
-
settings.defaultProvider == null || settings.defaultProvider === "";
|
|
80
|
-
|
|
81
|
-
if (noProjectDefault) {
|
|
82
|
-
settings.defaultProvider = "router";
|
|
83
|
-
settings.defaultModel = defaultRouterProfile;
|
|
84
|
-
changed = true;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (changed) {
|
|
88
|
-
saveSettings(settingsPath, settings);
|
|
89
|
-
console.log(
|
|
90
|
-
`✓ Router defaults set (\`router\` / \`${defaultRouterProfile}\`) — run /reload in pi when ready`,
|
|
91
|
-
);
|
|
92
|
-
} else {
|
|
93
|
-
console.log("[harness-model-router] Defaults unchanged (user set defaultProvider)");
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
main();
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Re-fetch upstream pi-model-router and re-apply ultimate-pi patches.
|
|
3
|
-
set -euo pipefail
|
|
4
|
-
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
-
VEND="$ROOT/vendor/pi-model-router"
|
|
6
|
-
|
|
7
|
-
rm -rf "$VEND"
|
|
8
|
-
git clone --depth 1 https://github.com/yeliu84/pi-model-router.git "$VEND"
|
|
9
|
-
COMMIT="$(git -C "$VEND" rev-parse HEAD)"
|
|
10
|
-
rm -rf "$VEND/.git"
|
|
11
|
-
|
|
12
|
-
for f in "$VEND"/extensions/*.ts; do
|
|
13
|
-
sed -i \
|
|
14
|
-
-e "s|'@earendil-works/pi-agent-core'|'@earendil-works/pi-agent-core'|g" \
|
|
15
|
-
-e "s|'@earendil-works/pi-ai'|'@earendil-works/pi-ai'|g" \
|
|
16
|
-
-e "s|'@earendil-works/pi-coding-agent'|'@earendil-works/pi-coding-agent'|g" \
|
|
17
|
-
-e "s|'@earendil-works/pi-tui'|'@earendil-works/pi-tui'|g" \
|
|
18
|
-
"$f"
|
|
19
|
-
done
|
|
20
|
-
|
|
21
|
-
# Align package.json peers with @earendil-works (upstream lists @earendil-works)
|
|
22
|
-
sed -i \
|
|
23
|
-
-e 's|"@earendil-works/pi-agent-core"|"@earendil-works/pi-agent-core"|g' \
|
|
24
|
-
-e 's|"@earendil-works/pi-ai"|"@earendil-works/pi-ai"|g' \
|
|
25
|
-
-e 's|"@earendil-works/pi-coding-agent"|"@earendil-works/pi-coding-agent"|g' \
|
|
26
|
-
-e 's|"@earendil-works/pi-tui"|"@earendil-works/pi-tui"|g' \
|
|
27
|
-
"$VEND/package.json"
|
|
28
|
-
|
|
29
|
-
python3 -c "
|
|
30
|
-
import re, pathlib
|
|
31
|
-
for p in pathlib.Path('$VEND/extensions').glob('*.ts'):
|
|
32
|
-
t = p.read_text()
|
|
33
|
-
t2 = re.sub(r\"from '\\./([^']+)'\", lambda m: f\"from './{m.group(1)}.js'\" if not m.group(1).endswith('.js') else m.group(0), t)
|
|
34
|
-
p.write_text(t2)
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
cat >"$VEND/UPSTREAM_PIN.md" <<EOF
|
|
38
|
-
# Vendored \`pi-model-router\`
|
|
39
|
-
|
|
40
|
-
- **Repository:** https://github.com/yeliu84/pi-model-router
|
|
41
|
-
- **License:** MIT (\`LICENSE\` in this tree)
|
|
42
|
-
- **Pinned upstream commit:** \`$COMMIT\`
|
|
43
|
-
- **Local changes:** \`extensions/*.ts\` imports use \`@earendil-works/*\` and relative paths end in \`.js\` for TypeScript nodenext.
|
|
44
|
-
EOF
|
|
45
|
-
|
|
46
|
-
rm -f "$VEND/package-lock.json"
|
|
47
|
-
echo "✓ Vendor refreshed at $VEND (commit $COMMIT)"
|