ultimate-pi 0.18.0 → 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 +2 -3
- package/.agents/skills/harness-governor/SKILL.md +6 -5
- package/.agents/skills/harness-orchestration/SKILL.md +4 -4
- package/.agents/skills/harness-review/SKILL.md +7 -7
- package/.agents/skills/harness-sentrux-setup/SKILL.md +4 -3
- package/.agents/skills/harness-steer/SKILL.md +1 -1
- package/.agents/skills/sentrux/SKILL.md +9 -9
- 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 +1 -3
- 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/{adversary.md → reviewing/adversary.md} +0 -2
- package/.pi/agents/harness/{evaluator.md → reviewing/evaluator.md} +0 -2
- package/.pi/agents/harness/{tie-breaker.md → reviewing/tie-breaker.md} +0 -2
- package/.pi/agents/harness/{executor.md → 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-harness-project-control.ts +133 -0
- 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/budget-guard.ts +2 -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 +7 -5
- package/.pi/extensions/harness-ask-user.ts +8 -8
- package/.pi/extensions/harness-debate-tools.ts +27 -43
- package/.pi/extensions/harness-lens.ts +94 -0
- package/.pi/extensions/harness-live-widget.ts +33 -2
- package/.pi/extensions/harness-plan-approval.ts +12 -12
- package/.pi/extensions/harness-run-context.ts +1214 -852
- package/.pi/extensions/harness-subagent-governance.ts +8 -0
- package/.pi/extensions/harness-subagent-submit.ts +36 -164
- package/.pi/extensions/harness-subagents.ts +4 -4
- package/.pi/extensions/harness-telemetry.ts +3 -1
- package/.pi/extensions/harness-web-tools.ts +3 -3
- package/.pi/extensions/observation-bus.ts +2 -0
- package/.pi/extensions/policy-gate.ts +27 -5
- package/.pi/extensions/review-integrity.ts +91 -10
- package/.pi/extensions/sentrux-rules-sync.ts +3 -1
- package/.pi/extensions/subagent-governance.ts +92 -0
- package/.pi/extensions/test-diff-integrity.ts +1 -0
- package/.pi/extensions/trace-recorder.ts +3 -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 +38 -49
- package/.pi/harness/agents.policy.yaml +275 -0
- package/.pi/harness/corpus/graphify-kb-updater.config.json +55 -0
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +2 -1
- 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/0044-harness-steer-loop.md +3 -2
- package/.pi/harness/docs/adrs/0045-harness-lens-minimal-contract.md +49 -0
- package/.pi/harness/docs/adrs/0045-phase-scoped-agent-directories.md +33 -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 +6 -0
- package/.pi/harness/docs/graphify-kb-updater-runbook.md +11 -5
- package/.pi/harness/docs/practice-map.md +2 -2
- 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/harness-spawn-context.schema.json +1 -1
- 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 +21 -0
- package/.pi/lib/harness-agt-tool-guard.ts +5 -0
- package/.pi/{extensions/lib → lib}/harness-artifact-gate.ts +6 -16
- 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-project-config.ts +91 -0
- package/.pi/lib/harness-run-context-responses.ts +9 -0
- package/.pi/lib/harness-run-context.ts +1 -3
- package/.pi/{extensions/lib/spawn-policy.ts → lib/harness-spawn-policy.ts} +4 -3
- package/.pi/{extensions/lib → lib}/harness-spawn-topology.ts +5 -28
- package/.pi/lib/harness-subagent-auth.ts +51 -0
- package/.pi/{extensions/lib → lib}/harness-subagent-precheck.ts +13 -10
- 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 -55
- package/.pi/{extensions/lib → lib}/harness-subagents-bridge.ts +53 -14
- package/.pi/{extensions/lib → lib}/harness-subprocess-bootstrap.ts +1 -1
- package/.pi/lib/harness-ui-state.ts +27 -12
- 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-approval-readiness.ts +3 -52
- 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-auto.md +2 -2
- package/.pi/prompts/harness-plan.md +4 -6
- package/.pi/prompts/harness-review.md +9 -9
- package/.pi/prompts/harness-run.md +7 -7
- package/.pi/prompts/harness-setup.md +42 -68
- package/.pi/prompts/harness-steer.md +2 -2
- package/.pi/scripts/README.md +3 -5
- package/.pi/scripts/generate-agents-policy-yaml.mjs +148 -0
- package/.pi/scripts/graphify-kb-updater.mjs +48 -8
- package/.pi/scripts/harness-agents-manifest.mjs +61 -4
- package/.pi/scripts/harness-agt-doctor.ts +36 -0
- package/.pi/scripts/harness-cli-verify.sh +9 -2
- package/.pi/scripts/harness-project-toggle.mjs +129 -0
- package/.pi/scripts/harness-sentrux-cli.mjs +142 -0
- 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 +26 -0
- package/README.md +85 -58
- 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/agents/harness/planning/scout-graphify.md +0 -39
- package/.pi/agents/harness/planning/scout-semantic.md +0 -41
- package/.pi/agents/harness/planning/scout-structure.md +0 -37
- package/.pi/extensions/lib/ask-user/dialog.ts +0 -260
- package/.pi/extensions/lib/harness-subagent-auth.ts +0 -209
- 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-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
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Generate .pi/harness/agents.policy.yaml from harness agent .md frontmatter + submit registry.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
7
|
+
import { join, dirname } from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
import { parse as parseYaml } from "yaml";
|
|
10
|
+
import { walkAgentsDir } from "../lib/harness-agent-discovery.mjs";
|
|
11
|
+
|
|
12
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
13
|
+
const AGENTS_DIR = join(ROOT, ".pi", "agents");
|
|
14
|
+
const OUT = join(ROOT, ".pi", "harness", "agents.policy.yaml");
|
|
15
|
+
|
|
16
|
+
const SUBMIT_BY_AGENT = {
|
|
17
|
+
"harness/planning/planning-context": ["submit_planning_context"],
|
|
18
|
+
"harness/planning/decompose": ["submit_decomposition_brief", "submit_human_required"],
|
|
19
|
+
"harness/planning/hypothesis": ["submit_hypothesis_brief"],
|
|
20
|
+
"harness/planning/hypothesis-validator": ["submit_hypothesis_validation"],
|
|
21
|
+
"harness/planning/plan-evaluator": ["submit_validation_turn"],
|
|
22
|
+
"harness/planning/plan-adversary": ["submit_adversary_brief"],
|
|
23
|
+
"harness/planning/sprint-contract-auditor": ["submit_sprint_audit"],
|
|
24
|
+
"harness/planning/review-integrator": ["submit_review_round_draft"],
|
|
25
|
+
"harness/planning/implementation-researcher": ["submit_implementation_research"],
|
|
26
|
+
"harness/planning/stack-researcher": ["submit_stack_brief"],
|
|
27
|
+
"harness/planning/execution-plan-author": ["submit_execution_plan_brief"],
|
|
28
|
+
"harness/running/executor": ["submit_executor_handoff"],
|
|
29
|
+
"harness/reviewing/evaluator": ["submit_eval_verdict"],
|
|
30
|
+
"harness/reviewing/adversary": ["submit_adversary_report"],
|
|
31
|
+
"harness/reviewing/tie-breaker": ["submit_human_required"],
|
|
32
|
+
"harness/trace-librarian": ["submit_human_required"],
|
|
33
|
+
"harness/incident-recorder": ["submit_human_required"],
|
|
34
|
+
"harness/sentrux-steward": ["submit_sentrux_manifest_proposal"],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
function parseFrontmatter(content) {
|
|
38
|
+
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
39
|
+
if (!m) return {};
|
|
40
|
+
return parseYaml(m[1]) ?? {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function kindFor(id) {
|
|
44
|
+
if (id.startsWith("harness/planning/")) return "planner";
|
|
45
|
+
if (id === "harness/running/executor") return "executor";
|
|
46
|
+
if (id === "harness/reviewing/evaluator") return "evaluator";
|
|
47
|
+
if (id === "harness/reviewing/adversary") return "adversary";
|
|
48
|
+
if (id === "harness/reviewing/tie-breaker") return "tie_breaker";
|
|
49
|
+
if (id === "harness/trace-librarian") return "trace";
|
|
50
|
+
if (id === "harness/incident-recorder") return "incident";
|
|
51
|
+
if (id === "harness/sentrux-steward" || id === "harness/sentrux-bootstrap")
|
|
52
|
+
return "planner";
|
|
53
|
+
return "other";
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const KIND_BASE = {
|
|
57
|
+
planner: ["read", "grep", "find", "ls"],
|
|
58
|
+
executor: ["read", "write", "edit", "bash", "grep", "find", "ls"],
|
|
59
|
+
evaluator: ["read", "grep", "find", "ls"],
|
|
60
|
+
adversary: ["read", "grep", "find", "ls"],
|
|
61
|
+
tie_breaker: ["read", "grep", "find", "ls"],
|
|
62
|
+
trace: ["read", "grep", "find", "ls"],
|
|
63
|
+
incident: ["read", "grep", "find", "ls"],
|
|
64
|
+
other: ["read", "grep", "find", "ls"],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
function csvTools(fm) {
|
|
68
|
+
const raw = fm.tools;
|
|
69
|
+
if (!raw) return [];
|
|
70
|
+
return String(raw)
|
|
71
|
+
.split(",")
|
|
72
|
+
.map((t) => t.trim())
|
|
73
|
+
.filter(Boolean);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function main() {
|
|
77
|
+
const files = new Map();
|
|
78
|
+
walkAgentsDir(AGENTS_DIR, "package", files);
|
|
79
|
+
|
|
80
|
+
const kinds = {
|
|
81
|
+
planner: { tools: KIND_BASE.planner, extensions: false, read_only: true },
|
|
82
|
+
executor: { tools: KIND_BASE.executor, extensions: true, read_only: false },
|
|
83
|
+
evaluator: { tools: KIND_BASE.evaluator, extensions: false, read_only: true },
|
|
84
|
+
adversary: { tools: KIND_BASE.adversary, extensions: false, read_only: true },
|
|
85
|
+
tie_breaker: {
|
|
86
|
+
tools: KIND_BASE.tie_breaker,
|
|
87
|
+
extensions: false,
|
|
88
|
+
read_only: true,
|
|
89
|
+
},
|
|
90
|
+
trace: { tools: KIND_BASE.trace, extensions: false, read_only: true },
|
|
91
|
+
incident: { tools: KIND_BASE.incident, extensions: false, read_only: true },
|
|
92
|
+
other: { tools: KIND_BASE.other, extensions: false, read_only: true },
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const agents = {};
|
|
96
|
+
|
|
97
|
+
for (const [id, file] of files) {
|
|
98
|
+
if (!id.startsWith("harness/")) continue;
|
|
99
|
+
const fm = parseFrontmatter(file.content);
|
|
100
|
+
const kind = kindFor(id);
|
|
101
|
+
const base = new Set(KIND_BASE[kind] ?? KIND_BASE.other);
|
|
102
|
+
const fromFm = csvTools(fm);
|
|
103
|
+
const submit = SUBMIT_BY_AGENT[id] ?? [];
|
|
104
|
+
const toolsAdd = [...new Set([...fromFm, ...submit])].filter(
|
|
105
|
+
(t) => !base.has(t),
|
|
106
|
+
);
|
|
107
|
+
const entry = { kind };
|
|
108
|
+
if (toolsAdd.length > 0) entry.tools_add = toolsAdd;
|
|
109
|
+
if (fm.extensions === false) entry.extensions = false;
|
|
110
|
+
if (fm.extensions === true) entry.extensions = true;
|
|
111
|
+
if (typeof fm.max_turns === "number") entry.max_turns = fm.max_turns;
|
|
112
|
+
if (typeof fm.thinking === "string") entry.thinking = fm.thinking;
|
|
113
|
+
if (submit.length === 1) entry.submit_tool = submit[0];
|
|
114
|
+
agents[id] = entry;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// plan-synthesizer: parent-only, minimal policy for spawn if ever used
|
|
118
|
+
agents["harness/planning/plan-synthesizer"] = {
|
|
119
|
+
kind: "planner",
|
|
120
|
+
tools_add: [
|
|
121
|
+
"submit_decomposition_brief",
|
|
122
|
+
"submit_hypothesis_brief",
|
|
123
|
+
"submit_execution_plan_brief",
|
|
124
|
+
],
|
|
125
|
+
extensions: false,
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const doc = {
|
|
129
|
+
apiVersion: "harness.toolkit/v1",
|
|
130
|
+
kinds,
|
|
131
|
+
agents,
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const yaml = [
|
|
135
|
+
"# Generated/maintained SSOT for harness agent tools (see ADR 0049).",
|
|
136
|
+
"# Regenerate hints: node .pi/scripts/generate-agents-policy-yaml.mjs",
|
|
137
|
+
"",
|
|
138
|
+
];
|
|
139
|
+
const { stringify } = await import("yaml");
|
|
140
|
+
yaml.push(stringify(doc));
|
|
141
|
+
await writeFile(OUT, yaml.join("\n"), "utf8");
|
|
142
|
+
console.log(`Wrote ${OUT} (${Object.keys(agents).length} harness agents)`);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
main().catch((err) => {
|
|
146
|
+
console.error(err);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
});
|
|
@@ -20,6 +20,7 @@ const DEFAULT_RAW_DIR = "raw/graphify-kb-updates";
|
|
|
20
20
|
const DEFAULT_DATA_DIR = "data";
|
|
21
21
|
const DEFAULT_GRAPH_DIR = "graphify-out";
|
|
22
22
|
const REQUIRED_RIGHTS = ["license", "access", "approved_by", "approved_at"];
|
|
23
|
+
const REQUIRED_PROVENANCE = ["origin", "locator"];
|
|
23
24
|
const RISKY_KINDS = new Set(["book", "transcript", "youtube"]);
|
|
24
25
|
|
|
25
26
|
function parseArgs(argv) {
|
|
@@ -92,6 +93,8 @@ function loadConfig(args) {
|
|
|
92
93
|
allowlist: (cfg.allowlist ?? []).map((entry) => typeof entry === "string" ? { domain: entry, approved: true } : entry),
|
|
93
94
|
reviewQueue: cfg.review_queue ?? [],
|
|
94
95
|
articleQueries: cfg.article_queries ?? [],
|
|
96
|
+
repoSources: cfg.repo_sources ?? [],
|
|
97
|
+
releaseFeeds: cfg.release_feeds ?? [],
|
|
95
98
|
paperFeeds: cfg.paper_feeds ?? [],
|
|
96
99
|
localBooks: cfg.local_books ?? [{ path: "data/books" }],
|
|
97
100
|
localTranscripts: cfg.local_transcripts ?? [{ path: "data/youtube-transcripts" }],
|
|
@@ -130,6 +133,11 @@ function hasRightsApproval(candidate) {
|
|
|
130
133
|
return Boolean(r && REQUIRED_RIGHTS.every((k) => typeof r[k] === "string" && r[k].trim()));
|
|
131
134
|
}
|
|
132
135
|
|
|
136
|
+
function hasCompleteProvenance(candidate) {
|
|
137
|
+
const p = candidate.provenance;
|
|
138
|
+
return Boolean(p && REQUIRED_PROVENANCE.every((k) => typeof p[k] === "string" && p[k].trim()));
|
|
139
|
+
}
|
|
140
|
+
|
|
133
141
|
function urlDomain(url) {
|
|
134
142
|
try { return new URL(url).hostname.replace(/^www\./, ""); } catch { return ""; }
|
|
135
143
|
}
|
|
@@ -138,6 +146,14 @@ function allowlistEntry(cfg, domain) {
|
|
|
138
146
|
return cfg.allowlist.find((entry) => entry.domain === domain || domain.endsWith(`.${entry.domain}`));
|
|
139
147
|
}
|
|
140
148
|
|
|
149
|
+
function allowlistEntryAllows(entry, kind) {
|
|
150
|
+
return Boolean(entry?.approved === true && (!Array.isArray(entry.allowed_source_classes) || entry.allowed_source_classes.includes(kind)));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function allowlistAllows(candidate) {
|
|
154
|
+
return Boolean(candidate.allowlist_state.allowed && candidate.allowlist_state.approved && (!Array.isArray(candidate.allowlist_state.allowed_source_classes) || candidate.allowlist_state.allowed_source_classes.includes(candidate.kind)));
|
|
155
|
+
}
|
|
156
|
+
|
|
141
157
|
function competitorLabels(cfg, candidate) {
|
|
142
158
|
const haystack = `${candidate.title ?? ""} ${candidate.url ?? ""} ${candidate.path ?? ""}`.toLowerCase();
|
|
143
159
|
const labels = [];
|
|
@@ -163,7 +179,7 @@ function normalizeCandidate(cfg, raw) {
|
|
|
163
179
|
risk_class: raw.risk_class ?? taxonomy.risk_class ?? (RISKY_KINDS.has(raw.kind) ? "high" : "medium"),
|
|
164
180
|
provenance: raw.provenance ?? { origin: raw.source_type, discovered_by: "graphify-kb-updater", locator: raw.url ?? raw.path ?? raw.query ?? null },
|
|
165
181
|
rights_access: raw.rights_access ?? null,
|
|
166
|
-
allowlist_state: allow ? { allowed: true, domain: allow.domain, approved: allow.approved === true, approved_by: allow.approved_by ?? null, approved_at: allow.approved_at ?? null } : { allowed: false },
|
|
182
|
+
allowlist_state: allow ? { allowed: true, domain: allow.domain, approved: allow.approved === true, approved_by: allow.approved_by ?? null, approved_at: allow.approved_at ?? null, allowed_source_classes: allow.allowed_source_classes ?? null } : { allowed: false },
|
|
167
183
|
approval_state: raw.approved === true ? "approved" : "not_approved",
|
|
168
184
|
};
|
|
169
185
|
candidate.competitor_labels = raw.competitor_labels ?? competitorLabels(cfg, candidate);
|
|
@@ -175,12 +191,27 @@ function normalizeCandidate(cfg, raw) {
|
|
|
175
191
|
function discoverCandidates(cfg, args) {
|
|
176
192
|
const candidates = [];
|
|
177
193
|
for (const query of cfg.articleQueries) candidates.push({ kind: "article", source_type: "web_search_query", title: query, query, review_required: true, promotion_policy: "stage_only" });
|
|
194
|
+
for (const repo of cfg.repoSources) {
|
|
195
|
+
const kind = "repo";
|
|
196
|
+
const domain = urlDomain(repo.url);
|
|
197
|
+
const allow = allowlistEntry(cfg, domain);
|
|
198
|
+
const explicit = cfg.autoPromoteAllowlist && allowlistEntryAllows(allow, kind) && repo.approved === true;
|
|
199
|
+
candidates.push({ ...repo, kind, source_type: "repo_metadata", domain, review_required: !explicit, promotion_policy: explicit ? "allowlist_auto_promote" : "manual_review", rights_access: repo.rights_access ?? null, provenance: repo.provenance });
|
|
200
|
+
}
|
|
201
|
+
for (const release of cfg.releaseFeeds) {
|
|
202
|
+
const kind = "release";
|
|
203
|
+
const domain = urlDomain(release.url);
|
|
204
|
+
const allow = allowlistEntry(cfg, domain);
|
|
205
|
+
const explicit = cfg.autoPromoteAllowlist && allowlistEntryAllows(allow, kind) && release.approved === true;
|
|
206
|
+
candidates.push({ ...release, kind, source_type: "release_metadata", domain, review_required: !explicit, promotion_policy: explicit ? "allowlist_auto_promote" : "manual_review", rights_access: release.rights_access ?? null, provenance: release.provenance });
|
|
207
|
+
}
|
|
178
208
|
for (const feed of cfg.paperFeeds) candidates.push({ kind: "paper", source_type: "feed", title: feed.title ?? feed.url, url: feed.url, rights_access: feed.rights_access ?? null, review_required: true, promotion_policy: "stage_only", provenance: feed.provenance });
|
|
179
209
|
for (const entry of cfg.reviewQueue) {
|
|
210
|
+
const kind = entry.kind ?? "article";
|
|
180
211
|
const domain = urlDomain(entry.url);
|
|
181
212
|
const allow = allowlistEntry(cfg, domain);
|
|
182
|
-
const explicit = cfg.autoPromoteAllowlist && allow
|
|
183
|
-
candidates.push({ ...entry, kind
|
|
213
|
+
const explicit = cfg.autoPromoteAllowlist && allowlistEntryAllows(allow, kind) && entry.approved === true;
|
|
214
|
+
candidates.push({ ...entry, kind, source_type: "review_queue", domain, review_required: !explicit, promotion_policy: explicit ? "allowlist_auto_promote" : "manual_review", rights_access: entry.rights_access ?? null });
|
|
184
215
|
}
|
|
185
216
|
for (const spec of cfg.localBooks) for (const file of walkFiles(resolve(ROOT, spec.path), [".md", ".txt", ".pdf"], spec.max_files ?? 50)) candidates.push({ kind: "book", source_type: "local_file", title: basename(file), path: rel(file), rights_access: rightsFromSidecar(file), review_required: true, promotion_policy: "manual_review" });
|
|
186
217
|
for (const spec of cfg.localTranscripts) for (const file of walkFiles(resolve(ROOT, spec.path), [".md", ".txt", ".vtt"], spec.max_files ?? 80)) candidates.push({ kind: "transcript", source_type: "local_file", title: basename(file), path: rel(file), rights_access: rightsFromSidecar(file), review_required: true, promotion_policy: "manual_review" });
|
|
@@ -208,10 +239,11 @@ function sourceBody(candidate) {
|
|
|
208
239
|
}
|
|
209
240
|
|
|
210
241
|
function promotionAllowed(candidate) {
|
|
242
|
+
if (!hasCompleteProvenance(candidate)) return { ok: false, reason: "missing_complete_provenance" };
|
|
211
243
|
if (!hasRightsApproval(candidate)) return { ok: false, reason: "missing_rights_access_approval" };
|
|
212
244
|
if (RISKY_KINDS.has(candidate.kind) && candidate.approved !== true) return { ok: false, reason: "manual_approval_required" };
|
|
213
|
-
if (candidate.
|
|
214
|
-
return candidate.approved === true ? { ok: true } : { ok: false, reason: "manual_approval_required" };
|
|
245
|
+
if (candidate.promotion_policy === "allowlist_auto_promote" && candidate.approved === true && allowlistAllows(candidate)) return { ok: true, reason: "allowlist_auto_promote" };
|
|
246
|
+
return candidate.approved === true ? { ok: true, reason: "manual_approved" } : { ok: false, reason: "manual_approval_required" };
|
|
215
247
|
}
|
|
216
248
|
|
|
217
249
|
function promote(candidate, args) {
|
|
@@ -267,15 +299,19 @@ function pilotMetrics(summary) {
|
|
|
267
299
|
function schedulerSmoke() {
|
|
268
300
|
const service = readFileSync(resolve(ROOT, ".pi/harness/corpus/systemd/graphify-kb-updater.service"), "utf8");
|
|
269
301
|
const timer = readFileSync(resolve(ROOT, ".pi/harness/corpus/systemd/graphify-kb-updater.timer"), "utf8");
|
|
302
|
+
const envTemplate = readFileSync(resolve(ROOT, ".pi/harness/corpus/systemd/graphify-kb-updater.env.template"), "utf8");
|
|
270
303
|
const cron = readFileSync(resolve(ROOT, ".pi/harness/corpus/cron.example"), "utf8");
|
|
304
|
+
const graphifyArgs = envTemplate.match(/^GRAPHIFY_KB_ARGS=(.+)$/m)?.[1] ?? "";
|
|
271
305
|
const checks = {
|
|
272
306
|
systemd_daily: /OnCalendar=\*-\*-\*\s+08:30:00|OnCalendar=daily/i.test(timer),
|
|
273
307
|
cron_daily: /^30\s+8\s+\*\s+\*\s+\*/m.test(cron),
|
|
274
308
|
bounded_timeout: /timeout 45m/.test(service) && /timeout 45m/.test(cron),
|
|
275
309
|
locked_no_overlap: /flock -n/.test(service) && /flock -n/.test(cron),
|
|
276
310
|
explicit_env: /EnvironmentFile/.test(service) && /UP_ROOT/.test(cron),
|
|
311
|
+
working_directory: /WorkingDirectory=\$\{UP_ROOT\}/.test(service),
|
|
277
312
|
logged: /StandardOutput=append/.test(service) && /HARNESS_GRAPHIFY_KB_LOG/.test(cron),
|
|
278
|
-
refresh_intent: /--refresh-graph/.test(cron),
|
|
313
|
+
refresh_intent: /--refresh-graph/.test(cron) && /--refresh-graph/.test(graphifyArgs),
|
|
314
|
+
graphify_kb_args_template: /--apply/.test(graphifyArgs) && /--pilot-report/.test(graphifyArgs) && !/[;&|`$<>]/.test(graphifyArgs),
|
|
279
315
|
};
|
|
280
316
|
const ok = Object.values(checks).every(Boolean);
|
|
281
317
|
console.log(JSON.stringify({ ok, checks }, null, 2));
|
|
@@ -305,8 +341,8 @@ function main() {
|
|
|
305
341
|
}
|
|
306
342
|
if (contentChanged) changedExisting++;
|
|
307
343
|
const gate = promotionAllowed(c);
|
|
308
|
-
registry.candidates[c.id] = { ...(prior ?? {}), ...c, first_seen_at: prior?.first_seen_at ?? runAt, last_seen_at: runAt, status: gate.ok ? "promotable" : "review_required",
|
|
309
|
-
if (!gate.ok) { blocked.push({ id: c.id, title: c.title, reason: gate.reason, allowlist_state: c.allowlist_state, category: c.category, competitor_labels: c.competitor_labels }); continue; }
|
|
344
|
+
registry.candidates[c.id] = { ...(prior ?? {}), ...c, first_seen_at: prior?.first_seen_at ?? runAt, last_seen_at: runAt, status: gate.ok ? "promotable" : "review_required", decision: gate.ok ? gate.reason : "stage_for_review", block_reason: gate.ok ? null : gate.reason, next_action: gate.ok ? "promote_on_apply" : "manual_review_required", content_state: contentChanged ? "changed" : "new" };
|
|
345
|
+
if (!gate.ok) { blocked.push({ id: c.id, title: c.title, kind: c.kind, source_type: c.source_type, reason: gate.reason, next_action: "manual_review_required", allowlist_state: c.allowlist_state, category: c.category, competitor_labels: c.competitor_labels }); continue; }
|
|
310
346
|
planned.push(c);
|
|
311
347
|
}
|
|
312
348
|
|
|
@@ -326,6 +362,7 @@ function main() {
|
|
|
326
362
|
|
|
327
363
|
const graph = refreshGraph(args, promoted);
|
|
328
364
|
const stale = registry.runs.at?.(-1)?.run_id ? [] : ["no_prior_apply_run_recorded"];
|
|
365
|
+
const reviewQueue = blocked.map((b) => ({ id: b.id, title: b.title, kind: b.kind, reason: b.reason, next_action: b.next_action })).slice(0, 50);
|
|
329
366
|
const summary = {
|
|
330
367
|
run_id: `kb-${Date.now()}`,
|
|
331
368
|
last_run_at: runAt,
|
|
@@ -335,6 +372,7 @@ function main() {
|
|
|
335
372
|
promoted_count: promoted,
|
|
336
373
|
duplicate_skips: duplicates,
|
|
337
374
|
blocked_count: blocked.length,
|
|
375
|
+
staged_count: blocked.length,
|
|
338
376
|
skipped_count: skipped.length,
|
|
339
377
|
failure_count: failed,
|
|
340
378
|
changed_existing_count: changedExisting,
|
|
@@ -344,6 +382,8 @@ function main() {
|
|
|
344
382
|
graph,
|
|
345
383
|
exit_status: failed || graph.ok === false ? 1 : 0,
|
|
346
384
|
promoted: promotedRefs,
|
|
385
|
+
review_queue_count: reviewQueue.length,
|
|
386
|
+
review_queue: reviewQueue,
|
|
347
387
|
blocked: blocked.slice(0, 50),
|
|
348
388
|
skipped: skipped.slice(0, 50),
|
|
349
389
|
config: rel(cfg.path),
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* node .pi/scripts/harness-agents-manifest.mjs --check
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { existsSync } from "node:fs";
|
|
10
11
|
import { readFile, writeFile } from "node:fs/promises";
|
|
11
12
|
import { join, dirname } from "node:path";
|
|
12
13
|
import { fileURLToPath } from "node:url";
|
|
@@ -15,6 +16,10 @@ import {
|
|
|
15
16
|
sha256Content,
|
|
16
17
|
walkAgentsDir,
|
|
17
18
|
} from "../lib/harness-agent-discovery.mjs";
|
|
19
|
+
import {
|
|
20
|
+
loadAgentsPolicyMerged,
|
|
21
|
+
packageAgentsPolicyPath,
|
|
22
|
+
} from "../lib/agents-policy.mjs";
|
|
18
23
|
|
|
19
24
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
20
25
|
const MANIFEST_PATH = join(ROOT, ".pi", "harness", "agents.manifest.json");
|
|
@@ -27,7 +32,7 @@ async function readPackageMeta() {
|
|
|
27
32
|
return { name: pkg.name ?? "ultimate-pi", version: pkg.version ?? "0.0.0" };
|
|
28
33
|
}
|
|
29
34
|
|
|
30
|
-
function buildManifest(packageFiles, packageName, packageVersion) {
|
|
35
|
+
async function buildManifest(packageFiles, packageName, packageVersion) {
|
|
31
36
|
const agents = {};
|
|
32
37
|
for (const f of packageFiles.values()) {
|
|
33
38
|
agents[f.id] = {
|
|
@@ -35,11 +40,17 @@ function buildManifest(packageFiles, packageName, packageVersion) {
|
|
|
35
40
|
sha256: sha256Content(f.content),
|
|
36
41
|
};
|
|
37
42
|
}
|
|
43
|
+
const policyPath = packageAgentsPolicyPath(ROOT);
|
|
44
|
+
let policy_sha256;
|
|
45
|
+
if (existsSync(policyPath)) {
|
|
46
|
+
policy_sha256 = sha256Content(await readFile(policyPath, "utf-8"));
|
|
47
|
+
}
|
|
38
48
|
return {
|
|
39
49
|
schema_version: "1.0.0",
|
|
40
50
|
package: packageName,
|
|
41
51
|
package_version: packageVersion,
|
|
42
52
|
generated_at: new Date().toISOString(),
|
|
53
|
+
...(policy_sha256 ? { policy_sha256 } : {}),
|
|
43
54
|
agents,
|
|
44
55
|
};
|
|
45
56
|
}
|
|
@@ -68,6 +79,37 @@ function getDriftReport(manifest, packageFiles) {
|
|
|
68
79
|
return { ok: items.length === 0, items };
|
|
69
80
|
}
|
|
70
81
|
|
|
82
|
+
function frontmatterHasToolLists(content) {
|
|
83
|
+
const m = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
84
|
+
if (!m) return false;
|
|
85
|
+
return /^tools:/m.test(m[1]) || /^disallowed_tools:/m.test(m[1]);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function verifyAgentsPolicy(packageFiles) {
|
|
89
|
+
const items = [];
|
|
90
|
+
const policyPath = packageAgentsPolicyPath(ROOT);
|
|
91
|
+
if (!existsSync(policyPath)) {
|
|
92
|
+
return { ok: false, items: [{ id: "*", kind: "missing_agents_policy_yaml" }] };
|
|
93
|
+
}
|
|
94
|
+
const merged = loadAgentsPolicyMerged(ROOT, ROOT);
|
|
95
|
+
for (const [id, file] of packageFiles) {
|
|
96
|
+
if (!id.startsWith("harness/")) continue;
|
|
97
|
+
if (frontmatterHasToolLists(file.content)) {
|
|
98
|
+
items.push({ id, kind: "frontmatter_tools" });
|
|
99
|
+
}
|
|
100
|
+
if (!merged.agents.has(id)) {
|
|
101
|
+
items.push({ id, kind: "missing_policy_entry" });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
for (const id of merged.agents.keys()) {
|
|
105
|
+
if (!id.startsWith("harness/")) continue;
|
|
106
|
+
if (!packageFiles.has(id)) {
|
|
107
|
+
items.push({ id, kind: "orphan_policy_entry" });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { ok: items.length === 0, items };
|
|
111
|
+
}
|
|
112
|
+
|
|
71
113
|
async function loadPackageFiles() {
|
|
72
114
|
const files = new Map();
|
|
73
115
|
walkAgentsDir(PACKAGE_AGENTS, "package", files);
|
|
@@ -81,10 +123,10 @@ async function main() {
|
|
|
81
123
|
const mode = process.argv.includes("--check") ? "check" : "write";
|
|
82
124
|
const { name, version } = await readPackageMeta();
|
|
83
125
|
const packageFiles = await loadPackageFiles();
|
|
84
|
-
const built = buildManifest(packageFiles, name, version);
|
|
126
|
+
const built = await buildManifest(packageFiles, name, version);
|
|
85
127
|
|
|
86
128
|
if (mode === "write") {
|
|
87
|
-
await writeFile(MANIFEST_PATH, `${JSON.stringify(built, null,
|
|
129
|
+
await writeFile(MANIFEST_PATH, `${JSON.stringify(built, null, "\t")}\n`, "utf-8");
|
|
88
130
|
console.log(
|
|
89
131
|
`Wrote ${MANIFEST_PATH} (${Object.keys(built.agents).length} agents)`,
|
|
90
132
|
);
|
|
@@ -107,6 +149,19 @@ async function main() {
|
|
|
107
149
|
process.exit(1);
|
|
108
150
|
}
|
|
109
151
|
|
|
152
|
+
const policyCheck = verifyAgentsPolicy(packageFiles);
|
|
153
|
+
if (!policyCheck.ok) {
|
|
154
|
+
for (const item of policyCheck.items) {
|
|
155
|
+
console.error(`policy: ${item.id} (${item.kind})`);
|
|
156
|
+
}
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (built.policy_sha256 && onDisk.policy_sha256 !== built.policy_sha256) {
|
|
161
|
+
console.error("policy_sha256 mismatch — regenerate manifest with --write");
|
|
162
|
+
process.exit(1);
|
|
163
|
+
}
|
|
164
|
+
|
|
110
165
|
if (onDisk.package_version !== version) {
|
|
111
166
|
console.error(
|
|
112
167
|
`package_version mismatch: manifest=${onDisk.package_version} package=${version}`,
|
|
@@ -114,7 +169,9 @@ async function main() {
|
|
|
114
169
|
process.exit(1);
|
|
115
170
|
}
|
|
116
171
|
|
|
117
|
-
console.log(
|
|
172
|
+
console.log(
|
|
173
|
+
`agents.manifest.json OK (${Object.keys(built.agents).length} agents, policy aligned)`,
|
|
174
|
+
);
|
|
118
175
|
}
|
|
119
176
|
|
|
120
177
|
main().catch((err) => {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env npx tsx
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import { doctorHarnessPolicies } from "../lib/agt/policy-engine.js";
|
|
6
|
+
|
|
7
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
8
|
+
const doc = doctorHarnessPolicies(ROOT);
|
|
9
|
+
if (!doc.ok) {
|
|
10
|
+
console.error("AGT policy doctor failed:");
|
|
11
|
+
for (const e of doc.errors) console.error(` - ${e}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(ROOT, "package.json"), "utf-8")) as {
|
|
15
|
+
files?: string[];
|
|
16
|
+
};
|
|
17
|
+
const files = pkg.files ?? [];
|
|
18
|
+
if (
|
|
19
|
+
!files.some(
|
|
20
|
+
(f: string) =>
|
|
21
|
+
f === ".pi/harness/policies" || f.startsWith(".pi/harness/policies/"),
|
|
22
|
+
)
|
|
23
|
+
) {
|
|
24
|
+
console.error("package.json files[] missing .pi/harness/policies");
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
if (
|
|
28
|
+
!files.includes(".pi/lib") &&
|
|
29
|
+
!files.some((f) => f.startsWith(".pi/lib/"))
|
|
30
|
+
) {
|
|
31
|
+
console.error("package.json files[] missing .pi/lib (ships .pi/lib/agt)");
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
console.log(
|
|
35
|
+
`AGT doctor OK (${doc.loaded.length} policies at ${doc.policyDir})`,
|
|
36
|
+
);
|
|
@@ -262,11 +262,18 @@ verify_cocoindex() {
|
|
|
262
262
|
|
|
263
263
|
verify_biome() {
|
|
264
264
|
log "[biome]"
|
|
265
|
-
|
|
265
|
+
if [ ! -f "${ROOT}/package.json" ] && [ ! -f "${ROOT}/biome.json" ]; then
|
|
266
|
+
warn "biome skipped (non-JS/TS repo detected; optional tool)"
|
|
267
|
+
return
|
|
268
|
+
fi
|
|
269
|
+
npm_global_install "@biomejs/biome" "biome" || {
|
|
270
|
+
warn "biome npm install failed (optional — use your stack's formatter/linter)"
|
|
271
|
+
return
|
|
272
|
+
}
|
|
266
273
|
if biome --version &>/dev/null; then
|
|
267
274
|
pass "biome $(biome --version 2>/dev/null | head -1)"
|
|
268
275
|
else
|
|
269
|
-
|
|
276
|
+
warn "biome --version failed (optional — use your stack's formatter/linter)"
|
|
270
277
|
fi
|
|
271
278
|
}
|
|
272
279
|
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Toggle per-project harness governance — writes `.pi/harness/project.json`.
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* node harness-project-toggle.mjs status [--project-root DIR]
|
|
7
|
+
* node harness-project-toggle.mjs enable [--project-root DIR]
|
|
8
|
+
* node harness-project-toggle.mjs disable [--project-root DIR]
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
12
|
+
import { dirname, join } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const CONFIG_BASENAME = "project.json";
|
|
16
|
+
|
|
17
|
+
function parseArgs(argv) {
|
|
18
|
+
const args = [...argv];
|
|
19
|
+
let projectRoot = process.cwd();
|
|
20
|
+
const positional = [];
|
|
21
|
+
for (let i = 0; i < args.length; i++) {
|
|
22
|
+
const arg = args[i];
|
|
23
|
+
if (arg === "--project-root" && args[i + 1]) {
|
|
24
|
+
projectRoot = args[++i];
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
positional.push(arg);
|
|
28
|
+
}
|
|
29
|
+
return { projectRoot, action: positional[0] ?? "status" };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function configPath(projectRoot) {
|
|
33
|
+
return join(projectRoot, ".pi", "harness", CONFIG_BASENAME);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function envOverrideEnabled() {
|
|
37
|
+
const raw = process.env.HARNESS_ENABLED?.trim().toLowerCase();
|
|
38
|
+
if (!raw) return null;
|
|
39
|
+
if (raw === "0" || raw === "false" || raw === "no") return false;
|
|
40
|
+
if (raw === "1" || raw === "true" || raw === "yes") return true;
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function readConfig(projectRoot) {
|
|
45
|
+
const fromEnv = envOverrideEnabled();
|
|
46
|
+
if (fromEnv !== null) {
|
|
47
|
+
return {
|
|
48
|
+
schema_version: "1.0.0",
|
|
49
|
+
enabled: fromEnv,
|
|
50
|
+
source: "env:HARNESS_ENABLED",
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const path = configPath(projectRoot);
|
|
55
|
+
if (!existsSync(path)) {
|
|
56
|
+
return {
|
|
57
|
+
schema_version: "1.0.0",
|
|
58
|
+
enabled: true,
|
|
59
|
+
source: "default",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
65
|
+
if (typeof raw.enabled === "boolean") {
|
|
66
|
+
return {
|
|
67
|
+
schema_version: "1.0.0",
|
|
68
|
+
enabled: raw.enabled,
|
|
69
|
+
updated_at: raw.updated_at,
|
|
70
|
+
source: path,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
} catch {
|
|
74
|
+
// fall through
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
schema_version: "1.0.0",
|
|
79
|
+
enabled: true,
|
|
80
|
+
source: "default-corrupt-file",
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function writeConfig(projectRoot, enabled) {
|
|
85
|
+
const path = configPath(projectRoot);
|
|
86
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
87
|
+
const payload = {
|
|
88
|
+
schema_version: "1.0.0",
|
|
89
|
+
enabled,
|
|
90
|
+
updated_at: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
writeFileSync(path, `${JSON.stringify(payload, null, "\t")}\n`, "utf8");
|
|
93
|
+
return { ...payload, path };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function main() {
|
|
97
|
+
const { projectRoot, action } = parseArgs(process.argv.slice(2));
|
|
98
|
+
if (!["status", "enable", "disable"].includes(action)) {
|
|
99
|
+
console.error(
|
|
100
|
+
"Usage: harness-project-toggle.mjs <status|enable|disable> [--project-root DIR]",
|
|
101
|
+
);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (action === "status") {
|
|
106
|
+
const config = readConfig(projectRoot);
|
|
107
|
+
console.log(JSON.stringify({ ok: true, projectRoot, ...config }, null, 2));
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const enabled = action === "enable";
|
|
112
|
+
const written = writeConfig(projectRoot, enabled);
|
|
113
|
+
console.log(
|
|
114
|
+
JSON.stringify(
|
|
115
|
+
{
|
|
116
|
+
ok: true,
|
|
117
|
+
projectRoot,
|
|
118
|
+
enabled: written.enabled,
|
|
119
|
+
path: written.path,
|
|
120
|
+
updated_at: written.updated_at,
|
|
121
|
+
reload_required: true,
|
|
122
|
+
},
|
|
123
|
+
null,
|
|
124
|
+
2,
|
|
125
|
+
),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main();
|