@pfoundation/ocadvisor 26.9.0 → 26.9.2
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/README.md +420 -43
- package/dist/artificialAnalysis.d.ts +4 -0
- package/dist/artificialAnalysis.d.ts.map +1 -0
- package/dist/artificialAnalysis.js +77 -0
- package/dist/artificialAnalysis.js.map +1 -0
- package/dist/benchmarkConfig.d.ts +19 -0
- package/dist/benchmarkConfig.d.ts.map +1 -0
- package/dist/benchmarkConfig.js +58 -0
- package/dist/benchmarkConfig.js.map +1 -0
- package/dist/benchmarkEvidence.d.ts +37 -0
- package/dist/benchmarkEvidence.d.ts.map +1 -0
- package/dist/benchmarkEvidence.js +81 -0
- package/dist/benchmarkEvidence.js.map +1 -0
- package/dist/benchmarkMatch.d.ts +57 -0
- package/dist/benchmarkMatch.d.ts.map +1 -0
- package/dist/benchmarkMatch.js +236 -0
- package/dist/benchmarkMatch.js.map +1 -0
- package/dist/benchmarkStore.d.ts +44 -0
- package/dist/benchmarkStore.d.ts.map +1 -0
- package/dist/benchmarkStore.js +308 -0
- package/dist/benchmarkStore.js.map +1 -0
- package/dist/benchmarkTypes.d.ts +48 -0
- package/dist/benchmarkTypes.d.ts.map +1 -0
- package/dist/benchmarkTypes.js +298 -0
- package/dist/benchmarkTypes.js.map +1 -0
- package/dist/benchmarkUpdate.d.ts +33 -0
- package/dist/benchmarkUpdate.d.ts.map +1 -0
- package/dist/benchmarkUpdate.js +327 -0
- package/dist/benchmarkUpdate.js.map +1 -0
- package/dist/cli.d.ts +13 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +377 -0
- package/dist/cli.js.map +1 -0
- package/dist/data/artificialAnalysis.mappings.json +197 -0
- package/dist/data/artificialAnalysis.snapshot.json +17684 -0
- package/dist/modelProfiles.d.ts +45 -0
- package/dist/modelProfiles.d.ts.map +1 -0
- package/dist/modelProfiles.js +141 -0
- package/dist/modelProfiles.js.map +1 -0
- package/dist/ocAdvisor.d.ts +152 -7
- package/dist/ocAdvisor.d.ts.map +1 -1
- package/dist/ocAdvisor.js +681 -54
- package/dist/ocAdvisor.js.map +1 -1
- package/dist/typesafeGate.d.ts +89 -0
- package/dist/typesafeGate.d.ts.map +1 -0
- package/dist/typesafeGate.js +463 -0
- package/dist/typesafeGate.js.map +1 -0
- package/dist/typesafeState.d.ts +94 -0
- package/dist/typesafeState.d.ts.map +1 -0
- package/dist/typesafeState.js +301 -0
- package/dist/typesafeState.js.map +1 -0
- package/package.json +9 -3
package/README.md
CHANGED
|
@@ -40,17 +40,24 @@ and the usage report accept both `advisor` and the pre-rename `ocAdvisor`.
|
|
|
40
40
|
|
|
41
41
|
## Configuration
|
|
42
42
|
|
|
43
|
-
The advisor model and limits are configurable. Defaults
|
|
44
|
-
`anthropic/claude-fable-5-1#
|
|
45
|
-
transcript cap
|
|
43
|
+
The advisor model and limits are configurable. Defaults:
|
|
44
|
+
`anthropic/claude-fable-5-1#xhigh`, a 300 s generation timeout, no
|
|
45
|
+
transcript cap, TypeSafe screening on when a key is present, and
|
|
46
|
+
model-capability evidence from a local Artificial Analysis snapshot when
|
|
47
|
+
one is available.
|
|
46
48
|
|
|
47
49
|
| Option | Default | Meaning |
|
|
48
50
|
|---|---|---|
|
|
49
51
|
| `model` | `claude-fable-5-1` | Model id, or a full `provider/model#variant` reference |
|
|
50
52
|
| `provider` | `anthropic` | Provider id (overrides the provider in `model`) |
|
|
51
|
-
| `variant` | `
|
|
53
|
+
| `variant` | `xhigh` | Reasoning-effort variant; `null` or `"none"` pins no variant |
|
|
52
54
|
| `timeoutMs` | `300000` | Per-consultation generation timeout, in milliseconds |
|
|
53
55
|
| `maxTranscriptChars` | `0` | Cap on transcript size (`0` = unlimited); the most recent tail is kept |
|
|
56
|
+
| `agentEffort` | `false` | Let the agent pick effort per call: `true` allows `high`, `xhigh`, `max`; an array or comma string sets an explicit allow-list |
|
|
57
|
+
| `disabledForModels` | `[]` | Exact caller `provider/model` IDs that must not see or invoke advisor. Applies to every effort variant of that ID. A string array is the documented form; a comma-separated string is also accepted |
|
|
58
|
+
| `typesafe` | enabled with a key | `false` disables screening; `true` or an object enables it (see below) |
|
|
59
|
+
| `benchmarks.path` | data-directory snapshot | Absolute path to the local Artificial Analysis snapshot file |
|
|
60
|
+
| `benchmarks.mappingsPath` | beside the snapshot | Absolute path to the local model-mapping overrides file |
|
|
54
61
|
|
|
55
62
|
Set them as plugin options in `opencode.json`. Because a plugin loaded from
|
|
56
63
|
the auto-discovered `plugin/` directory cannot receive options, list it
|
|
@@ -67,48 +74,319 @@ explicitly in the `plugins` array:
|
|
|
67
74
|
}
|
|
68
75
|
```
|
|
69
76
|
|
|
77
|
+
To let the calling agent choose the reasoning effort per consultation:
|
|
78
|
+
|
|
79
|
+
```jsonc
|
|
80
|
+
{
|
|
81
|
+
"plugins": [
|
|
82
|
+
{
|
|
83
|
+
"package": "@pfoundation/ocadvisor",
|
|
84
|
+
"options": { "agentEffort": true }
|
|
85
|
+
}
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
With `agentEffort` enabled the tool accepts an optional `effort` argument
|
|
91
|
+
(one of the allowed levels); when the agent omits it, the configured
|
|
92
|
+
`variant` is used. A requested effort that is not a variant of the advisor
|
|
93
|
+
model fails the call with an `invalid_effort` error instead of silently
|
|
94
|
+
falling back.
|
|
95
|
+
|
|
96
|
+
To hide advisor from selected *caller* models (for example Astra), list
|
|
97
|
+
their exact `provider/model` IDs. This is independent of `model` /
|
|
98
|
+
`provider`, which configure the advisor itself:
|
|
99
|
+
|
|
100
|
+
```jsonc
|
|
101
|
+
{
|
|
102
|
+
"$schema": "https://opencode.ai/config.json",
|
|
103
|
+
"plugins": [
|
|
104
|
+
{
|
|
105
|
+
"package": "@pfoundation/ocadvisor",
|
|
106
|
+
"options": {
|
|
107
|
+
"disabledForModels": ["openai/gpt-6-astra"]
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Matching is exact and case-sensitive on `provider/model`. Effort variants
|
|
115
|
+
are ignored, so `openai/gpt-6-astra#xhigh` is still excluded by
|
|
116
|
+
`openai/gpt-6-astra`. Nearby names (`openai/gpt-6-astra-preview`) and other
|
|
117
|
+
providers do not match. Gateway IDs keep the serving provider: list
|
|
118
|
+
`openrouter/openai/gpt-6-astra` to exclude that route, not
|
|
119
|
+
`openai/gpt-6-astra`. Bare names, `#variant` suffixes, and wildcards are
|
|
120
|
+
rejected at config load.
|
|
121
|
+
|
|
122
|
+
Plugin options replace the environment list rather than merging it. An
|
|
123
|
+
explicit `[]` (or a blank string) clears configurable exclusions even when
|
|
124
|
+
`OCADVISOR_DISABLED_FOR_MODELS` is set:
|
|
125
|
+
|
|
126
|
+
```sh
|
|
127
|
+
OCADVISOR_DISABLED_FOR_MODELS=openai/gpt-6-astra,anthropic/claude-opus-5
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
After changing plugin options, restart the background service
|
|
131
|
+
(`opencode service restart`) so the server reloads configuration.
|
|
132
|
+
|
|
70
133
|
Environment variables work for any install and take
|
|
71
134
|
lower precedence than plugin options: `OCADVISOR_MODEL` (accepts
|
|
72
135
|
`provider/model#variant`), `OCADVISOR_PROVIDER`, `OCADVISOR_VARIANT`,
|
|
73
|
-
`OCADVISOR_TIMEOUT_MS`, `OCADVISOR_MAX_TRANSCRIPT_CHARS
|
|
136
|
+
`OCADVISOR_TIMEOUT_MS`, `OCADVISOR_MAX_TRANSCRIPT_CHARS`,
|
|
137
|
+
`OCADVISOR_AGENT_EFFORT` (`true`, `false`, or a comma-separated allow-list),
|
|
138
|
+
`OCADVISOR_DISABLED_FOR_MODELS` (comma-separated exact `provider/model` IDs),
|
|
139
|
+
`OCADVISOR_BENCHMARKS_PATH`, and `OCADVISOR_BENCHMARK_MAPPINGS_PATH`.
|
|
74
140
|
|
|
75
141
|
The "already the advisor model" skip is still keyed to Fable
|
|
76
142
|
(`anthropic/claude-fable-*`); if you point the advisor at a different model,
|
|
77
|
-
that self-consultation guard no longer matches it.
|
|
143
|
+
that self-consultation guard no longer matches it. Fable sessions stay
|
|
144
|
+
blocked even when they also appear in `disabledForModels`.
|
|
78
145
|
|
|
79
146
|
## Usage policy (what agents are told)
|
|
80
147
|
|
|
81
|
-
Use `advisor`
|
|
82
|
-
|
|
148
|
+
Use `advisor` when an independent perspective could improve the approach, help
|
|
149
|
+
resolve a problem, or strengthen an implementation review. On substantial
|
|
150
|
+
work, consider consulting before committing to an approach, when progress
|
|
151
|
+
stalls, or before completing meaningful changes. Additional consultations are
|
|
152
|
+
welcome as the work evolves — particularly when new evidence appears, the
|
|
153
|
+
approach changes, or another concern needs review. Straightforward tasks
|
|
154
|
+
usually need no consultation.
|
|
83
155
|
|
|
84
|
-
|
|
85
|
-
second opinion has the most value — pick one stage, not all three:
|
|
156
|
+
Typical checkpoints:
|
|
86
157
|
|
|
87
158
|
| Situation | Mode / trigger |
|
|
88
159
|
|---|---|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
160
|
+
| Deciding an approach with real tradeoffs | `plan` / `before_approach` |
|
|
161
|
+
| Stalled progress or contradictory evidence | `debug` / `stuck` |
|
|
162
|
+
| Reviewing meaningful changes before declaring done | `review` / `pre_complete` |
|
|
92
163
|
|
|
93
164
|
Rules enforced by the tool description and an injected session instruction:
|
|
94
165
|
|
|
95
166
|
- Always pass a concrete `question` naming the decision or artifact.
|
|
96
|
-
-
|
|
97
|
-
unresolved issue, or an explicit user request (`followup` trigger to
|
|
167
|
+
- Avoid repeating settled questions without new context (`followup` trigger to
|
|
98
168
|
reconcile conflicts with primary-source evidence).
|
|
99
169
|
- Give the advice serious weight; a passing self-test alone is not
|
|
100
170
|
counter-evidence. Clear factual corrections do not need another
|
|
101
171
|
confirmation call.
|
|
102
172
|
- The tool is hidden in `anthropic/claude-fable-*` sessions (the current
|
|
103
|
-
model is already Fable)
|
|
173
|
+
model is already Fable) and in sessions whose caller model is listed in
|
|
174
|
+
`disabledForModels`. Direct calls there return a disabled notice and do
|
|
175
|
+
not run TypeSafe screening or advisor generation. Eligibility is
|
|
176
|
+
reevaluated per request, so switching models mid-session takes effect
|
|
177
|
+
immediately; a child session uses its own model, not the parent's.
|
|
178
|
+
- When the `agentEffort` plugin option is enabled, an optional `effort`
|
|
179
|
+
argument selects the reasoning effort for that consultation.
|
|
180
|
+
- When TypeSafe screening is active, a clearly unnecessary consultation
|
|
181
|
+
returns a skip notice instead of advice, and an omitted effort may be
|
|
182
|
+
chosen automatically. When model-capability evidence is available, it is
|
|
183
|
+
weighed as context, not as a rule: a strong requester can still benefit
|
|
184
|
+
from independent review and a weak requester still needs no advice for
|
|
185
|
+
trivial work.
|
|
186
|
+
|
|
187
|
+
## TypeSafe screening (optional)
|
|
188
|
+
|
|
189
|
+
When `TYPESAFE_API_KEY` is available to the OpenCode server process, the
|
|
190
|
+
plugin can screen each consultation before paying for advisor generation.
|
|
191
|
+
One [TypeSafe](https://docs.typesafe.ai) System One request asks whether an
|
|
192
|
+
independent advisor would materially help at this point and — when the caller
|
|
193
|
+
did not pin an effort — which allowed effort fits. Only a clearly low need
|
|
194
|
+
probability skips generation; uncertain judgments preserve the consultation,
|
|
195
|
+
and any gate failure (timeout, transport, malformed answer) falls back to the
|
|
196
|
+
ordinary advisor call.
|
|
197
|
+
|
|
198
|
+
Screening is enabled automatically when the key is present. Configure it with
|
|
199
|
+
the `typesafe` plugin option:
|
|
200
|
+
|
|
201
|
+
```jsonc
|
|
202
|
+
{
|
|
203
|
+
"plugins": [
|
|
204
|
+
{
|
|
205
|
+
"package": "@pfoundation/ocadvisor",
|
|
206
|
+
"options": {
|
|
207
|
+
"typesafe": {
|
|
208
|
+
"model": "jev-1.13.0",
|
|
209
|
+
"timeoutMs": 3000,
|
|
210
|
+
"skipBelow": 0.2,
|
|
211
|
+
"minEffortConfidence": 0.6,
|
|
212
|
+
"maxStateBytes": 16384,
|
|
213
|
+
"efforts": ["high", "xhigh", "max"]
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
]
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
| Option | Default | Meaning |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `typesafe` | `true` when the key is set | `false` disables the gate completely; an object enables it with overrides |
|
|
224
|
+
| `model` | SDK default (`TYPESAFE_DEFAULT_MODEL`, else `jev-latest`) | Gate model; pin a version for reproducible decisions |
|
|
225
|
+
| `timeoutMs` | `3000` | Total gate budget, no retries |
|
|
226
|
+
| `skipBelow` | `0.20` | Skip only below this need probability |
|
|
227
|
+
| `minEffortConfidence` | `0.60` | Below this, keep the configured effort |
|
|
228
|
+
| `maxStateBytes` | `16384` | Byte budget for the compact decision state |
|
|
229
|
+
| `efforts` | `["high", "xhigh", "max"]` | Allowed automatic effort levels, intersected with the model's live variants |
|
|
230
|
+
|
|
231
|
+
Behavior notes:
|
|
232
|
+
|
|
233
|
+
- The gate runs only when an agent invokes `advisor`; ordinary model turns
|
|
234
|
+
never trigger TypeSafe calls.
|
|
235
|
+
- A missing or blank key, or `typesafe: false`, means no client is created and
|
|
236
|
+
no request is sent.
|
|
237
|
+
- Effort precedence when generation proceeds: an explicit tool `effort`, then
|
|
238
|
+
a confident gate selection, then the configured `variant`.
|
|
239
|
+
- A skip returns a visible `advisor consultation skipped (typesafe)` notice
|
|
240
|
+
and does not create, switch, or generate on the advisor session. On a
|
|
241
|
+
proceed or fallback the tool output gains one short line, for example
|
|
242
|
+
`gate: need=0.31, effort=xhigh (gate), decision=proceed` or
|
|
243
|
+
`gate: decision=fallback, timeout`; bypassed screening adds nothing.
|
|
244
|
+
- Gate metrics (decision, need probability, selected effort, latency, tokens)
|
|
245
|
+
are recorded alongside the consultation in `ocAdvisor-metrics.jsonl`.
|
|
246
|
+
|
|
247
|
+
## Model-capability benchmarks (Artificial Analysis)
|
|
248
|
+
|
|
249
|
+
The gate judges how much an independent advisor could help *this* requesting
|
|
250
|
+
model. To do that it needs two things: the actual model that made the call —
|
|
251
|
+
including its reasoning effort — and measured capability data for both that
|
|
252
|
+
model and the advisor. The plugin reads a local snapshot of
|
|
253
|
+
[Artificial Analysis](https://artificialanalysis.ai/) results; ordinary
|
|
254
|
+
consultations never touch the network for benchmark data.
|
|
255
|
+
|
|
256
|
+
What the gate receives, in the same single TypeSafe request:
|
|
257
|
+
|
|
258
|
+
- `models.requester` — `provider`, `model`, effort variant, and where the
|
|
259
|
+
identity came from (`invocation_message`, `latest_message`,
|
|
260
|
+
`session_fallback`, or `unknown`). The requester is the model behind the
|
|
261
|
+
outgoing tool call, not the parent session or whatever the session row
|
|
262
|
+
points at after a later switch.
|
|
263
|
+
- `models.advisor` — `provider`, `model`, and the effort policy in force:
|
|
264
|
+
a pinned caller effort, gate-selectable candidates with their fallback, or
|
|
265
|
+
a fixed default.
|
|
266
|
+
- `benchmarks` — the snapshot source and age, one match per profile, and
|
|
267
|
+
comparisons for the shared metrics: Artificial Analysis Coding Index,
|
|
268
|
+
Intelligence Index, and up to two reasoning/math results (HLE, GPQA, Math
|
|
269
|
+
Index) when present. Code computes oriented differences
|
|
270
|
+
(`advisor_minus_requester`); the model never does arithmetic. Scores that
|
|
271
|
+
cannot be strictly compared carry an explicit `reason` (`effort_mismatch`,
|
|
272
|
+
`effort_unknown`, `missing_requester`, …) and no delta.
|
|
273
|
+
|
|
274
|
+
Policy rule baked into the gate instructions: scores estimate comparative
|
|
275
|
+
capability, not certainty about the task. A strong requester can still benefit
|
|
276
|
+
from independent review or a fresh perspective when stuck, and a weaker
|
|
277
|
+
requester still needs no advice for trivial work. Unknown, mismatched, or
|
|
278
|
+
stale data is uncertainty — never evidence against consultation. No
|
|
279
|
+
automatic skip is ever derived from scores alone.
|
|
280
|
+
|
|
281
|
+
### Refreshing data between releases
|
|
282
|
+
|
|
283
|
+
The data lives in a local file the plugin reads at consultation time:
|
|
284
|
+
|
|
285
|
+
```text
|
|
286
|
+
$XDG_DATA_HOME/opencode/ocadvisor/artificial-analysis.json # or ~/.local/share/...
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
The `ocadvisor` CLI refreshes it independently of plugin releases:
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
# Installed (npm bin):
|
|
293
|
+
ARTIFICIAL_ANALYSIS_API_KEY=... ocadvisor benchmarks update
|
|
294
|
+
|
|
295
|
+
# One-shot without a global install:
|
|
296
|
+
ARTIFICIAL_ANALYSIS_API_KEY=... bunx --package @pfoundation/ocadvisor ocadvisor benchmarks update
|
|
297
|
+
|
|
298
|
+
# Custom locations (absolute paths):
|
|
299
|
+
ocadvisor benchmarks update --path /srv/ocadvisor/artificial-analysis.json
|
|
300
|
+
ocadvisor benchmarks status --path /srv/ocadvisor/artificial-analysis.json --model openai/gpt-6-astra#xhigh
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
- `benchmarks update` fetches the official models endpoint once, validates it,
|
|
304
|
+
and atomically replaces the snapshot. The old file is preserved byte-for-byte
|
|
305
|
+
if fetch, validation, or write fails; concurrent updaters coordinate through
|
|
306
|
+
a per-target lock. It prints the path, fetch time, model count, metric
|
|
307
|
+
coverage, and content hash.
|
|
308
|
+
- `benchmarks status` is fully offline. It reports the active source
|
|
309
|
+
(user file, bundled baseline, or unavailable), schema version, age, model
|
|
310
|
+
count, metric coverage, mapping counts, and — with `--model` — the exact
|
|
311
|
+
match or the reason a model is unresolved. Status describes what is on
|
|
312
|
+
disk; a running plugin keeps serving its last good in-memory copy until a
|
|
313
|
+
valid replacement is observed.
|
|
314
|
+
- Exit codes: `0` success; `1` update failed, no usable snapshot, or model
|
|
315
|
+
unresolved; `2` usage error.
|
|
316
|
+
- The plugin picks up a refreshed snapshot on the next consultation without a
|
|
317
|
+
restart. A consultation already in flight keeps the snapshot it started
|
|
318
|
+
with, so both models are always resolved against one consistent dataset.
|
|
319
|
+
|
|
320
|
+
The API key is read from the environment, trimmed, sent only as the
|
|
321
|
+
`x-api-key` header, and never stored or logged. The free Artificial Analysis
|
|
322
|
+
API is rate-limited, so refresh manually (for example between releases)
|
|
323
|
+
rather than in a loop. Benchmark data is provided by Artificial Analysis
|
|
324
|
+
(https://artificialanalysis.ai/).
|
|
325
|
+
|
|
326
|
+
A validated snapshot from the package release date ships inside the package
|
|
327
|
+
as the bundled baseline, so a fresh install has real coverage before the
|
|
328
|
+
first refresh. Regenerate the shipped data with
|
|
329
|
+
`ARTIFICIAL_ANALYSIS_API_KEY=... bun scripts/buildBenchmarkData.ts
|
|
330
|
+
--refresh-snapshot` (mappings only without the flag; the docs recommend
|
|
331
|
+
refreshing the user snapshot instead of rerunning this).
|
|
332
|
+
|
|
333
|
+
### Model mappings
|
|
334
|
+
|
|
335
|
+
Published model IDs do not always line up with serving providers (gateways
|
|
336
|
+
add prefixes) or with the effort used in an evaluation. The plugin resolves
|
|
337
|
+
matches only through explicit bindings to stable Artificial Analysis IDs —
|
|
338
|
+
never fuzzy names, prefix stripping, or sibling substitution:
|
|
339
|
+
|
|
340
|
+
- `src/data/artificialAnalysis.mappings.json` ships baseline bindings
|
|
341
|
+
(curated in `scripts/buildBenchmarkData.ts`, every entry pointing at a
|
|
342
|
+
stable Artificial Analysis ID with its published evaluated effort).
|
|
343
|
+
- `model-mappings.json` beside the snapshot holds user overrides; these
|
|
344
|
+
survive refreshes and plugin upgrades.
|
|
345
|
+
- Binding keys are exact `(providerID, modelID, variant)` tuples. A null
|
|
346
|
+
variant covers unset variants only — it is never a wildcard. Variants are
|
|
347
|
+
independent: a `high` binding does not match an `xhigh` call.
|
|
348
|
+
- Local bindings override bundled ones per tuple and can cover a newly
|
|
349
|
+
released model with no plugin release:
|
|
350
|
+
|
|
351
|
+
```jsonc
|
|
352
|
+
{
|
|
353
|
+
"schemaVersion": 1,
|
|
354
|
+
"bindings": [
|
|
355
|
+
{
|
|
356
|
+
"providerID": "openai",
|
|
357
|
+
"modelID": "gpt-6-astra",
|
|
358
|
+
"variant": "xhigh",
|
|
359
|
+
"aaModelID": "the-stable-artificial-analysis-uuid",
|
|
360
|
+
"evaluatedEffort": "max",
|
|
361
|
+
"evidenceURL": "https://artificialanalysis.ai/models/gpt-6-astra"
|
|
362
|
+
}
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
Set `evaluatedEffort` to the effort the evaluation actually ran at when the
|
|
368
|
+
source documents it, or `null` when it is unreported. Bindings with a null
|
|
369
|
+
evaluated effort still show scores but are labeled `effort_unknown`, and
|
|
370
|
+
their deltas are withheld from strict comparison. Use `benchmarks status
|
|
371
|
+
--model` to check exactly how a model resolves.
|
|
372
|
+
|
|
373
|
+
Unknown or unmapped models are not an error: consultations proceed with no
|
|
374
|
+
benchmark evidence for them, and the gate treats the gap as uncertainty.
|
|
104
375
|
|
|
105
376
|
## How it works
|
|
106
377
|
|
|
107
378
|
- `src/index.ts` → `dist/index.js` is the published entrypoint (default
|
|
108
|
-
export)
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
379
|
+
export); `index.js` at the repository root forwards to it so OpenCode's
|
|
380
|
+
local-directory loader can resolve a configured checkout.
|
|
381
|
+
`src/ocAdvisor.ts` holds the plugin implementation: it registers the
|
|
382
|
+
`advisor` tool, injects a short selective-use instruction into eligible
|
|
383
|
+
sessions via the `context` hook, and builds the transcript from the OpenCode
|
|
384
|
+
SQLite database. `src/typesafeGate.ts` and `src/typesafeState.ts` hold the
|
|
385
|
+
optional TypeSafe preflight. `src/benchmarkTypes.ts`, `src/artificialAnalysis.ts`,
|
|
386
|
+
`src/benchmarkUpdate.ts`, `src/benchmarkStore.ts`, `src/benchmarkMatch.ts`,
|
|
387
|
+
and `src/benchmarkEvidence.ts` hold the local Artificial Analysis snapshot,
|
|
388
|
+
its refresh transaction, the hot-reloading reader, and exact model mapping;
|
|
389
|
+
`src/cli.ts` exposes the maintenance commands.
|
|
112
390
|
- The tool is registered as a direct tool (`options.codemode: false`).
|
|
113
391
|
OpenCode 2 otherwise exposes plugin tools only through the `execute` Code
|
|
114
392
|
Mode tool, whose tool log records each nested call's input but hides the
|
|
@@ -116,20 +394,29 @@ Rules enforced by the tool description and an injected session instruction:
|
|
|
116
394
|
TUI. As a direct tool, the TUI's tool log shows the call's `mode`,
|
|
117
395
|
`trigger`, and `question` fields followed by `output:` with the answer.
|
|
118
396
|
Direct calls also avoid Code Mode's output-size truncation. The `context`
|
|
119
|
-
hook can only hide the tool (Fable sessions
|
|
120
|
-
|
|
121
|
-
the request.
|
|
397
|
+
hook can only hide the tool (Fable sessions and `disabledForModels`
|
|
398
|
+
callers), never add one, and the selective-use instruction is injected
|
|
399
|
+
only when the tool is available to the request.
|
|
122
400
|
- Before each consultation it checks OpenCode for support of the configured
|
|
123
|
-
advisor model: the provider is enabled (`
|
|
124
|
-
|
|
125
|
-
|
|
401
|
+
advisor model: the provider is enabled (`provider.get`), the model is
|
|
402
|
+
available (`model.list`, configured variant when listed), and a connection
|
|
403
|
+
exists (`integration.connection.active`). The older `catalog.*` discovery
|
|
404
|
+
namespace remains as a compatibility fallback.
|
|
405
|
+
- Benchmark snapshots and mapping overrides are read through one cached
|
|
406
|
+
store per process. Before each gate-enabled consultation the store
|
|
407
|
+
re-stats both files and reloads only when their identity changed, so CLI
|
|
408
|
+
refreshes land on the next call without a restart; invalid replacements
|
|
409
|
+
keep the last good data and unmatched scenarios report explicit coverage
|
|
410
|
+
instead of guessing.
|
|
126
411
|
- Consultations run as transient generations on a dedicated, reusable
|
|
127
412
|
`advisor` session pinned to the configured model (default
|
|
128
|
-
`anthropic/claude-fable-5-1#
|
|
413
|
+
`anthropic/claude-fable-5-1#xhigh`) via `session.create` +
|
|
129
414
|
`session.switchModel` once, then `session.generate` per call. Transient
|
|
130
415
|
generations do not mutate session history, so the advisor session stays
|
|
131
416
|
empty while its stats attribute advisor spend. Title discovery also
|
|
132
|
-
accepts the pre-rename `ocAdvisor` session title.
|
|
417
|
+
accepts the pre-rename `ocAdvisor` session title. When the agent requests
|
|
418
|
+
a different effort, the session is re-pinned to that variant first
|
|
419
|
+
(a no-op when it already matches).
|
|
133
420
|
- The generation timeout wraps only the model call, not the time a call
|
|
134
421
|
spends queued behind another consultation. Oversized transcripts are
|
|
135
422
|
capped to the configured `maxTranscriptChars` (keeping the recent tail)
|
|
@@ -145,25 +432,55 @@ Rules enforced by the tool description and an injected session instruction:
|
|
|
145
432
|
what is new since then.
|
|
146
433
|
- Real failures (provider/model/connection issues, missing
|
|
147
434
|
transcript/session) throw so OpenCode records them as errors instead of
|
|
148
|
-
silent `completed` results. Fable skips still return
|
|
435
|
+
silent `completed` results. Fable and configured-model skips still return
|
|
436
|
+
a disabled notice without TypeSafe or advisor requests.
|
|
149
437
|
|
|
150
438
|
## Metrics
|
|
151
439
|
|
|
152
440
|
Every invocation appends one JSON line to
|
|
153
441
|
`~/.local/share/opencode/ocAdvisor-metrics.jsonl` with timestamp, session,
|
|
154
|
-
caller model/agent, mode, trigger, outcome (`advisor_response`,
|
|
155
|
-
`skipped_fable`, `
|
|
156
|
-
|
|
442
|
+
caller model/agent, mode, trigger, effective effort, outcome (`advisor_response`,
|
|
443
|
+
`skipped_fable`, `skipped_model`, `skipped_typesafe`, `error`, `no_transcript`,
|
|
444
|
+
`no_session`), error type
|
|
445
|
+
(`provider_unavailable`, `model_unavailable`, `invalid_effort`, `auth`, …), latency,
|
|
157
446
|
transcript size, prior-consultation count, and transport (`via`).
|
|
158
447
|
Token usage is `null`: OpenCode generation returns text only.
|
|
159
448
|
Logging is best-effort and never breaks a call.
|
|
449
|
+
When the tool is hidden for the request, there is no invocation and no
|
|
450
|
+
metrics row. A direct call that still reaches the executor for an excluded
|
|
451
|
+
caller records one `skipped_model` line and no TypeSafe gate field.
|
|
452
|
+
|
|
453
|
+
Gate-enabled calls also carry a compact `benchmarks` object: snapshot source
|
|
454
|
+
(`user`/`seed`/`unavailable`), content hash, fetch time, hash-verified flag,
|
|
455
|
+
requester identity plus match status, advisor effort policy, the default
|
|
456
|
+
match, and — once generation runs — the final effort and its match. Rows
|
|
457
|
+
from before this feature simply lack the field; the report treats it as
|
|
458
|
+
optional and summarizes benchmark coverage when present.
|
|
160
459
|
|
|
161
460
|
## Activation
|
|
162
461
|
|
|
163
462
|
The server loads plugin files once per process, so after installing or
|
|
164
|
-
updating the plugin
|
|
165
|
-
|
|
166
|
-
|
|
463
|
+
updating the plugin, or after changing plugin options such as
|
|
464
|
+
`disabledForModels`, restart the background service
|
|
465
|
+
(`opencode service restart`) or the old code and config keep running.
|
|
466
|
+
Location eviction does not reload plugin files.
|
|
467
|
+
|
|
468
|
+
## Local development install
|
|
469
|
+
|
|
470
|
+
A configured local checkout (the global `plugins` entry pointing at the
|
|
471
|
+
repository directory) is resolved through the root `index.js` shim, which
|
|
472
|
+
forwards the compiled `dist/index.js`. After changing `src/`, run
|
|
473
|
+
`bun run build` and restart the service:
|
|
474
|
+
|
|
475
|
+
```sh
|
|
476
|
+
opencode plugin list
|
|
477
|
+
opencode api get '/api/plugin?location%5Bdirectory%5D=%2Fhome%2Fubuntu%2Fdev%2FocAdvisor'
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
The first command lists CLI-managed package plugins; the second reports the
|
|
481
|
+
server plugins for an explicit location, where `oc-advisor` should appear
|
|
482
|
+
active. The config-directory watcher only tracks the `plugin/` and `plugins/`
|
|
483
|
+
directories, so changes to this checkout need an explicit service restart.
|
|
167
484
|
|
|
168
485
|
## Development
|
|
169
486
|
|
|
@@ -171,21 +488,81 @@ eviction does not reload plugin files.
|
|
|
171
488
|
bun install # install dependencies (frozen lockfile in CI)
|
|
172
489
|
bun test # unit tests
|
|
173
490
|
bun run typecheck # typecheck (tsc --noEmit)
|
|
174
|
-
bun run build # compile dist/ (runs
|
|
491
|
+
bun run build # compile dist/ + copy benchmark data (runs on npm pack/publish)
|
|
175
492
|
bun run report # advisor usage over the last 30 days
|
|
176
493
|
bun src/usageReport.ts --days 7
|
|
494
|
+
bun src/cli.ts benchmarks status # the shipped CLI, from source
|
|
177
495
|
```
|
|
178
496
|
|
|
179
497
|
## Evaluation
|
|
180
498
|
|
|
181
499
|
The report combines the metrics log with the session database and shows
|
|
182
|
-
|
|
183
|
-
(sessions with ≥10 non-Fable tool calls vs. sessions that
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
500
|
+
generated advice, skips, gate decisions, and caller results plus eligibility
|
|
501
|
+
coverage (sessions with ≥10 non-Fable tool calls vs. sessions that received
|
|
502
|
+
generated advice). That coverage figure is a non-Fable activity proxy, not a
|
|
503
|
+
historical reconstruction of per-location `disabledForModels` settings.
|
|
504
|
+
Database and metrics views are independent and must not be
|
|
505
|
+
summed: a caller entry can fail while the plugin still recorded a generated
|
|
506
|
+
response, and a completed entry without visible output is reported as
|
|
507
|
+
unknown rather than success. Re-run it after a few weeks of the new
|
|
508
|
+
checkpoints to judge coverage and whether advice is changing outcomes. The
|
|
509
|
+
usage report is a maintainer tool run from a source checkout; it is not
|
|
510
|
+
shipped in the npm package.
|
|
511
|
+
|
|
512
|
+
## Gate evaluation
|
|
513
|
+
|
|
514
|
+
`bun src/typesafeGate.eval.ts [--model jev-1.13.0]` runs labeled cases from
|
|
515
|
+
`src/fixtures/typesafeGate.cases.json` against the gate and prints per-case
|
|
516
|
+
decisions plus false-skip, unnecessary-proceed, fallback, latency, token, and
|
|
517
|
+
cost figures. Cases may carry synthetic benchmark profiles (fake model IDs
|
|
518
|
+
and scores) that exercise the full evidence wire format without touching real
|
|
519
|
+
snapshot files; the fixture file says so explicitly. It requires
|
|
520
|
+
`TYPESAFE_API_KEY` in the process environment and calls TypeSafe only — never
|
|
521
|
+
the advisor model. Neither the script nor its fixtures are published.
|
|
522
|
+
|
|
523
|
+
Initial run (2026-09-19, `jev-1.13.0`, six cases, 4,240 input tokens,
|
|
524
|
+
≈$0.00018): no false skips on must-consult cases, no fallbacks, latency
|
|
525
|
+
p50/p95 266/626 ms.
|
|
526
|
+
|
|
527
|
+
Benchmark-aware run (same model and day, ten cases including four synthetic
|
|
528
|
+
benchmark profiles — stronger requester, weaker requester, unknown scores,
|
|
529
|
+
stale snapshot — 11,164 input tokens, ≈$0.00047): **0 false skips** on
|
|
530
|
+
must-consult cases, including the stronger-requester review and the
|
|
531
|
+
unknown-scores debug; the stale-snapshot review proceeded as well. Two
|
|
532
|
+
unnecessary proceeds on routine questions landed at need 0.23–0.33, above the
|
|
533
|
+
conservative default `skipBelow` (0.20): uncertainty preserves consultation
|
|
534
|
+
by design. No fallbacks; latency p50/p95 280/607 ms. Adjust the threshold
|
|
535
|
+
only from observed cases and re-run the evaluation after changing it.
|
|
536
|
+
|
|
537
|
+
### End-to-end benchmark data flow
|
|
538
|
+
|
|
539
|
+
1. Fresh install: the plugin reads the bundled snapshot shipped with the
|
|
540
|
+
package, so real coverage exists before any refresh. User files, when
|
|
541
|
+
present, take precedence over it.
|
|
542
|
+
2. An operator with an Artificial Analysis key runs
|
|
543
|
+
`ocadvisor benchmarks update`. The validated snapshot lands in the data
|
|
544
|
+
directory and the next consultation logs its content hash.
|
|
545
|
+
3. `ocadvisor benchmarks status --model ...` confirms which models now
|
|
546
|
+
resolve, and local `model-mappings.json` entries cover anything missing.
|
|
547
|
+
4. A later plugin upgrade leaves the user snapshot and mappings untouched —
|
|
548
|
+
they live outside the package — so coverage persists across releases.
|
|
187
549
|
|
|
188
550
|
## Versioning
|
|
189
551
|
|
|
190
|
-
Releases use calendar versioning (`YY.M.patch`, e.g. `26.9.0`). The
|
|
191
|
-
(`v26.9.0`)
|
|
552
|
+
Releases use calendar versioning (`YY.M.patch`, e.g. `26.9.0`). The GitHub
|
|
553
|
+
Release tag (`v26.9.0`) is the source of truth: the publish workflow overwrites
|
|
554
|
+
`package.json` `version` from the tag. Do not bump `version` in `package.json`
|
|
555
|
+
for a release; leave it at the last published version.
|
|
556
|
+
|
|
557
|
+
## Releasing (maintainers)
|
|
558
|
+
|
|
559
|
+
1. Land the change on `master` and wait for CI to pass. Leave `package.json`
|
|
560
|
+
`version` as the last published release.
|
|
561
|
+
2. Cut a GitHub Release on tag `vYY.M.patch`. Pre-releases (the release's
|
|
562
|
+
pre-release flag, or a `-suffix` version) publish under dist-tag `next`;
|
|
563
|
+
stable releases publish `--tag latest`. Pushing a tag alone publishes
|
|
564
|
+
nothing.
|
|
565
|
+
3. The `publish` workflow sets the package version from the tag, re-runs
|
|
566
|
+
every gate, and publishes via OIDC trusted publishing (no npm token).
|
|
567
|
+
`v*` tag creation is restricted to maintainers by a ruleset.
|
|
568
|
+
4. Verify: `npm view @pfoundation/ocadvisor@YY.M.patch`.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { type BenchmarkSnapshot, type BenchmarkValidationError } from "./benchmarkTypes.js";
|
|
2
|
+
export declare const AA_LLM_MODELS_ENDPOINT = "https://artificialanalysis.ai/api/v2/data/llms/models";
|
|
3
|
+
export declare function normalizeAAResponse(body: unknown, fetchedAt: string): BenchmarkSnapshot | BenchmarkValidationError;
|
|
4
|
+
//# sourceMappingURL=artificialAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artificialAnalysis.d.ts","sourceRoot":"","sources":["../src/artificialAnalysis.ts"],"names":[],"mappings":"AAIA,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,qBAAqB,CAAC;AAE7B,eAAO,MAAM,sBAAsB,0DACsB,CAAC;AAM1D,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,OAAO,EACb,SAAS,EAAE,MAAM,GAChB,iBAAiB,GAAG,wBAAwB,CA0E9C"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
// Artificial Analysis response normalization: maps the documented LLM
|
|
2
|
+
// models payload onto the versioned benchmark snapshot. The response
|
|
3
|
+
// carries no structured effort, evaluation date, or methodology version,
|
|
4
|
+
// so those fields stay null rather than guessed.
|
|
5
|
+
import { BENCHMARK_SCHEMA_VERSION, BENCHMARK_SOURCE, BENCHMARK_SOURCE_URL, RECOGNIZED_METRICS, computeSnapshotHash, parseBenchmarkModel, } from "./benchmarkTypes.js";
|
|
6
|
+
export const AA_LLM_MODELS_ENDPOINT = "https://artificialanalysis.ai/api/v2/data/llms/models";
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
export function normalizeAAResponse(body, fetchedAt) {
|
|
11
|
+
if (!isRecord(body)) {
|
|
12
|
+
return { error: "artificial analysis response must be an object" };
|
|
13
|
+
}
|
|
14
|
+
if (typeof fetchedAt !== "string" ||
|
|
15
|
+
!Number.isFinite(Date.parse(fetchedAt))) {
|
|
16
|
+
return {
|
|
17
|
+
error: "artificial analysis refresh needs a valid fetchedAt timestamp",
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (!Array.isArray(body.data) || body.data.length === 0) {
|
|
21
|
+
return { error: "artificial analysis response has an empty data array" };
|
|
22
|
+
}
|
|
23
|
+
const seen = new Set();
|
|
24
|
+
const models = [];
|
|
25
|
+
for (let index = 0; index < body.data.length; index++) {
|
|
26
|
+
const entry = body.data[index];
|
|
27
|
+
const label = `entry ${index}`;
|
|
28
|
+
if (!isRecord(entry))
|
|
29
|
+
return { error: `${label} must be an object` };
|
|
30
|
+
const creator = isRecord(entry.model_creator) ? entry.model_creator : null;
|
|
31
|
+
const creatorID = creator && typeof creator.id === "string" ? creator.id.trim() : "";
|
|
32
|
+
if (!creatorID) {
|
|
33
|
+
return { error: `${label} is missing a model creator id` };
|
|
34
|
+
}
|
|
35
|
+
const parsed = parseBenchmarkModel({
|
|
36
|
+
id: entry.id,
|
|
37
|
+
creatorID,
|
|
38
|
+
name: entry.name,
|
|
39
|
+
slug: entry.slug,
|
|
40
|
+
evaluatedEffort: null,
|
|
41
|
+
evaluatedAt: null,
|
|
42
|
+
evaluations: entry.evaluations ?? {},
|
|
43
|
+
}, label);
|
|
44
|
+
if ("error" in parsed)
|
|
45
|
+
return parsed;
|
|
46
|
+
if (seen.has(parsed.id)) {
|
|
47
|
+
return { error: `duplicate benchmark model id ${parsed.id}` };
|
|
48
|
+
}
|
|
49
|
+
seen.add(parsed.id);
|
|
50
|
+
models.push(parsed);
|
|
51
|
+
}
|
|
52
|
+
const usable = models.some((model) => RECOGNIZED_METRICS.some((metric) => typeof model.evaluations[metric.key] === "number"));
|
|
53
|
+
if (!usable) {
|
|
54
|
+
return {
|
|
55
|
+
error: "artificial analysis response has no usable benchmark scores",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const snapshot = {
|
|
59
|
+
schemaVersion: BENCHMARK_SCHEMA_VERSION,
|
|
60
|
+
source: BENCHMARK_SOURCE,
|
|
61
|
+
sourceURL: BENCHMARK_SOURCE_URL,
|
|
62
|
+
endpoint: AA_LLM_MODELS_ENDPOINT,
|
|
63
|
+
fetchedAt,
|
|
64
|
+
contentHash: "",
|
|
65
|
+
methodologyVersion: null,
|
|
66
|
+
metricDefinitions: [...RECOGNIZED_METRICS],
|
|
67
|
+
models,
|
|
68
|
+
};
|
|
69
|
+
snapshot.contentHash = computeSnapshotHash({
|
|
70
|
+
source: snapshot.source,
|
|
71
|
+
endpoint: snapshot.endpoint,
|
|
72
|
+
methodologyVersion: snapshot.methodologyVersion,
|
|
73
|
+
models: snapshot.models,
|
|
74
|
+
});
|
|
75
|
+
return snapshot;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=artificialAnalysis.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"artificialAnalysis.js","sourceRoot":"","sources":["../src/artificialAnalysis.ts"],"names":[],"mappings":"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"}
|