ultimate-pi 0.10.1 → 0.12.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 +44 -0
- package/.agents/skills/harness-decisions/SKILL.md +3 -3
- package/.agents/skills/harness-orchestration/SKILL.md +59 -25
- package/.agents/skills/harness-plan/SKILL.md +16 -15
- package/.pi/agents/harness/adversary.md +0 -1
- package/.pi/agents/harness/evaluator.md +0 -1
- package/.pi/agents/harness/executor.md +1 -2
- package/.pi/agents/harness/incident-recorder.md +0 -1
- package/.pi/agents/harness/meta-optimizer.md +0 -1
- package/.pi/agents/harness/planning/decompose.md +83 -0
- package/.pi/agents/harness/planning/execution-plan-author.md +30 -0
- package/.pi/agents/harness/planning/hypothesis-validator.md +23 -0
- package/.pi/agents/harness/planning/hypothesis.md +89 -0
- package/.pi/agents/harness/planning/plan-adversary.md +18 -0
- package/.pi/agents/harness/planning/plan-evaluator.md +18 -0
- package/.pi/agents/harness/planning/review-integrator.md +23 -0
- package/.pi/agents/harness/planning/scout-graphify.md +54 -0
- package/.pi/agents/harness/planning/scout-semantic.md +47 -0
- package/.pi/agents/harness/planning/scout-structure.md +50 -0
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +18 -0
- package/.pi/agents/harness/planning/stack-researcher.md +24 -0
- package/.pi/agents/harness/tie-breaker.md +0 -1
- package/.pi/agents/harness/trace-librarian.md +0 -1
- package/.pi/extensions/debate-orchestrator.ts +90 -53
- package/.pi/extensions/harness-ask-user.ts +5 -0
- package/.pi/extensions/harness-plan-approval.ts +137 -3
- package/.pi/extensions/harness-run-context.ts +146 -6
- package/.pi/extensions/harness-subagents.ts +10 -5
- package/.pi/extensions/harness-web-tools.ts +2 -0
- package/.pi/extensions/lib/extension-load-guard.ts +39 -0
- package/.pi/extensions/lib/harness-posthog.ts +6 -1
- package/.pi/extensions/lib/harness-spawn-budget.ts +75 -0
- package/.pi/extensions/lib/harness-subagent-auth.ts +123 -0
- package/.pi/extensions/lib/{harness-subagents/harness-subagent-policy.ts → harness-subagent-policy.ts} +34 -9
- package/.pi/extensions/lib/harness-subagent-precheck.ts +95 -0
- package/.pi/extensions/lib/harness-subagents-bridge.ts +176 -0
- package/.pi/extensions/lib/plan-approval/create-plan.ts +9 -7
- package/.pi/extensions/lib/plan-approval/plan-review.ts +393 -0
- package/.pi/extensions/lib/plan-approval/schema.ts +16 -1
- package/.pi/extensions/lib/plan-approval/types.ts +16 -0
- package/.pi/extensions/lib/plan-approval/validate.ts +2 -0
- package/.pi/extensions/lib/plan-debate-envelope.ts +84 -0
- package/.pi/extensions/lib/{harness-subagents/spawn-policy.ts → spawn-policy.ts} +2 -5
- package/.pi/extensions/policy-gate.ts +1 -1
- package/.pi/extensions/review-integrity.ts +48 -29
- package/.pi/extensions/ultimate-pi-vcc.ts +5 -0
- package/.pi/harness/agents.manifest.json +126 -82
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -6
- package/.pi/harness/docs/adrs/0033-parent-orchestrated-planning.md +34 -0
- package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +41 -0
- package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +27 -0
- package/.pi/harness/docs/adrs/README.md +2 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r1.yaml +25 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r4.yaml +26 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/sprint-audit-r4.yaml +5 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/plan-packet.yaml +196 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/plan-review.md +14 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/research-brief.yaml +32 -0
- package/.pi/harness/evals/smoke/run-context.fixture.json +1 -1
- package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +88 -0
- package/.pi/harness/specs/README.md +1 -1
- package/.pi/harness/specs/harness-posthog-event.schema.json +6 -1
- package/.pi/harness/specs/harness-spawn-context.schema.json +2 -1
- package/.pi/harness/specs/plan-adversary-brief.schema.json +45 -0
- package/.pi/harness/specs/plan-decomposition-brief.schema.json +108 -0
- package/.pi/harness/specs/plan-execution-plan-brief.schema.json +13 -0
- package/.pi/harness/specs/plan-execution-plan.schema.json +255 -0
- package/.pi/harness/specs/plan-hypothesis-brief.schema.json +96 -0
- package/.pi/harness/specs/plan-hypothesis-eval.schema.json +61 -0
- package/.pi/harness/specs/plan-packet.schema.json +14 -5
- package/.pi/harness/specs/plan-review-round-draft.schema.json +68 -0
- package/.pi/harness/specs/plan-sprint-audit-turn.schema.json +29 -0
- package/.pi/harness/specs/plan-stack-brief.schema.json +65 -0
- package/.pi/harness/specs/plan-validation-turn.schema.json +42 -0
- package/.pi/harness/specs/round-result.schema.json +16 -9
- package/.pi/lib/debate-orchestrator-types.ts +38 -0
- package/.pi/lib/harness-agent-discovery.mjs +81 -0
- package/.pi/lib/harness-run-context.ts +76 -38
- package/.pi/lib/harness-yaml.mjs +73 -0
- package/.pi/lib/harness-yaml.ts +90 -0
- package/.pi/prompts/harness-auto.md +13 -11
- package/.pi/prompts/harness-critic.md +2 -2
- package/.pi/prompts/harness-eval.md +3 -3
- package/.pi/prompts/harness-incident.md +2 -2
- package/.pi/prompts/harness-plan.md +106 -37
- package/.pi/prompts/harness-review.md +2 -2
- package/.pi/prompts/harness-router-tune.md +1 -1
- package/.pi/prompts/harness-run.md +2 -2
- package/.pi/prompts/harness-setup.md +15 -6
- package/.pi/prompts/harness-trace.md +2 -2
- package/.pi/scripts/harness-agents-manifest.mjs +1 -1
- package/.pi/scripts/harness-resolve-up-pkg.mjs +13 -0
- package/.pi/scripts/harness-verify.mjs +28 -19
- package/.pi/scripts/validate-plan-dag.mjs +258 -0
- package/.pi/scripts/vendor-sync-pi-subagents.sh +19 -0
- package/CHANGELOG.md +24 -0
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/biome.json +4 -1
- package/package.json +6 -4
- package/.pi/agents/harness/planner.md +0 -54
- package/.pi/extensions/lib/harness-subagents/agent-loader.ts +0 -126
- package/.pi/extensions/lib/harness-subagents/agent-manifest.ts +0 -119
- package/.pi/extensions/lib/harness-subagents/agent-parser.ts +0 -87
- package/.pi/extensions/lib/harness-subagents/blackboard-tool.ts +0 -118
- package/.pi/extensions/lib/harness-subagents/blackboard.ts +0 -175
- package/.pi/extensions/lib/harness-subagents/parent-ask-user-bridge.ts +0 -10
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-bridge.ts +0 -310
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-hooks.ts +0 -59
- package/.pi/extensions/lib/harness-subagents/types-blackboard.ts +0 -27
- package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +0 -558
- package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +0 -684
- package/.pi/extensions/lib/harness-subagents/vendored/agent-types.ts +0 -175
- package/.pi/extensions/lib/harness-subagents/vendored/context.ts +0 -59
- package/.pi/extensions/lib/harness-subagents/vendored/cross-extension-rpc.ts +0 -134
- package/.pi/extensions/lib/harness-subagents/vendored/custom-agents.ts +0 -5
- package/.pi/extensions/lib/harness-subagents/vendored/default-agents.ts +0 -123
- package/.pi/extensions/lib/harness-subagents/vendored/env.ts +0 -43
- package/.pi/extensions/lib/harness-subagents/vendored/group-join.ts +0 -144
- package/.pi/extensions/lib/harness-subagents/vendored/index.ts +0 -2494
- package/.pi/extensions/lib/harness-subagents/vendored/invocation-config.ts +0 -52
- package/.pi/extensions/lib/harness-subagents/vendored/memory.ts +0 -182
- package/.pi/extensions/lib/harness-subagents/vendored/model-resolver.ts +0 -92
- package/.pi/extensions/lib/harness-subagents/vendored/output-file.ts +0 -115
- package/.pi/extensions/lib/harness-subagents/vendored/prompts.ts +0 -103
- package/.pi/extensions/lib/harness-subagents/vendored/schedule-store.ts +0 -177
- package/.pi/extensions/lib/harness-subagents/vendored/schedule.ts +0 -416
- package/.pi/extensions/lib/harness-subagents/vendored/settings.ts +0 -210
- package/.pi/extensions/lib/harness-subagents/vendored/skill-loader.ts +0 -108
- package/.pi/extensions/lib/harness-subagents/vendored/types.ts +0 -187
- package/.pi/extensions/lib/harness-subagents/vendored/ui/agent-widget.ts +0 -639
- package/.pi/extensions/lib/harness-subagents/vendored/ui/conversation-viewer.ts +0 -324
- package/.pi/extensions/lib/harness-subagents/vendored/ui/schedule-menu.ts +0 -110
- package/.pi/extensions/lib/harness-subagents/vendored/usage.ts +0 -71
- package/.pi/extensions/lib/harness-subagents/vendored/worktree.ts +0 -195
- /package/.pi/extensions/{00-ultimate-pi-system-prompt.ts → custom-system-prompt.ts} +0 -0
|
@@ -1,71 +1,140 @@
|
|
|
1
1
|
---
|
|
2
|
-
description:
|
|
2
|
+
description: PM-grade harness plan — scouts, ExecutionPlan, DAG validation, Review Gate debate, approval.
|
|
3
3
|
argument-hint: "\"<task>\" [--risk low|med|high] [--budget <amount>] [--quick]"
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# harness-plan
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
You are the **planning PM** for this harness run. Produce an execution baseline (`plan-packet.yaml` + `plan-review.md`), not strategy theater. Parent owns `ask_user`, `approve_plan`, `create_plan`, debate bus commands, and YAML writes under `.pi/harness/runs/<run_id>/`.
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
Never `write`/`edit` the final canonical packet except via **`write_harness_yaml`** for run artifacts and **`create_plan`** after approval. Do not paste JSON into `.yaml` files — subagents emit JSON; you convert via `write_harness_yaml`.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
## Allowed subagents
|
|
13
13
|
|
|
14
|
-
-
|
|
15
|
-
-
|
|
14
|
+
- `harness/planning/scout-graphify`
|
|
15
|
+
- `harness/planning/scout-structure`
|
|
16
|
+
- `harness/planning/scout-semantic` (skip when `--quick`)
|
|
17
|
+
- `harness/planning/decompose`
|
|
18
|
+
- `harness/planning/hypothesis`
|
|
19
|
+
- `harness/planning/stack-researcher`
|
|
20
|
+
- `harness/planning/execution-plan-author`
|
|
21
|
+
- `harness/planning/hypothesis-validator` (debate R1 only)
|
|
22
|
+
- `harness/planning/plan-evaluator`
|
|
23
|
+
- `harness/planning/plan-adversary`
|
|
24
|
+
- `harness/planning/sprint-contract-auditor`
|
|
25
|
+
- `harness/planning/review-integrator`
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
Read **harness-debate-plan** skill before Review Gate rounds.
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
## Performance rules
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
1. Use `subagent` with `agentScope: "both"` and parallel `tasks` where lanes are independent.
|
|
32
|
+
2. Each `subagent` call blocks until subprocesses finish — batch parallel scouts in one `tasks` array.
|
|
33
|
+
3. Cap: **12** harness subagent invocations per parent session (extension-enforced).
|
|
34
|
+
4. Compact task text: embed `HarnessSpawnContext` JSON + lane-specific instructions only.
|
|
35
|
+
|
|
36
|
+
## Step 0 — Parse `$ARGUMENTS`
|
|
37
|
+
|
|
38
|
+
- task (required)
|
|
39
|
+
- `--risk low|med|high`, `--budget`, `--quick`
|
|
40
|
+
|
|
41
|
+
`--quick` skips **scout-semantic** and post-run adversary only — **never** skip graphify, structure, decompose, hypothesis, stack research, execution plan, DAG validation, or **4-round plan debate**.
|
|
22
42
|
|
|
23
43
|
## Active plan context
|
|
24
44
|
|
|
25
|
-
Use
|
|
45
|
+
Use `[HarnessActivePlan]` / `[HarnessRunContext]` only. On revise: preserve `plan_id` / `task_id`. Canonical paths: `plan-packet.yaml`, `research-brief.yaml`, `artifacts/*.yaml`.
|
|
46
|
+
|
|
47
|
+
## Phase 1 — Parallel scouts
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"agentScope": "both",
|
|
52
|
+
"tasks": [
|
|
53
|
+
{ "agent": "harness/planning/scout-graphify", "task": "<HarnessSpawnContext + graphify lane>", "timeoutMs": 90000 },
|
|
54
|
+
{ "agent": "harness/planning/scout-structure", "task": "<HarnessSpawnContext + structure lane>", "timeoutMs": 90000 }
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
Add `harness/planning/scout-semantic` to `tasks` unless `--quick`. Require graphify + structure success.
|
|
60
|
+
|
|
61
|
+
## Phase 2 & 3 — Decompose + hypothesis (parallel)
|
|
62
|
+
|
|
63
|
+
One `subagent` call with `tasks` for `harness/planning/decompose` and `harness/planning/hypothesis`. Parse `PlanDecompositionBrief` and `PlanHypothesisBrief` from outputs. Persist with `write_harness_yaml` → `artifacts/decomposition.yaml` and `artifacts/hypothesis.yaml`.
|
|
64
|
+
|
|
65
|
+
## Phase 4 — Draft shell + fork
|
|
66
|
+
|
|
67
|
+
Build draft `PlanPacket` (`contract_version: "1.1.0"`):
|
|
68
|
+
|
|
69
|
+
- `scope`, `assumptions`, `acceptance_checks`, `risk_level`, `rollback_plan`
|
|
70
|
+
- `execution_plan` placeholder until Phase 4b
|
|
71
|
+
|
|
72
|
+
`ask_user` when `dialectical_fork` is material.
|
|
26
73
|
|
|
27
|
-
|
|
74
|
+
Initialize `research-brief.yaml` with decomposition + hypothesis (`write_harness_yaml`).
|
|
28
75
|
|
|
29
|
-
|
|
30
|
-
- Pass `mode: revise` using the `HarnessSpawnContext` JSON in `[HarnessRunContext]`.
|
|
76
|
+
## Phase 4a — Stack research
|
|
31
77
|
|
|
32
|
-
|
|
78
|
+
```
|
|
79
|
+
subagent({ agentScope: "both", agent: "harness/planning/stack-researcher", task: "<HarnessSpawnContext + stack research brief>" })
|
|
80
|
+
```
|
|
33
81
|
|
|
34
|
-
|
|
82
|
+
`write_harness_yaml` → `artifacts/stack.yaml`; merge into `research-brief.yaml` → `stack`.
|
|
35
83
|
|
|
36
|
-
|
|
37
|
-
2. Spawn **once** with **`inherit_context: false`**:
|
|
84
|
+
## Phase 4b — Execution plan author
|
|
38
85
|
|
|
39
86
|
```
|
|
40
|
-
|
|
87
|
+
subagent({ agentScope: "both", agent: "harness/planning/execution-plan-author", task: "<HarnessSpawnContext + execution plan brief>" })
|
|
41
88
|
```
|
|
42
89
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
90
|
+
Merge `execution_plan` into draft `plan-packet.yaml` (`write_harness_yaml`). Save `artifacts/execution-plan-draft.yaml` the same way.
|
|
91
|
+
|
|
92
|
+
## Phase 4c — DAG validation (hard gate)
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
node .pi/scripts/validate-plan-dag.mjs --packet .pi/harness/runs/<run_id>/plan-packet.yaml --write
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Must **pass** before debate. On fail: fix via author or parent patches, re-run.
|
|
99
|
+
|
|
100
|
+
## Phase 5 — Review Gate debate (4 rounds, even with `--quick`)
|
|
101
|
+
|
|
102
|
+
1. `/harness-debate-open plan-<run_id>`
|
|
103
|
+
2. For rounds 1–4 (`debate_round_focus`: spec, wbs, schedule, quality):
|
|
104
|
+
|
|
105
|
+
| Round | Extra spawns (before integrator) |
|
|
106
|
+
|-------|----------------------------------|
|
|
107
|
+
| 1 | `hypothesis-validator` (blind: task + hypothesis only) → `plan-evaluator` → `plan-adversary` |
|
|
108
|
+
| 2 | `plan-evaluator` → `plan-adversary` (optional `sprint-contract-auditor` if done_criteria thin) |
|
|
109
|
+
| 3 | `plan-evaluator` → `plan-adversary` |
|
|
110
|
+
| 4 | `plan-evaluator` → `plan-adversary` → **`sprint-contract-auditor` (required)** |
|
|
111
|
+
|
|
112
|
+
Then `review-integrator` → `write_harness_yaml` → `artifacts/review-round-r{N}.yaml` → build bus envelope → `/harness-debate-round '<json>'`.
|
|
113
|
+
|
|
114
|
+
3. `/harness-debate-consensus` after round 4.
|
|
115
|
+
|
|
116
|
+
**R1 blind rule:** hypothesis-validator prompt must exclude decomposition, scouts, PlanPacket, prior debate.
|
|
117
|
+
|
|
118
|
+
If R1 `revision_recommended` or `relevance.passes === false`: one `hypothesis` re-spawn, update brief, continue.
|
|
47
119
|
|
|
48
|
-
|
|
120
|
+
**Blockers:** `policy_decision: block` → do not `approve_plan`. `human_required` → `ask_user` before approval.
|
|
49
121
|
|
|
50
|
-
|
|
122
|
+
## Phase 5b — Revise packet
|
|
51
123
|
|
|
52
|
-
|
|
124
|
+
Apply `recommended_packet_patches` from last integrator round. Re-run `validate-plan-dag.mjs`. If >30% work items changed, one partial re-round on affected focus.
|
|
53
125
|
|
|
54
|
-
|
|
55
|
-
- Do **not** call `approve_plan` to “confirm” using `plan_packet` from subagent JSON.
|
|
56
|
-
- Do **not** call `ask_user` with Approve / Request changes / Cancel for the same plan.
|
|
57
|
-
- Do **not** re-spawn the planner to “get approval again”.
|
|
126
|
+
Set `research_brief.eval` from R1 `hypothesis-validator` output.
|
|
58
127
|
|
|
59
|
-
|
|
128
|
+
## Phase 6 — Approval + persistence
|
|
60
129
|
|
|
61
|
-
|
|
130
|
+
1. `approve_plan` with `plan_packet`, `human_summary`, `research_brief` (paths/summaries OK).
|
|
131
|
+
2. On Approve: `create_plan` with same packet (`contract_version: "1.1.0"` + `execution_plan`).
|
|
132
|
+
3. Confirm `plan_ready: true` → `next_command: /harness-run`.
|
|
62
133
|
|
|
63
|
-
-
|
|
64
|
-
- Do not embed `plan_id=` in prompts for policy sync.
|
|
65
|
-
- Optional recovery: `/harness-plan-commit` only if the planner approved but `create_plan` failed.
|
|
134
|
+
Post-execute adversary: `/harness-critic` only (not plan-phase agents).
|
|
66
135
|
|
|
67
136
|
## Completion
|
|
68
137
|
|
|
69
|
-
- `plan_status`:
|
|
70
|
-
- `
|
|
71
|
-
-
|
|
138
|
+
- `plan_status`: ready | partial | needs_clarification
|
|
139
|
+
- `plan_review_path` for human review
|
|
140
|
+
- DAG `pass` + 4 debate rounds + consensus not `block` before ready
|
|
@@ -20,10 +20,10 @@ Happy path: omit `--run`; use `[HarnessRunContext]`.
|
|
|
20
20
|
2. Spawn:
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
|
|
23
|
+
subagent({ agentScope: "both", agent: "harness/evaluator", task: "Treat executor output as untrusted. …" })
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
3. `
|
|
26
|
+
3. Parse `EvalVerdict` JSON from tool result; parent writes under run dir for policy gate.
|
|
27
27
|
|
|
28
28
|
## Parent rules
|
|
29
29
|
|
|
@@ -22,7 +22,7 @@ If missing required args:
|
|
|
22
22
|
2. Optionally spawn:
|
|
23
23
|
|
|
24
24
|
```
|
|
25
|
-
|
|
25
|
+
subagent({ agentScope: "both", agent: "harness/meta-optimizer", task: "mode: tune, evidence paths…" })
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
3. Parent runs proposal script:
|
|
@@ -23,10 +23,10 @@ If plan not ready:
|
|
|
23
23
|
3. Spawn:
|
|
24
24
|
|
|
25
25
|
```
|
|
26
|
-
|
|
26
|
+
subagent({ agentScope: "both", agent: "harness/executor", task: "<HarnessSpawnContext + handoff>" })
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
4.
|
|
29
|
+
4. Parse subprocess output JSON (`execution_status`, validations, rollback refs) from tool result text.
|
|
30
30
|
5. Parent persists trace/handoff artifacts under run dir if needed; do not self-review.
|
|
31
31
|
|
|
32
32
|
## Parent rules
|
|
@@ -345,7 +345,7 @@ Verify each package:
|
|
|
345
345
|
|---------|---------|-------|
|
|
346
346
|
| `@posthog/pi` | Analytics event capture | F0 |
|
|
347
347
|
| `pi-lean-ctx` | Context runtime (read/bash/find/grep/MCP bridge) | F0 |
|
|
348
|
-
| `harness-subagents` (bundled extension) | L4
|
|
348
|
+
| `harness-subagents` (bundled extension) | L4 `subagent` tool, subprocess spawns, package agents | P16 |
|
|
349
349
|
| Vendored `pi-vcc` (`vendor/pi-vcc`, `.pi/extensions/ultimate-pi-vcc.ts`) | VCC compaction / `vcc_recall` — env-only: `HARNESS_VCC_COMPACTION` (default on), `HARNESS_VCC_DEBUG` | Shipped |
|
|
350
350
|
| `pi-model-router` | Vendored (`vendor/`); activates after `.pi/model-router.json` exists | F0 |
|
|
351
351
|
|
|
@@ -383,11 +383,11 @@ Manual override: **`/router profile auto`** anytime after reload if they changed
|
|
|
383
383
|
|
|
384
384
|
## Step 3.6 — Harness agents (package-resolved)
|
|
385
385
|
|
|
386
|
-
`harness-subagents` loads agents from the installed **`ultimate-pi`** package (`$UP_PKG/.pi/agents/**`) with namespaced ids (`harness/
|
|
386
|
+
`harness-subagents` loads agents from the installed **`ultimate-pi`** package (`$UP_PKG/.pi/agents/**`) with namespaced ids (`harness/executor`, `harness/planning/scout-graphify`, `pi-pi/agent-expert`). **Do not copy** agents into the project unless you want a deliberate override.
|
|
387
387
|
|
|
388
388
|
**Slash commands are orchestrators:** `/harness-plan`, `/harness-run`, etc. spawn `harness/*` agents via the `Agent` tool — bootstrap stays **script-first**; only optionally spawn `harness/sentrux-bootstrap` for Sentrux (see Step 4.2).
|
|
389
389
|
|
|
390
|
-
Optional per-repo overrides: place `.md` files at the **same relative path** (e.g. `.pi/agents/harness/
|
|
390
|
+
Optional per-repo overrides: place `.md` files at the **same relative path** (e.g. `.pi/agents/harness/planning/scout-graphify.md` overrides the package scout).
|
|
391
391
|
|
|
392
392
|
Verify manifest drift after `pi update ultimate-pi`:
|
|
393
393
|
|
|
@@ -478,16 +478,25 @@ Template keys (placeholders — user fills secrets): `HARNESS_TELEMETRY_ENABLED`
|
|
|
478
478
|
|
|
479
479
|
### 4.1 — .gitignore Entries
|
|
480
480
|
|
|
481
|
-
Ensure `.gitignore` contains:
|
|
481
|
+
Ensure `.gitignore` contains harness runtime entries (see repo root `.gitignore` — **do not** ignore `.pi/harness/specs/`; JSON schemas are shared contracts):
|
|
482
|
+
|
|
482
483
|
```
|
|
483
484
|
.env
|
|
484
485
|
.web/
|
|
485
486
|
.searxng/
|
|
486
487
|
.raw/
|
|
487
488
|
.vault-meta/
|
|
488
|
-
.pi/harness/
|
|
489
|
+
.pi/harness/active-run.json
|
|
490
|
+
.pi/harness/release-readiness-report.md
|
|
489
491
|
.pi/harness/plans/
|
|
490
|
-
.pi/harness/
|
|
492
|
+
.pi/harness/critics/
|
|
493
|
+
.pi/harness/runs/**
|
|
494
|
+
!.pi/harness/runs/README.md
|
|
495
|
+
.pi/harness/incidents/*
|
|
496
|
+
!.pi/harness/incidents/README.md
|
|
497
|
+
.pi/harness/debates/*
|
|
498
|
+
!.pi/harness/debates/README.md
|
|
499
|
+
.pi/harness/router/proposals/*
|
|
491
500
|
|
|
492
501
|
# Model router config (user-specific — generated from env)
|
|
493
502
|
.pi/model-router.json
|
|
@@ -20,10 +20,10 @@ Happy path: omit `--run`.
|
|
|
20
20
|
2. Spawn:
|
|
21
21
|
|
|
22
22
|
```
|
|
23
|
-
|
|
23
|
+
subagent({ agentScope: "both", agent: "harness/trace-librarian", task: "…" })
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
3.
|
|
26
|
+
3. Present timeline and artifact index from tool result to user.
|
|
27
27
|
|
|
28
28
|
## Completion
|
|
29
29
|
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
isSafeAgentId,
|
|
15
15
|
sha256Content,
|
|
16
16
|
walkAgentsDir,
|
|
17
|
-
} from "
|
|
17
|
+
} from "../lib/harness-agent-discovery.mjs";
|
|
18
18
|
|
|
19
19
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
20
20
|
const MANIFEST_PATH = join(ROOT, ".pi", "harness", "agents.manifest.json");
|
|
@@ -30,7 +30,20 @@ function hasHarnessScripts(root) {
|
|
|
30
30
|
return existsSync(join(root, ".pi", "scripts", "harness-cli-verify.sh"));
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function isSourceCheckout(root) {
|
|
34
|
+
try {
|
|
35
|
+
const pkg = requireFromCwd.resolve("./package.json");
|
|
36
|
+
return dirname(pkg) === root;
|
|
37
|
+
} catch {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
33
42
|
function tryResolveUltimatePi() {
|
|
43
|
+
if (hasHarnessScripts(process.cwd()) && isSourceCheckout(process.cwd())) {
|
|
44
|
+
return process.cwd();
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
if (process.env.ULTIMATE_PI_PKG) {
|
|
35
48
|
const envRoot = process.env.ULTIMATE_PI_PKG;
|
|
36
49
|
if (hasHarnessScripts(envRoot)) return envRoot;
|
|
@@ -202,32 +202,41 @@ async function main() {
|
|
|
202
202
|
if (!(await fileExists(runCtxLib))) fail("missing lib/harness-run-context.ts");
|
|
203
203
|
ok("lib/harness-run-context.ts");
|
|
204
204
|
|
|
205
|
-
const
|
|
205
|
+
const subagentsVendor = join(
|
|
206
|
+
ROOT,
|
|
207
|
+
"vendor",
|
|
208
|
+
"pi-subagents",
|
|
209
|
+
"src",
|
|
210
|
+
"subagents.ts",
|
|
211
|
+
);
|
|
212
|
+
if (!(await fileExists(subagentsVendor))) {
|
|
213
|
+
fail("missing vendor/pi-subagents/src/subagents.ts");
|
|
214
|
+
}
|
|
215
|
+
const bridgePath = join(
|
|
206
216
|
ROOT,
|
|
207
217
|
".pi",
|
|
208
218
|
"extensions",
|
|
209
219
|
"lib",
|
|
210
|
-
"harness-subagents",
|
|
211
|
-
"vendored",
|
|
212
|
-
"index.ts",
|
|
220
|
+
"harness-subagents-bridge.ts",
|
|
213
221
|
);
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
/from ["']([^"']*harness-run-context\.js)["']/,
|
|
217
|
-
);
|
|
218
|
-
if (!runCtxImport) {
|
|
219
|
-
fail("vendored/index.ts must import harness-run-context.js");
|
|
222
|
+
if (!(await fileExists(bridgePath))) {
|
|
223
|
+
fail("missing harness-subagents-bridge.ts");
|
|
220
224
|
}
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
if (
|
|
226
|
-
fail(
|
|
227
|
-
|
|
228
|
-
|
|
225
|
+
const bridgeSrc = await readFile(bridgePath, "utf-8");
|
|
226
|
+
if (!bridgeSrc.includes("precheckHarnessSubagentSpawn")) {
|
|
227
|
+
fail("harness-subagents-bridge must run precheckHarnessSubagentSpawn");
|
|
228
|
+
}
|
|
229
|
+
if (!bridgeSrc.includes("packageRoot")) {
|
|
230
|
+
fail("harness-subagents-bridge must pass packageRoot for agent discovery");
|
|
231
|
+
}
|
|
232
|
+
const subagentsSrc = await readFile(subagentsVendor, "utf-8");
|
|
233
|
+
if (!subagentsSrc.includes("discoverAgents")) {
|
|
234
|
+
fail("vendor subagents.ts must implement discoverAgents");
|
|
235
|
+
}
|
|
236
|
+
if (!subagentsSrc.includes("packageRoot")) {
|
|
237
|
+
fail("vendor subagents.ts must pass packageRoot into discovery");
|
|
229
238
|
}
|
|
230
|
-
ok("
|
|
239
|
+
ok("vendor pi-subagents + harness bridge");
|
|
231
240
|
|
|
232
241
|
const policyGateSrc = await readFile(
|
|
233
242
|
join(ROOT, ".pi", "extensions", "policy-gate.ts"),
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* validate-plan-dag — deterministic ExecutionPlan DAG checks (YAML packet in).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { access } from "node:fs/promises";
|
|
7
|
+
import { constants } from "node:fs";
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { readYamlFile, writeYamlFile } from "../lib/harness-yaml.mjs";
|
|
11
|
+
|
|
12
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
13
|
+
|
|
14
|
+
const MINIMUMS = {
|
|
15
|
+
low: { phases: 2, work_items: 5, acceptance_checks: 3, risks: 0 },
|
|
16
|
+
med: { phases: 3, work_items: 8, acceptance_checks: 5, risks: 3 },
|
|
17
|
+
high: { phases: 4, work_items: 12, acceptance_checks: 8, risks: 3 },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function fail(msg) {
|
|
21
|
+
console.error(`validate-plan-dag: FAIL: ${msg}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ok(msg) {
|
|
26
|
+
console.log(` ✓ ${msg}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function topoSort(workItems) {
|
|
30
|
+
const ids = new Set(workItems.map((w) => w.work_item_id));
|
|
31
|
+
const adj = new Map();
|
|
32
|
+
for (const w of workItems) {
|
|
33
|
+
adj.set(w.work_item_id, (w.depends_on ?? []).filter((d) => ids.has(d)));
|
|
34
|
+
}
|
|
35
|
+
const visited = new Set();
|
|
36
|
+
const stack = new Set();
|
|
37
|
+
const order = [];
|
|
38
|
+
const cycles = [];
|
|
39
|
+
|
|
40
|
+
function dfs(n, path) {
|
|
41
|
+
if (stack.has(n)) {
|
|
42
|
+
cycles.push([...path, n]);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (visited.has(n)) return;
|
|
46
|
+
visited.add(n);
|
|
47
|
+
stack.add(n);
|
|
48
|
+
for (const d of adj.get(n) ?? []) dfs(d, [...path, n]);
|
|
49
|
+
stack.delete(n);
|
|
50
|
+
order.push(n);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const id of ids) dfs(id, []);
|
|
54
|
+
order.reverse();
|
|
55
|
+
return { order, cycles };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function computeCriticalPath(workItems) {
|
|
59
|
+
const ids = new Set(workItems.map((w) => w.work_item_id));
|
|
60
|
+
const len = new Map();
|
|
61
|
+
for (const w of workItems) len.set(w.work_item_id, 0);
|
|
62
|
+
let changed = true;
|
|
63
|
+
while (changed) {
|
|
64
|
+
changed = false;
|
|
65
|
+
for (const w of workItems) {
|
|
66
|
+
const deps = (w.depends_on ?? []).filter((d) => ids.has(d));
|
|
67
|
+
const base = deps.length === 0 ? 0 : Math.max(...deps.map((d) => len.get(d) ?? 0)) + 1;
|
|
68
|
+
if (base > (len.get(w.work_item_id) ?? 0)) {
|
|
69
|
+
len.set(w.work_item_id, base);
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const maxLen = Math.max(0, ...len.values());
|
|
75
|
+
const end = workItems.filter((w) => len.get(w.work_item_id) === maxLen).map((w) => w.work_item_id);
|
|
76
|
+
// Backtrack one longest path
|
|
77
|
+
const path = [];
|
|
78
|
+
let cur = end[0];
|
|
79
|
+
if (!cur) return [];
|
|
80
|
+
const byId = new Map(workItems.map((w) => [w.work_item_id, w]));
|
|
81
|
+
while (cur) {
|
|
82
|
+
path.unshift(cur);
|
|
83
|
+
const w = byId.get(cur);
|
|
84
|
+
const deps = (w?.depends_on ?? []).filter((d) => ids.has(d));
|
|
85
|
+
if (deps.length === 0) break;
|
|
86
|
+
cur = deps.reduce((a, b) => ((len.get(a) ?? 0) >= (len.get(b) ?? 0) ? a : b));
|
|
87
|
+
}
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function validateExecutionPlan(packet, projectRoot = ROOT) {
|
|
92
|
+
const errors = [];
|
|
93
|
+
const ep = packet.execution_plan;
|
|
94
|
+
if (!ep) {
|
|
95
|
+
errors.push("execution_plan required");
|
|
96
|
+
return { status: "fail", errors, report: null };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const risk = packet.risk_level ?? "med";
|
|
100
|
+
const min = MINIMUMS[risk] ?? MINIMUMS.med;
|
|
101
|
+
const phases = ep.phases ?? [];
|
|
102
|
+
const workItems = ep.work_items ?? [];
|
|
103
|
+
const conflicts = [];
|
|
104
|
+
|
|
105
|
+
if (phases.length < min.phases) {
|
|
106
|
+
errors.push(`need >= ${min.phases} phases for risk ${risk}`);
|
|
107
|
+
}
|
|
108
|
+
if (workItems.length < min.work_items) {
|
|
109
|
+
errors.push(`need >= ${min.work_items} work_items for risk ${risk}`);
|
|
110
|
+
}
|
|
111
|
+
const ac = packet.acceptance_checks ?? [];
|
|
112
|
+
if (ac.length < min.acceptance_checks) {
|
|
113
|
+
errors.push(`need >= ${min.acceptance_checks} acceptance_checks`);
|
|
114
|
+
}
|
|
115
|
+
if ((ep.risk_register ?? []).length < min.risks) {
|
|
116
|
+
errors.push(`need >= ${min.risks} risks for risk ${risk}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const phaseIds = new Set(phases.map((p) => p.phase_id));
|
|
120
|
+
const phaseIndex = new Map(phases.map((p, i) => [p.phase_id, i]));
|
|
121
|
+
const wiIds = new Set(workItems.map((w) => w.work_item_id));
|
|
122
|
+
|
|
123
|
+
for (const p of phases) {
|
|
124
|
+
if (!p.exit_criteria?.length) errors.push(`phase ${p.phase_id} missing exit_criteria`);
|
|
125
|
+
if (!p.work_item_ids?.length) errors.push(`phase ${p.phase_id} has no work items`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const wiInPhase = new Set();
|
|
129
|
+
for (const w of workItems) {
|
|
130
|
+
if (!phaseIds.has(w.phase_id)) {
|
|
131
|
+
errors.push(`work_item ${w.work_item_id} unknown phase_id`);
|
|
132
|
+
}
|
|
133
|
+
wiInPhase.add(w.work_item_id);
|
|
134
|
+
for (const d of w.depends_on ?? []) {
|
|
135
|
+
if (!wiIds.has(d)) errors.push(`work_item ${w.work_item_id} depends_on missing ${d}`);
|
|
136
|
+
}
|
|
137
|
+
if (!w.non_code && (!w.files || w.files.length === 0)) {
|
|
138
|
+
errors.push(`work_item ${w.work_item_id} needs files[] or non_code: true`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const p of phases) {
|
|
143
|
+
for (const wid of p.work_item_ids ?? []) {
|
|
144
|
+
if (!wiIds.has(wid)) errors.push(`phase ${p.phase_id} references missing ${wid}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const { order, cycles } = topoSort(workItems);
|
|
149
|
+
if (cycles.length) errors.push(`cycle detected: ${JSON.stringify(cycles[0])}`);
|
|
150
|
+
|
|
151
|
+
// File conflicts
|
|
152
|
+
for (let i = 0; i < workItems.length; i++) {
|
|
153
|
+
for (let j = i + 1; j < workItems.length; j++) {
|
|
154
|
+
const a = workItems[i];
|
|
155
|
+
const b = workItems[j];
|
|
156
|
+
const filesA = new Set(a.files ?? []);
|
|
157
|
+
const overlap = (b.files ?? []).filter((f) => filesA.has(f));
|
|
158
|
+
if (overlap.length === 0) continue;
|
|
159
|
+
const reachable = (from, to, seen = new Set()) => {
|
|
160
|
+
if (from === to) return true;
|
|
161
|
+
if (seen.has(from)) return false;
|
|
162
|
+
seen.add(from);
|
|
163
|
+
const w = workItems.find((x) => x.work_item_id === from);
|
|
164
|
+
for (const d of w?.depends_on ?? []) {
|
|
165
|
+
if (reachable(d, to, seen)) return true;
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
};
|
|
169
|
+
if (!reachable(a.work_item_id, b.work_item_id) && !reachable(b.work_item_id, a.work_item_id)) {
|
|
170
|
+
if ((phaseIndex.get(a.phase_id) ?? 0) === (phaseIndex.get(b.phase_id) ?? 0)) {
|
|
171
|
+
conflicts.push(
|
|
172
|
+
`file overlap ${overlap.join(",")} between ${a.work_item_id} and ${b.work_item_id} without dependency`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const computedCp = computeCriticalPath(workItems);
|
|
180
|
+
const authorCp = ep.schedule_metadata?.critical_path_work_item_ids ?? [];
|
|
181
|
+
if (computedCp.length >= 3 && authorCp.length) {
|
|
182
|
+
const same =
|
|
183
|
+
authorCp.length === computedCp.length &&
|
|
184
|
+
authorCp.every((id, i) => id === computedCp[i]);
|
|
185
|
+
if (!same) {
|
|
186
|
+
errors.push(
|
|
187
|
+
`critical_path mismatch author=${authorCp.join("→")} computed=${computedCp.join("→")}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const acIds = new Set(
|
|
193
|
+
ac.map((c) => (typeof c === "string" ? c : c.id)).filter(Boolean),
|
|
194
|
+
);
|
|
195
|
+
for (const w of workItems) {
|
|
196
|
+
for (const acid of w.acceptance_check_ids ?? []) {
|
|
197
|
+
if (!acIds.has(acid)) errors.push(`${w.work_item_id} references orphan ${acid}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const acid of acIds) {
|
|
201
|
+
const used = workItems.some((w) => (w.acceptance_check_ids ?? []).includes(acid));
|
|
202
|
+
if (!used) errors.push(`orphan acceptance check ${acid}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const status = errors.length === 0 && conflicts.length === 0 ? "pass" : "fail";
|
|
206
|
+
const report = {
|
|
207
|
+
status,
|
|
208
|
+
topological_order: order,
|
|
209
|
+
cycles,
|
|
210
|
+
conflicts: [...conflicts, ...errors],
|
|
211
|
+
};
|
|
212
|
+
return { status, errors: [...errors, ...conflicts], report };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function main() {
|
|
216
|
+
const args = process.argv.slice(2);
|
|
217
|
+
let packetPath = null;
|
|
218
|
+
let writeBack = false;
|
|
219
|
+
for (let i = 0; i < args.length; i++) {
|
|
220
|
+
if (args[i] === "--packet" && args[i + 1]) packetPath = args[++i];
|
|
221
|
+
else if (args[i] === "--write") writeBack = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!packetPath) {
|
|
225
|
+
console.error("Usage: validate-plan-dag.mjs --packet <plan-packet.yaml> [--write]");
|
|
226
|
+
process.exit(2);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const abs = resolve(packetPath);
|
|
230
|
+
try {
|
|
231
|
+
await access(abs, constants.R_OK);
|
|
232
|
+
} catch {
|
|
233
|
+
fail(`cannot read ${abs}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const packet = await readYamlFile(abs);
|
|
237
|
+
const { status, errors, report } = validateExecutionPlan(packet, dirname(abs));
|
|
238
|
+
|
|
239
|
+
if (writeBack && report && packet.execution_plan) {
|
|
240
|
+
packet.execution_plan.dag_validation = {
|
|
241
|
+
status: report.status,
|
|
242
|
+
topological_order: report.topological_order,
|
|
243
|
+
cycles: report.cycles,
|
|
244
|
+
conflicts: report.conflicts,
|
|
245
|
+
};
|
|
246
|
+
await writeYamlFile(abs, packet);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (status !== "pass") {
|
|
250
|
+
for (const e of errors) console.error(` - ${e}`);
|
|
251
|
+
fail("validation failed");
|
|
252
|
+
}
|
|
253
|
+
ok(`DAG validation pass (${report.topological_order.length} work items)`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
257
|
+
main();
|
|
258
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Re-fetch upstream pi-subagents from narumiruna/pi-extensions.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
+
VEND="$ROOT/vendor/pi-subagents"
|
|
6
|
+
BASE="https://raw.githubusercontent.com/narumiruna/pi-extensions/main/extensions/pi-subagents"
|
|
7
|
+
|
|
8
|
+
mkdir -p "$VEND/src"
|
|
9
|
+
curl -fsSL "$BASE/LICENSE" -o "$VEND/LICENSE"
|
|
10
|
+
curl -fsSL "$BASE/src/subagents.ts" -o "$VEND/src/subagents.upstream.ts"
|
|
11
|
+
|
|
12
|
+
# Preserve ultimate-pi harness extensions (agents.ts, harness patches applied to subagents.ts manually or via merge).
|
|
13
|
+
if [[ ! -f "$VEND/src/agents.ts" ]]; then
|
|
14
|
+
curl -fsSL "$BASE/src/agents.ts" -o "$VEND/src/agents.ts"
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
sed -i 's/from "typebox"/from "@sinclair\/typebox"/g' "$VEND/src/subagents.upstream.ts" 2>/dev/null || true
|
|
18
|
+
|
|
19
|
+
echo "Fetched upstream into $VEND/src/subagents.upstream.ts — merge harness changes into subagents.ts before commit."
|