ultimate-pi 0.19.0 → 0.20.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.
Files changed (85) hide show
  1. package/.agents/skills/web-retrieval/SKILL.md +163 -0
  2. package/.agents/skills/wiki-autoresearch/SKILL.md +6 -6
  3. package/.pi/SYSTEM.md +30 -12
  4. package/.pi/agents/harness/planning/implementation-researcher.md +1 -1
  5. package/.pi/agents/harness/planning/stack-researcher.md +5 -1
  6. package/.pi/agents/harness/running/executor.md +42 -1
  7. package/.pi/agents/harness/web-retrieval/web-answerer.md +35 -0
  8. package/.pi/agents/harness/web-retrieval/web-criteria-verifier.md +28 -0
  9. package/.pi/agents/harness/web-retrieval/web-gap-analyzer.md +31 -0
  10. package/.pi/agents/harness/web-retrieval/web-query-expander-fast.md +34 -0
  11. package/.pi/agents/harness/web-retrieval/web-query-expander.md +60 -0
  12. package/.pi/agents/harness/web-retrieval/web-summarizer.md +18 -0
  13. package/.pi/extensions/harness-anchored-edit.ts +141 -0
  14. package/.pi/extensions/harness-web-guard.ts +2 -1
  15. package/.pi/extensions/harness-web-tools.ts +689 -51
  16. package/.pi/harness/agents.manifest.json +30 -6
  17. package/.pi/harness/agents.policy.yaml +37 -4
  18. package/.pi/harness/docs/adrs/0050-agentic-web-retrieval-stack.md +46 -0
  19. package/.pi/harness/docs/adrs/0051-hash-anchored-executor-edits.md +41 -0
  20. package/.pi/harness/docs/adrs/README.md +2 -0
  21. package/.pi/harness/docs/harness-web-search.md +97 -0
  22. package/.pi/harness/docs/practice-map.md +11 -0
  23. package/.pi/harness/env.harness.template +9 -1
  24. package/.pi/harness/examples/web-heuristic-angles.project.yaml +22 -0
  25. package/.pi/harness/web-heuristic-angles.json +278 -0
  26. package/.pi/harness/web-heuristic-angles.yaml +182 -0
  27. package/.pi/lib/agents-policy.d.mts +4 -0
  28. package/.pi/lib/agents-policy.mjs +49 -1
  29. package/.pi/lib/agents-policy.ts +1 -0
  30. package/.pi/lib/harness-anchored-edit/.hash_anchors +1721 -0
  31. package/.pi/lib/harness-anchored-edit/anchor-state.ts +320 -0
  32. package/.pi/lib/harness-anchored-edit/apply-anchored-edits.ts +161 -0
  33. package/.pi/lib/harness-anchored-edit/edit-executor.ts +146 -0
  34. package/.pi/lib/harness-anchored-edit/index.ts +9 -0
  35. package/.pi/lib/harness-anchored-edit/line-protocol.ts +38 -0
  36. package/.pi/lib/harness-anchored-edit/settings.ts +1 -0
  37. package/.pi/lib/harness-anchored-edit/task-id.ts +8 -0
  38. package/.pi/lib/harness-anchored-edit/types.ts +19 -0
  39. package/.pi/lib/harness-lens/clients/anchored-edit-autopatch.ts +158 -0
  40. package/.pi/lib/harness-lens/index.ts +24 -7
  41. package/.pi/lib/harness-subagent-auth.ts +39 -9
  42. package/.pi/lib/harness-subagents-bridge.ts +24 -1
  43. package/.pi/lib/harness-web/artifacts.ts +200 -0
  44. package/.pi/lib/harness-web/cache.ts +369 -0
  45. package/.pi/lib/harness-web/run-cli.ts +42 -2
  46. package/.pi/prompts/harness-plan.md +1 -0
  47. package/.pi/prompts/harness-setup.md +3 -1
  48. package/.pi/prompts/harness-steer.md +1 -1
  49. package/.pi/scripts/gen-web-heuristic-angles-json.mjs +24 -0
  50. package/.pi/scripts/harness-anchored-edit-smoke.mjs +45 -0
  51. package/.pi/scripts/harness-cli-verify.sh +5 -0
  52. package/.pi/scripts/harness-verify.mjs +145 -0
  53. package/.pi/scripts/harness-web-policy-guard.mjs +1 -1
  54. package/.pi/scripts/harness-web.py +218 -15
  55. package/.pi/scripts/harness_web/deep_search.py +55 -0
  56. package/.pi/scripts/harness_web/evidence_bundle.py +47 -0
  57. package/.pi/scripts/harness_web/find_similar.py +88 -0
  58. package/.pi/scripts/harness_web/heuristic_angles_shipped.py +85 -0
  59. package/.pi/scripts/harness_web/heuristic_config.py +251 -0
  60. package/.pi/scripts/harness_web/highlights.py +47 -0
  61. package/.pi/scripts/harness_web/multi_search.py +59 -0
  62. package/.pi/scripts/harness_web/output.py +24 -0
  63. package/.pi/scripts/harness_web/query_angles.py +116 -0
  64. package/.pi/scripts/harness_web/rank.py +163 -0
  65. package/.pi/scripts/harness_web/scrape.py +30 -0
  66. package/.pi/scripts/run-tests.mjs +64 -0
  67. package/.pi/scripts/tests/test_harness_web_heuristic_config.py +132 -0
  68. package/.pi/scripts/tests/test_harness_web_query_angles.py +45 -0
  69. package/.pi/scripts/tests/test_harness_web_rank.py +56 -0
  70. package/AGENTS.md +2 -2
  71. package/CHANGELOG.md +12 -0
  72. package/THIRD_PARTY_NOTICES.md +7 -0
  73. package/package.json +7 -4
  74. package/vendor/pi-subagents/src/agents.ts +5 -0
  75. package/vendor/pi-subagents/src/subagents.ts +22 -3
  76. package/.agents/skills/scrapling-web/SKILL.md +0 -98
  77. package/.pi/extensions/00-posthog-network-bootstrap.ts +0 -11
  78. package/.pi/scripts/harness_web/__pycache__/__init__.cpython-314.pyc +0 -0
  79. package/.pi/scripts/harness_web/__pycache__/config.cpython-314.pyc +0 -0
  80. package/.pi/scripts/harness_web/__pycache__/output.cpython-314.pyc +0 -0
  81. package/.pi/scripts/harness_web/__pycache__/scrape.cpython-314.pyc +0 -0
  82. package/.pi/scripts/harness_web/__pycache__/search.cpython-314.pyc +0 -0
  83. package/.pi/scripts/harness_web/__pycache__/search_ddg.cpython-314.pyc +0 -0
  84. package/.pi/scripts/harness_web/__pycache__/search_searxng.cpython-314.pyc +0 -0
  85. package/.pi/scripts/release.sh +0 -338
@@ -0,0 +1,278 @@
1
+ {
2
+ "version": 1,
3
+ "max_angles": 8,
4
+ "base": [
5
+ {
6
+ "id": "definitional",
7
+ "query": "{query}",
8
+ "rationale": "Core intent phrasing"
9
+ },
10
+ {
11
+ "id": "authoritative",
12
+ "query": "{query} official documentation OR specification OR RFC",
13
+ "rationale": "Primary specs and vendor docs"
14
+ }
15
+ ],
16
+ "categories": {
17
+ "code": [
18
+ {
19
+ "id": "github",
20
+ "query": "{query} site:github.com",
21
+ "rationale": "Source, issues, discussions"
22
+ },
23
+ {
24
+ "id": "stackoverflow",
25
+ "query": "{query} site:stackoverflow.com",
26
+ "rationale": "Debugging and API usage Q&A"
27
+ },
28
+ {
29
+ "id": "stackexchange",
30
+ "query": "{query} site:stackexchange.com",
31
+ "rationale": "Broader SE network (Super User, Server Fault, etc.)"
32
+ },
33
+ {
34
+ "id": "readthedocs",
35
+ "query": "{query} site:readthedocs.io",
36
+ "rationale": "OSS library documentation"
37
+ },
38
+ {
39
+ "id": "mdn",
40
+ "query": "{query} site:developer.mozilla.org",
41
+ "rationale": "Web platform and browser APIs"
42
+ },
43
+ {
44
+ "id": "package_registries",
45
+ "query": "{query} site:npmjs.com OR site:pypi.org OR site:pkg.go.dev OR site:crates.io",
46
+ "rationale": "Package metadata across major ecosystems"
47
+ },
48
+ {
49
+ "id": "microsoft_learn",
50
+ "query": "{query} site:learn.microsoft.com",
51
+ "rationale": ".NET, Azure, Windows, and enterprise stacks"
52
+ },
53
+ {
54
+ "id": "hacker_news",
55
+ "query": "{query} site:news.ycombinator.com",
56
+ "rationale": "High-signal practitioner discussion"
57
+ },
58
+ {
59
+ "id": "gitlab",
60
+ "query": "{query} site:gitlab.com",
61
+ "rationale": "Alternate host and CI-visible code"
62
+ },
63
+ {
64
+ "id": "devto",
65
+ "query": "{query} site:dev.to OR site:medium.com",
66
+ "rationale": "Tutorials and implementation writeups"
67
+ }
68
+ ],
69
+ "paper": [
70
+ {
71
+ "id": "arxiv",
72
+ "query": "{query} site:arxiv.org",
73
+ "rationale": "Preprints and latest ML/CS uploads"
74
+ },
75
+ {
76
+ "id": "semantic_scholar",
77
+ "query": "{query} site:semanticscholar.org",
78
+ "rationale": "Citations, influences, and PDF links"
79
+ },
80
+ {
81
+ "id": "google_scholar",
82
+ "query": "{query} site:scholar.google.com",
83
+ "rationale": "Broad academic discovery"
84
+ },
85
+ {
86
+ "id": "papers_with_code",
87
+ "query": "{query} site:paperswithcode.com",
88
+ "rationale": "Benchmarks tied to implementations"
89
+ },
90
+ {
91
+ "id": "openreview",
92
+ "query": "{query} site:openreview.net",
93
+ "rationale": "Peer reviews and ML conference submissions"
94
+ },
95
+ {
96
+ "id": "acl_anthology",
97
+ "query": "{query} site:aclanthology.org",
98
+ "rationale": "NLP and computational linguistics"
99
+ },
100
+ {
101
+ "id": "acm_dl",
102
+ "query": "{query} site:dl.acm.org",
103
+ "rationale": "ACM proceedings and journals"
104
+ },
105
+ {
106
+ "id": "pubmed",
107
+ "query": "{query} site:pubmed.ncbi.nlm.nih.gov",
108
+ "rationale": "Biomedical and life-sciences literature"
109
+ }
110
+ ],
111
+ "news": [
112
+ {
113
+ "id": "recent",
114
+ "query": "{query} news 2025 2026",
115
+ "rationale": "Recency-biased open web"
116
+ },
117
+ {
118
+ "id": "wire_reuters",
119
+ "query": "{query} site:reuters.com",
120
+ "rationale": "Wire-service reporting"
121
+ },
122
+ {
123
+ "id": "wire_ap",
124
+ "query": "{query} site:apnews.com",
125
+ "rationale": "Associated Press coverage"
126
+ },
127
+ {
128
+ "id": "tech_press",
129
+ "query": "{query} site:techcrunch.com OR site:theverge.com OR site:arstechnica.com",
130
+ "rationale": "Technology industry news"
131
+ },
132
+ {
133
+ "id": "business_press",
134
+ "query": "{query} site:bloomberg.com OR site:ft.com OR site:wsj.com",
135
+ "rationale": "Markets and business context"
136
+ },
137
+ {
138
+ "id": "analysis",
139
+ "query": "{query} in-depth analysis explainer",
140
+ "rationale": "Long-form journalism and explainers"
141
+ },
142
+ {
143
+ "id": "bbc",
144
+ "query": "{query} site:bbc.com/news",
145
+ "rationale": "International general news desk"
146
+ }
147
+ ],
148
+ "company": [
149
+ {
150
+ "id": "official_site",
151
+ "query": "{query} official website",
152
+ "rationale": "Company-controlled messaging"
153
+ },
154
+ {
155
+ "id": "crunchbase",
156
+ "query": "{query} site:crunchbase.com",
157
+ "rationale": "Funding, investors, and competitors"
158
+ },
159
+ {
160
+ "id": "linkedin_company",
161
+ "query": "{query} site:linkedin.com/company",
162
+ "rationale": "Headcount, hiring, and positioning"
163
+ },
164
+ {
165
+ "id": "sec_filings",
166
+ "query": "{query} site:sec.gov 10-K OR 10-Q OR S-1",
167
+ "rationale": "US public-company disclosures"
168
+ },
169
+ {
170
+ "id": "g2_reviews",
171
+ "query": "{query} site:g2.com OR site:capterra.com",
172
+ "rationale": "B2B software reviews and comparisons"
173
+ },
174
+ {
175
+ "id": "company_news",
176
+ "query": "{query} company announcement press release",
177
+ "rationale": "Launches, partnerships, and earnings"
178
+ },
179
+ {
180
+ "id": "glassdoor",
181
+ "query": "{query} site:glassdoor.com",
182
+ "rationale": "Employee sentiment and culture signals"
183
+ }
184
+ ],
185
+ "people": [
186
+ {
187
+ "id": "linkedin",
188
+ "query": "{query} site:linkedin.com/in",
189
+ "rationale": "Professional profiles"
190
+ },
191
+ {
192
+ "id": "github_person",
193
+ "query": "{query} site:github.com",
194
+ "rationale": "Open-source footprint for builders"
195
+ },
196
+ {
197
+ "id": "wikipedia",
198
+ "query": "{query} site:en.wikipedia.org",
199
+ "rationale": "Neutral biographical baseline"
200
+ },
201
+ {
202
+ "id": "scholar_person",
203
+ "query": "{query} site:scholar.google.com",
204
+ "rationale": "Publication record for researchers"
205
+ },
206
+ {
207
+ "id": "interviews",
208
+ "query": "{query} interview podcast keynote",
209
+ "rationale": "First-person statements and talks"
210
+ },
211
+ {
212
+ "id": "twitter_x",
213
+ "query": "{query} site:x.com OR site:twitter.com",
214
+ "rationale": "Public statements and discourse"
215
+ }
216
+ ],
217
+ "security": [
218
+ {
219
+ "id": "cve_nvd",
220
+ "query": "{query} CVE site:nvd.nist.gov",
221
+ "rationale": "National Vulnerability Database"
222
+ },
223
+ {
224
+ "id": "owasp",
225
+ "query": "{query} site:owasp.org",
226
+ "rationale": "AppSec standards and cheat sheets"
227
+ },
228
+ {
229
+ "id": "cwe",
230
+ "query": "{query} site:cwe.mitre.org",
231
+ "rationale": "Weakness taxonomy"
232
+ },
233
+ {
234
+ "id": "github_advisories",
235
+ "query": "{query} site:github.com/advisories OR dependabot",
236
+ "rationale": "Ecosystem security advisories"
237
+ },
238
+ {
239
+ "id": "snyk_blog",
240
+ "query": "{query} site:snyk.io/blog OR vulnerability",
241
+ "rationale": "Practitioner security writeups"
242
+ }
243
+ ],
244
+ "default": [
245
+ {
246
+ "id": "technical",
247
+ "query": "{query} how it works architecture internals",
248
+ "rationale": "Mechanism and design"
249
+ },
250
+ {
251
+ "id": "criticism",
252
+ "query": "{query} limitations criticism drawbacks",
253
+ "rationale": "Counterpoints and failure modes"
254
+ },
255
+ {
256
+ "id": "wikipedia",
257
+ "query": "{query} site:en.wikipedia.org",
258
+ "rationale": "Structured overview"
259
+ },
260
+ {
261
+ "id": "comparison",
262
+ "query": "{query} vs alternatives comparison benchmark",
263
+ "rationale": "Competitive landscape"
264
+ },
265
+ {
266
+ "id": "reddit",
267
+ "query": "{query} site:reddit.com",
268
+ "rationale": "Community experience reports"
269
+ },
270
+ {
271
+ "id": "hn_default",
272
+ "query": "{query} site:news.ycombinator.com",
273
+ "rationale": "Practitioner threads when category unknown"
274
+ }
275
+ ]
276
+ }
277
+ }
278
+
@@ -0,0 +1,182 @@
1
+ # WRS emergency heuristic angles (--expand-heuristic / expandHeuristic:true).
2
+ # Package defaults (ultimate-pi). External projects: copy
3
+ # .pi/harness/examples/web-heuristic-angles.project.yaml to
4
+ # <your-project>/.pi/harness/web-heuristic-angles.yaml and edit.
5
+ #
6
+ # Placeholders: {query} → user search string
7
+ # Order matters: base angles run first, then category (deduped by id, capped at max_angles).
8
+ # JSON mirror (no PyYAML): run `node .pi/scripts/gen-web-heuristic-angles-json.mjs` after edits.
9
+ # Stdlib fallback: .pi/scripts/harness_web/heuristic_angles_shipped.py
10
+
11
+ version: 1
12
+ max_angles: 8
13
+
14
+ base:
15
+ - id: definitional
16
+ query: "{query}"
17
+ rationale: Core intent phrasing
18
+ - id: authoritative
19
+ query: "{query} official documentation OR specification OR RFC"
20
+ rationale: Primary specs and vendor docs
21
+
22
+ categories:
23
+ code:
24
+ - id: github
25
+ query: "{query} site:github.com"
26
+ rationale: Source, issues, discussions
27
+ - id: stackoverflow
28
+ query: "{query} site:stackoverflow.com"
29
+ rationale: Debugging and API usage Q&A
30
+ - id: stackexchange
31
+ query: "{query} site:stackexchange.com"
32
+ rationale: Broader SE network (Super User, Server Fault, etc.)
33
+ - id: readthedocs
34
+ query: "{query} site:readthedocs.io"
35
+ rationale: OSS library documentation
36
+ - id: mdn
37
+ query: "{query} site:developer.mozilla.org"
38
+ rationale: Web platform and browser APIs
39
+ - id: package_registries
40
+ query: "{query} site:npmjs.com OR site:pypi.org OR site:pkg.go.dev OR site:crates.io"
41
+ rationale: Package metadata across major ecosystems
42
+ - id: microsoft_learn
43
+ query: "{query} site:learn.microsoft.com"
44
+ rationale: .NET, Azure, Windows, and enterprise stacks
45
+ - id: hacker_news
46
+ query: "{query} site:news.ycombinator.com"
47
+ rationale: High-signal practitioner discussion
48
+ - id: gitlab
49
+ query: "{query} site:gitlab.com"
50
+ rationale: Alternate host and CI-visible code
51
+ - id: devto
52
+ query: "{query} site:dev.to OR site:medium.com"
53
+ rationale: Tutorials and implementation writeups
54
+
55
+ paper:
56
+ - id: arxiv
57
+ query: "{query} site:arxiv.org"
58
+ rationale: Preprints and latest ML/CS uploads
59
+ - id: semantic_scholar
60
+ query: "{query} site:semanticscholar.org"
61
+ rationale: Citations, influences, and PDF links
62
+ - id: google_scholar
63
+ query: "{query} site:scholar.google.com"
64
+ rationale: Broad academic discovery
65
+ - id: papers_with_code
66
+ query: "{query} site:paperswithcode.com"
67
+ rationale: Benchmarks tied to implementations
68
+ - id: openreview
69
+ query: "{query} site:openreview.net"
70
+ rationale: Peer reviews and ML conference submissions
71
+ - id: acl_anthology
72
+ query: "{query} site:aclanthology.org"
73
+ rationale: NLP and computational linguistics
74
+ - id: acm_dl
75
+ query: "{query} site:dl.acm.org"
76
+ rationale: ACM proceedings and journals
77
+ - id: pubmed
78
+ query: "{query} site:pubmed.ncbi.nlm.nih.gov"
79
+ rationale: Biomedical and life-sciences literature
80
+
81
+ news:
82
+ - id: recent
83
+ query: "{query} news 2025 2026"
84
+ rationale: Recency-biased open web
85
+ - id: wire_reuters
86
+ query: "{query} site:reuters.com"
87
+ rationale: Wire-service reporting
88
+ - id: wire_ap
89
+ query: "{query} site:apnews.com"
90
+ rationale: Associated Press coverage
91
+ - id: tech_press
92
+ query: "{query} site:techcrunch.com OR site:theverge.com OR site:arstechnica.com"
93
+ rationale: Technology industry news
94
+ - id: business_press
95
+ query: "{query} site:bloomberg.com OR site:ft.com OR site:wsj.com"
96
+ rationale: Markets and business context
97
+ - id: analysis
98
+ query: "{query} in-depth analysis explainer"
99
+ rationale: Long-form journalism and explainers
100
+ - id: bbc
101
+ query: "{query} site:bbc.com/news"
102
+ rationale: International general news desk
103
+
104
+ company:
105
+ - id: official_site
106
+ query: "{query} official website"
107
+ rationale: Company-controlled messaging
108
+ - id: crunchbase
109
+ query: "{query} site:crunchbase.com"
110
+ rationale: Funding, investors, and competitors
111
+ - id: linkedin_company
112
+ query: "{query} site:linkedin.com/company"
113
+ rationale: Headcount, hiring, and positioning
114
+ - id: sec_filings
115
+ query: "{query} site:sec.gov 10-K OR 10-Q OR S-1"
116
+ rationale: US public-company disclosures
117
+ - id: g2_reviews
118
+ query: "{query} site:g2.com OR site:capterra.com"
119
+ rationale: B2B software reviews and comparisons
120
+ - id: company_news
121
+ query: "{query} company announcement press release"
122
+ rationale: Launches, partnerships, and earnings
123
+ - id: glassdoor
124
+ query: "{query} site:glassdoor.com"
125
+ rationale: Employee sentiment and culture signals
126
+
127
+ people:
128
+ - id: linkedin
129
+ query: "{query} site:linkedin.com/in"
130
+ rationale: Professional profiles
131
+ - id: github_person
132
+ query: "{query} site:github.com"
133
+ rationale: Open-source footprint for builders
134
+ - id: wikipedia
135
+ query: "{query} site:en.wikipedia.org"
136
+ rationale: Neutral biographical baseline
137
+ - id: scholar_person
138
+ query: "{query} site:scholar.google.com"
139
+ rationale: Publication record for researchers
140
+ - id: interviews
141
+ query: "{query} interview podcast keynote"
142
+ rationale: First-person statements and talks
143
+ - id: twitter_x
144
+ query: "{query} site:x.com OR site:twitter.com"
145
+ rationale: Public statements and discourse
146
+
147
+ security:
148
+ - id: cve_nvd
149
+ query: "{query} CVE site:nvd.nist.gov"
150
+ rationale: National Vulnerability Database
151
+ - id: owasp
152
+ query: "{query} site:owasp.org"
153
+ rationale: AppSec standards and cheat sheets
154
+ - id: cwe
155
+ query: "{query} site:cwe.mitre.org"
156
+ rationale: Weakness taxonomy
157
+ - id: github_advisories
158
+ query: "{query} site:github.com/advisories OR dependabot"
159
+ rationale: Ecosystem security advisories
160
+ - id: snyk_blog
161
+ query: "{query} site:snyk.io/blog OR vulnerability"
162
+ rationale: Practitioner security writeups
163
+
164
+ default:
165
+ - id: technical
166
+ query: "{query} how it works architecture internals"
167
+ rationale: Mechanism and design
168
+ - id: criticism
169
+ query: "{query} limitations criticism drawbacks"
170
+ rationale: Counterpoints and failure modes
171
+ - id: wikipedia
172
+ query: "{query} site:en.wikipedia.org"
173
+ rationale: Structured overview
174
+ - id: comparison
175
+ query: "{query} vs alternatives comparison benchmark"
176
+ rationale: Competitive landscape
177
+ - id: reddit
178
+ query: "{query} site:reddit.com"
179
+ rationale: Community experience reports
180
+ - id: hn_default
181
+ query: "{query} site:news.ycombinator.com"
182
+ rationale: Practitioner threads when category unknown
@@ -6,6 +6,10 @@ export interface AgentPolicySpec {
6
6
  kind: string;
7
7
  effectiveTools: string[];
8
8
  extensionsOff: boolean;
9
+ /** Subprocess-only: load curated -e extensions instead of full .pi/extensions. */
10
+ extensionBundle?: string;
11
+ extensionsFull: boolean;
12
+ noBuiltinTools: boolean;
9
13
  readOnly: boolean;
10
14
  maxTurns?: number;
11
15
  thinking?: string;
@@ -31,10 +31,26 @@ const MUTATING_TOOLS = new Set(["write", "edit"]);
31
31
 
32
32
  const cache = new Map();
33
33
 
34
+ const EXTENSION_BUNDLE_MODULES = {
35
+ executor: [
36
+ "subagent-governance.ts",
37
+ "harness-anchored-edit.ts",
38
+ "harness-lens.ts",
39
+ ],
40
+ };
41
+
34
42
  export function packageAgentsPolicyPath(packageRoot) {
35
43
  return join(packageRoot, ".pi", "harness", "agents.policy.yaml");
36
44
  }
37
45
 
46
+ /** Absolute paths for subprocess `-e` loads (curated; avoids parent-only extensions). */
47
+ export function resolveExtensionBundlePaths(packageRoot, bundleName) {
48
+ const modules = EXTENSION_BUNDLE_MODULES[bundleName];
49
+ if (!modules) return [];
50
+ const extDir = join(packageRoot, ".pi", "extensions");
51
+ return modules.map((name) => join(extDir, name));
52
+ }
53
+
38
54
  export function projectAgentsPolicyPath(projectRoot) {
39
55
  return join(projectRoot, ".pi", "agents.policy.yaml");
40
56
  }
@@ -52,12 +68,19 @@ function readYamlFile(path) {
52
68
  }
53
69
  }
54
70
 
71
+ function normalizeExtensionBundle(raw) {
72
+ if (typeof raw.extension_bundle !== "string") return undefined;
73
+ const bundle = raw.extension_bundle.trim();
74
+ return bundle.length > 0 ? bundle : undefined;
75
+ }
76
+
55
77
  function normalizeKindEntry(raw) {
56
78
  if (!raw || typeof raw !== "object") return null;
57
79
  const tools = Array.isArray(raw.tools) ? raw.tools.map(String) : [];
58
80
  return {
59
81
  tools,
60
82
  extensions: raw.extensions === false ? false : Boolean(raw.extensions),
83
+ extensionBundle: normalizeExtensionBundle(raw),
61
84
  readOnly: raw.read_only === true,
62
85
  maxTurns:
63
86
  typeof raw.max_turns === "number" && raw.max_turns > 0
@@ -67,6 +90,8 @@ function normalizeKindEntry(raw) {
67
90
  typeof raw.thinking === "string" && raw.thinking.trim()
68
91
  ? raw.thinking.trim()
69
92
  : undefined,
93
+ model:
94
+ typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : undefined,
70
95
  };
71
96
  }
72
97
 
@@ -91,6 +116,7 @@ function normalizeAgentEntry(raw) {
91
116
  : raw.extensions === true
92
117
  ? true
93
118
  : undefined,
119
+ extensionBundle: normalizeExtensionBundle(raw),
94
120
  maxTurns:
95
121
  typeof raw.max_turns === "number" && raw.max_turns > 0
96
122
  ? raw.max_turns
@@ -99,6 +125,8 @@ function normalizeAgentEntry(raw) {
99
125
  typeof raw.thinking === "string" && raw.thinking.trim()
100
126
  ? raw.thinking.trim()
101
127
  : undefined,
128
+ model:
129
+ typeof raw.model === "string" && raw.model.trim() ? raw.model.trim() : undefined,
102
130
  };
103
131
  }
104
132
 
@@ -158,13 +186,29 @@ export function resolveEffectiveTools(agentId, merged) {
158
186
  for (const t of entry.toolsDeny ?? []) base.delete(t);
159
187
  for (const t of BUILTIN_DENY_TOOLS) base.delete(t);
160
188
 
189
+ const extensionBundle =
190
+ entry.extensionBundle ?? kind.extensionBundle ?? undefined;
191
+ const extensionsFull =
192
+ !extensionBundle &&
193
+ (entry.extensions === true
194
+ ? true
195
+ : entry.extensions === false
196
+ ? false
197
+ : Boolean(kind.extensions));
198
+ const extensionsOff = !extensionsFull;
199
+
161
200
  return {
162
201
  kind: kindName,
163
202
  effectiveTools: [...base],
164
- extensionsOff: entry.extensions === true ? false : entry.extensions === false ? true : !kind.extensions,
203
+ extensionsOff,
204
+ extensionBundle,
205
+ extensionsFull,
206
+ /** Suppress Pi builtins when harness read/edit register (full extensions or subprocess bundle). */
207
+ noBuiltinTools: extensionsFull || Boolean(extensionBundle),
165
208
  readOnly: kind.readOnly,
166
209
  maxTurns: entry.maxTurns ?? kind.maxTurns,
167
210
  thinking: entry.thinking ?? kind.thinking,
211
+ model: entry.model ?? kind.model,
168
212
  submitTool: entry.submitTool,
169
213
  };
170
214
  }
@@ -299,8 +343,12 @@ export function applyAgentPolicyToConfig(agent, packageRoot, projectRoot) {
299
343
  ...agent,
300
344
  tools: spec.effectiveTools.length > 0 ? spec.effectiveTools : undefined,
301
345
  extensionsOff: spec.extensionsOff,
346
+ extensionBundle: spec.extensionBundle,
347
+ extensionsFull: spec.extensionsFull,
348
+ noBuiltinTools: spec.noBuiltinTools,
302
349
  maxTurns: spec.maxTurns ?? agent.maxTurns,
303
350
  thinking: spec.thinking ?? agent.thinking,
351
+ model: spec.model ?? agent.model,
304
352
  };
305
353
  }
306
354
 
@@ -16,4 +16,5 @@ export {
16
16
  projectAgentsPolicyPath,
17
17
  projectPoliciesDir,
18
18
  resolveEffectiveTools,
19
+ resolveExtensionBundlePaths,
19
20
  } from "./agents-policy.mjs";