scoutline 0.2.0 → 0.6.1

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 (110) hide show
  1. package/README.md +240 -18
  2. package/dist/capabilities/diagnostics.d.ts +70 -32
  3. package/dist/capabilities/diagnostics.d.ts.map +1 -1
  4. package/dist/capabilities/diagnostics.js +97 -46
  5. package/dist/capabilities/diagnostics.js.map +1 -1
  6. package/dist/capabilities/reader.d.ts +227 -0
  7. package/dist/capabilities/reader.d.ts.map +1 -0
  8. package/dist/capabilities/reader.js +100 -0
  9. package/dist/capabilities/reader.js.map +1 -0
  10. package/dist/capabilities/repository.d.ts +221 -0
  11. package/dist/capabilities/repository.d.ts.map +1 -0
  12. package/dist/capabilities/repository.js +172 -0
  13. package/dist/capabilities/repository.js.map +1 -0
  14. package/dist/commands/cache.d.ts +106 -0
  15. package/dist/commands/cache.d.ts.map +1 -0
  16. package/dist/commands/cache.js +203 -0
  17. package/dist/commands/cache.js.map +1 -0
  18. package/dist/commands/doctor.d.ts +17 -6
  19. package/dist/commands/doctor.d.ts.map +1 -1
  20. package/dist/commands/doctor.js +42 -17
  21. package/dist/commands/doctor.js.map +1 -1
  22. package/dist/commands/read.d.ts +74 -14
  23. package/dist/commands/read.d.ts.map +1 -1
  24. package/dist/commands/read.js +257 -117
  25. package/dist/commands/read.js.map +1 -1
  26. package/dist/commands/repo.d.ts +53 -7
  27. package/dist/commands/repo.d.ts.map +1 -1
  28. package/dist/commands/repo.js +104 -123
  29. package/dist/commands/repo.js.map +1 -1
  30. package/dist/commands/repository-explorer.d.ts +147 -0
  31. package/dist/commands/repository-explorer.d.ts.map +1 -0
  32. package/dist/commands/repository-explorer.js +550 -0
  33. package/dist/commands/repository-explorer.js.map +1 -0
  34. package/dist/index.d.ts +20 -0
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +209 -34
  37. package/dist/index.js.map +1 -1
  38. package/dist/lib/cache.d.ts +123 -18
  39. package/dist/lib/cache.d.ts.map +1 -1
  40. package/dist/lib/cache.js +324 -49
  41. package/dist/lib/cache.js.map +1 -1
  42. package/dist/lib/errors.d.ts +24 -1
  43. package/dist/lib/errors.d.ts.map +1 -1
  44. package/dist/lib/errors.js +33 -2
  45. package/dist/lib/errors.js.map +1 -1
  46. package/dist/lib/execution.d.ts +119 -5
  47. package/dist/lib/execution.d.ts.map +1 -1
  48. package/dist/lib/execution.js +216 -10
  49. package/dist/lib/execution.js.map +1 -1
  50. package/dist/lib/index.d.ts +1 -1
  51. package/dist/lib/index.d.ts.map +1 -1
  52. package/dist/lib/index.js +1 -1
  53. package/dist/lib/index.js.map +1 -1
  54. package/dist/lib/mcp-client.d.ts +29 -5
  55. package/dist/lib/mcp-client.d.ts.map +1 -1
  56. package/dist/lib/mcp-client.js +97 -97
  57. package/dist/lib/mcp-client.js.map +1 -1
  58. package/dist/lib/tool-cache.d.ts +86 -0
  59. package/dist/lib/tool-cache.d.ts.map +1 -0
  60. package/dist/lib/tool-cache.js +123 -0
  61. package/dist/lib/tool-cache.js.map +1 -0
  62. package/dist/providers/minimax/adapter.d.ts +6 -4
  63. package/dist/providers/minimax/adapter.d.ts.map +1 -1
  64. package/dist/providers/minimax/adapter.js +64 -57
  65. package/dist/providers/minimax/adapter.js.map +1 -1
  66. package/dist/providers/minimax/coding-plan-client.d.ts +60 -0
  67. package/dist/providers/minimax/coding-plan-client.d.ts.map +1 -0
  68. package/dist/providers/minimax/coding-plan-client.js +204 -0
  69. package/dist/providers/minimax/coding-plan-client.js.map +1 -0
  70. package/dist/providers/minimax/media.d.ts +60 -6
  71. package/dist/providers/minimax/media.d.ts.map +1 -1
  72. package/dist/providers/minimax/media.js +147 -7
  73. package/dist/providers/minimax/media.js.map +1 -1
  74. package/dist/providers/minimax/quota-client.d.ts +13 -6
  75. package/dist/providers/minimax/quota-client.d.ts.map +1 -1
  76. package/dist/providers/minimax/quota-client.js +5 -0
  77. package/dist/providers/minimax/quota-client.js.map +1 -1
  78. package/dist/providers/minimax/vision-attestations.d.ts +23 -0
  79. package/dist/providers/minimax/vision-attestations.d.ts.map +1 -1
  80. package/dist/providers/minimax/vision-attestations.js +35 -10
  81. package/dist/providers/minimax/vision-attestations.js.map +1 -1
  82. package/dist/providers/minimax/vision-conformance.d.ts +8 -6
  83. package/dist/providers/minimax/vision-conformance.d.ts.map +1 -1
  84. package/dist/providers/minimax/vision-conformance.js +8 -6
  85. package/dist/providers/minimax/vision-conformance.js.map +1 -1
  86. package/dist/providers/minimax/vision-revisions.d.ts +8 -1
  87. package/dist/providers/minimax/vision-revisions.d.ts.map +1 -1
  88. package/dist/providers/minimax/vision-revisions.js +13 -6
  89. package/dist/providers/minimax/vision-revisions.js.map +1 -1
  90. package/dist/providers/selection.d.ts +3 -3
  91. package/dist/providers/selection.js +3 -3
  92. package/dist/providers/types.d.ts +66 -32
  93. package/dist/providers/types.d.ts.map +1 -1
  94. package/dist/providers/types.js.map +1 -1
  95. package/dist/providers/zai/adapter.d.ts.map +1 -1
  96. package/dist/providers/zai/adapter.js +71 -5
  97. package/dist/providers/zai/adapter.js.map +1 -1
  98. package/dist/providers/zai/encoded-error.d.ts +90 -0
  99. package/dist/providers/zai/encoded-error.d.ts.map +1 -0
  100. package/dist/providers/zai/encoded-error.js +169 -0
  101. package/dist/providers/zai/encoded-error.js.map +1 -0
  102. package/dist/providers/zai/reader.d.ts +82 -0
  103. package/dist/providers/zai/reader.d.ts.map +1 -0
  104. package/dist/providers/zai/reader.js +490 -0
  105. package/dist/providers/zai/reader.js.map +1 -0
  106. package/dist/providers/zai/repository.d.ts +76 -0
  107. package/dist/providers/zai/repository.d.ts.map +1 -0
  108. package/dist/providers/zai/repository.js +715 -0
  109. package/dist/providers/zai/repository.js.map +1 -0
  110. package/package.json +3 -3
package/README.md CHANGED
@@ -73,7 +73,7 @@ npx scoutline --help
73
73
 
74
74
  ## Provider Selection
75
75
 
76
- Shared commands (`search`, `vision`, `quota`, `doctor`) accept a global
76
+ Shared commands (`search`, `vision`, `quota`, `doctor`, `repo`) accept a global
77
77
  `--provider <zai|minimax>` flag. Resolution precedence:
78
78
 
79
79
  1. Explicit `--provider <zai|minimax>` on the command line
@@ -99,9 +99,15 @@ empty credentials leave the Provider unconfigured and shared capabilities will
99
99
  fail with `AUTH_ERROR` or `CONFIGURATION_ERROR`. Unknown Provider IDs fail
100
100
  fast with `VALIDATION_ERROR`.
101
101
 
102
- `scoutline read`, `scoutline repo`, `scoutline tools`, `scoutline tool`,
103
- `scoutline call`, and `scoutline code` accept the flag but ignore it; they
104
- remain Z.AI-only in the base release.
102
+ `scoutline search`, `scoutline vision`, `scoutline quota`, `scoutline doctor`,
103
+ **`scoutline repo`**, and **`scoutline read`** participate in Provider
104
+ selection. `scoutline tools`, `scoutline tool`, `scoutline call`, and
105
+ `scoutline code` accept the flag but ignore it; they remain Z.AI-only.
106
+ MiniMax does not currently advertise the `repository-exploration` or `reader`
107
+ Capabilities — selecting MiniMax (explicitly or via `SCOUTLINE_PROVIDER`) for
108
+ any `repo` subcommand or for `read` returns `UNSUPPORTED_CAPABILITY` before
109
+ descriptor configuration, Adapter creation, cache identity, or transport
110
+ construction, with no Z.AI fallback.
105
111
 
106
112
  ## Capability Matrix
107
113
 
@@ -114,14 +120,14 @@ remain Z.AI-only in the base release.
114
120
  | `vision.diagnose-error` | Yes | Available | Live-attested; conformance-gated |
115
121
  | `vision.diagram` | Yes | Pending | Implemented, pending live conformance |
116
122
  | `vision.chart` | Yes | Pending | Implemented, pending live conformance |
117
- | `vision.diff` (image diff) | Yes | No | Base-release Z.AI-only |
118
- | `vision.video` | Yes | No | Base-release Z.AI-only |
123
+ | `vision.diff` (image diff) | Yes | No | Z.AI-only (never MiniMax-claimable) |
124
+ | `vision.video` | Yes | No | Z.AI-only (never MiniMax-claimable) |
119
125
  | `quota` | Yes | Yes | Normalized `QuotaDashboard` (ADR-0001) |
120
126
  | `diagnostics` (`doctor`) | Yes | Yes | Lists both Providers; probes configured |
121
- | `read` (Reader) | Yes | No | Base-release Z.AI-only |
122
- | `repo` (Repository exploration) | Yes | No | Base-release Z.AI-only |
123
- | `tools`, `tool`, `call` (Raw tools) | Yes | No | Base-release Z.AI-only |
124
- | `code` (Code Mode) | Yes | No | Base-release Z.AI-only |
127
+ | `read` (Reader) | Yes | **No** (UNSUPPORTED_CAPABILITY) | Participates in selection; only Z.AI supplies `reader` |
128
+ | `repo search` / `repo read` / `repo tree` | Yes | **No** (UNSUPPORTED_CAPABILITY) | Participates in selection; only Z.AI supplies `repository-exploration` |
129
+ | `tools`, `tool`, `call` (Raw tools) | Yes | No | Z.AI-only; accepts but ignores `--provider` |
130
+ | `code` (Code Mode) | Yes | No | Z.AI-only; accepts but ignores `--provider` |
125
131
 
126
132
  Media limits for general single-image interpretation:
127
133
 
@@ -153,9 +159,12 @@ In the current release, `ui-artifact` and `diagnose-error` have offline
153
159
  `pass`, live `pass`, and compiled attestations — they are **supported at
154
160
  runtime** through MiniMax. The remaining three operations (`extract-text`,
155
161
  `diagram`, `chart`) have offline `pass` and live `pending`; they are
156
- **unsupported at runtime** and surface `UNSUPPORTED_CAPABILITY`. The CLI
157
- falls back to Z.AI (the Z.AI Provider supports every operation in the
158
- base release).
162
+ **unsupported at runtime** through MiniMax. Selecting MiniMax explicitly
163
+ for one of these operations fails closed with `UNSUPPORTED_CAPABILITY`
164
+ before credentials, media, transport, cache, or any other Provider is
165
+ touched (FR-023, FR-024). There is **no automatic Z.AI fallback** for an
166
+ explicit MiniMax selection — call without `--provider minimax` (or unset
167
+ `SCOUTLINE_PROVIDER`) to route through Z.AI instead.
159
168
 
160
169
  No environment variable, flag, or configuration value can promote a
161
170
  mapping to supported. Support is driven exclusively by the compiled
@@ -201,6 +210,7 @@ scoutline search --help # Search options
201
210
  scoutline repo --help # GitHub repo commands
202
211
  scoutline doctor --help # Provider diagnostics
203
212
  scoutline quota --help # Plan usage
213
+ scoutline cache --help # Local cache inspection and clearing
204
214
  ```
205
215
 
206
216
  ### Examples
@@ -239,6 +249,10 @@ scoutline quota --all-providers # every configured Provider
239
249
  scoutline doctor # full diagnostics
240
250
  scoutline doctor --no-tools # metadata only, no transport
241
251
  scoutline doctor --provider minimax # MiniMax connectivity
252
+
253
+ # Cache - inspect or clear the local cache
254
+ scoutline cache stats # show inventory of both subdirectories
255
+ scoutline cache clear # delete every file under cache/ and tools/
242
256
  ```
243
257
 
244
258
  ## Output Format
@@ -273,7 +287,8 @@ Quota output is a schema-version-1 `QuotaDashboard`:
273
287
  ```
274
288
 
275
289
  Doctor output is a schema-version-1 `DiagnosticsReport` listing every built-in
276
- Provider with its configured state, declared capabilities, and probe status.
290
+ Provider with its configured state, declared capabilities, probe status, and a
291
+ one-line cache summary under the `cache` field.
277
292
 
278
293
  ## Notes
279
294
 
@@ -283,11 +298,218 @@ Provider with its configured state, declared capabilities, and probe status.
283
298
  entries are still reported.
284
299
  - `doctor` exits 1 when the effective Provider is unconfigured or any
285
300
  configured probe fails; successful entries are still reported.
286
- - `read` supports `--with-images-summary`, `--no-gfm`, and `--keep-img-data-url` for richer parsing control.
301
+ - `read` returns a schema-version-1 envelope (content read or extract read) in every output mode. `--with-images-summary`, `--no-gfm`, and `--keep-img-data-url` are passed through to the Provider request. `--max-chars` is ignored on extract reads; `--full-envelope` is silently deprecated.
287
302
  - Vision tool calls automatically retry transient 5xx/network errors (default: 2 retries). Configure with `ZAI_MCP_VISION_RETRY_COUNT` (or `ZAI_MCP_RETRY_COUNT` for all tools).
288
- - Tool discovery can be cached to speed `tools`/`tool`/`doctor` (default: on, 24h TTL). Configure with `ZAI_MCP_TOOL_CACHE`, `ZAI_MCP_TOOL_CACHE_TTL_MS`, `ZAI_MCP_CACHE_DIR`.
289
- - The response cache uses provider-partitioned keys; legacy `zai-cli` cache
290
- entries remain readable for Z.AI but are never migrated.
303
+ - Tool discovery can be cached to speed `tools`/`tool`/`doctor` (default: on, 24h TTL). The cache shares the unified root with the response cache; configure both via `SCOUTLINE_CACHE`, `SCOUTLINE_CACHE_TTL_MS`, `SCOUTLINE_CACHE_SIZE_MB`, and `SCOUTLINE_CACHE_DIR` (legacy aliases `ZAI_MCP_TOOL_CACHE*`, `ZAI_MCP_CACHE_DIR`, and `ZAI_CACHE*` are accepted silently).
304
+ - The local cache lives at `~/.scoutline/` (`cache/` for responses, `tools/` for tool discovery) on every platform. Inspect or clear it with `scoutline cache stats` and `scoutline cache clear`.
305
+
306
+ ## Repository Exploration (P6)
307
+
308
+ `scoutline repo search`, `scoutline repo read`, and `scoutline repo tree`
309
+ participate in `--provider` selection. Z.AI advertises the
310
+ `repository-exploration` Capability and supplies it through the Z.AI
311
+ Repository Adapter. MiniMax does not advertise it; selecting MiniMax returns
312
+ `UNSUPPORTED_CAPABILITY` before descriptor configuration, Adapter creation,
313
+ credential resolution for use, cache identity, or transport construction
314
+ with no fallback to Z.AI.
315
+
316
+ ### Breaking data-mode migration (v0.2 → v1)
317
+
318
+ The three `repo` successes return **schema-version-1 structured values** in
319
+ every output mode. This is an intentional breaking change from the v0.2 raw
320
+ Search/File strings and the depth-dependent raw Tree shape:
321
+
322
+ | Command | v0.2 (legacy, now obsolete) | v1 (current) |
323
+ | --- | --- | --- |
324
+ | `repo search` | Raw ZRead text with `<excerpt>` blocks | `{schemaVersion, repository, query, language, excerpts:[{text}], truncated, originalTextLength}` |
325
+ | `repo read` | Raw `<file_content>…</file_content>` body | `{schemaVersion, repository, path, content, truncated, originalContentLength}` |
326
+ | `repo tree` | `<structure>` block (depth 1 returned split/deep routes) | `{schemaVersion, repository, path, depth, snapshots:[{repository, path, entries:[{name, path, kind}]}]}` (structured at every depth, including depth 1) |
327
+
328
+ `data` mode emits the exact object above. `json` and `pretty` wrap it through
329
+ the standard success envelope. Text-oriented modes (`compact`, `markdown`,
330
+ `refs`, `tty`) fall back to the JSON value because the command supplies no
331
+ prose presentation override.
332
+
333
+ **Scripting impact:** any consumer parsing v0.2 raw ZRead text or the v0.2
334
+ split/deep `tree` shape must switch to the v1 structured fields. The raw
335
+ `scoutline.zai.*` namespace remains available for callers that need the
336
+ legacy grammar; it is not wrapped in the v1 envelope.
337
+
338
+ ### Canonical repository paths
339
+
340
+ - Tree aliases omitted, empty, `/`, or `.` normalize to the root path `""`.
341
+ - File paths must be non-root; the root is invalid for `repo read`.
342
+ - Leading `./` and leading `/` are accepted on File; leading and trailing `/`
343
+ are stripped and repeated `/` collapses on both.
344
+ - Actual `.`/`..` segments, backslashes, and ASCII control characters are
345
+ rejected. Percent escapes (`%XX`) are never decoded — they remain literal.
346
+
347
+ ### `--max-chars` (deterministic, local)
348
+
349
+ `--max-chars` is **never** a summarization model call. It is presentation
350
+ projection applied to the normalized result after caching.
351
+
352
+ - Absent, zero, or negative → no truncation.
353
+ - `repo read` → truncates `content` with the existing ellipsis rule; preserves
354
+ the original length in `originalContentLength` and sets `truncated: true`.
355
+ - `repo search` → applies **one total budget** across `excerpts[].text` in
356
+ Provider order; the final retained excerpt is truncated and later excerpts
357
+ are omitted.
358
+ - `repo tree` → never character-limited.
359
+ - Metadata, JSON envelopes, and Tree snapshots are not part of the budget.
360
+
361
+ ### Empty results
362
+
363
+ A future Provider Adapter may explicitly return an empty `excerpts`/`entries`
364
+ array when its own contract distinguishes a valid empty state. The Z.AI
365
+ Adapter requires at least one well-formed `<excerpt>` block per Search;
366
+ unwrapped text is malformed and surfaces as a normalized `API_ERROR 502`, not
367
+ as an empty success. An empty ZRead structure without `entries` is malformed
368
+ the same way.
369
+
370
+ ### Cache continuity
371
+
372
+ Repository results use a new key shape
373
+ `v2.repository-exploration-<op>.<provider>.<credential-hash>.<request-hash>.json`.
374
+ The credential hash is supplied by the Adapter (full lowercase SHA-256 hex
375
+ digest of the active credential) and is never re-hashed by cache code.
376
+
377
+ Legacy v0.2 Z.AI cache entries remain readable **read-only**: their key is
378
+ reconstructed from the same Adapter-resolved credential using the exact v0.2
379
+ algorithm, and a valid hit is written through to the new key. Old files are
380
+ never rewritten, migrated, or deleted. `--no-cache` performs no reads or
381
+ writes. Injected credentials drive the fingerprint and legacy-key
382
+ construction; ambient `process.env` is never reread.
383
+
384
+ ### Errors and lifecycle
385
+
386
+ Encoded MCP error envelopes are recognized before success parsing:
387
+ `QUOTA_ERROR` 429 (exhausted ZRead quota, code `1310`) is terminal; transient
388
+ 429/5xx and a malformed envelope retry once; auth 401/403 and other 4xx are
389
+ terminal. Raw Provider response bodies, reset metadata, and error texts are
390
+ discarded.
391
+
392
+ Transport close is best-effort and called once per constructed attempt. A
393
+ successful operation does not become a failure when close rejects or times
394
+ out, and a primary failure remains the outward failure when close also fails.
395
+ Cache hits construct and close no transport.
396
+
397
+ ### Diagnostics inventory
398
+
399
+ `sharedCapabilities` and `zaiOnlyCapabilities` are derived from descriptor
400
+ metadata (intersection across built-in Providers; Z.AI minus the union of
401
+ the others). `repository-exploration` is therefore `zaiOnlyCapabilities`
402
+ while still participating in Provider selection. Doctor help names MiniMax
403
+ as unsupported for `repo`.
404
+
405
+ ### Non-goals
406
+
407
+ This release does not add MiniMax repository exploration, a Reader
408
+ migration, automatic summarization, dynamic Provider loading, or an implicit
409
+ Z.AI fallback for unsupported Providers. The P5 specialized Vision mappings
410
+ remain independent and are not claimed complete here.
411
+
412
+ ## Reader (P7)
413
+
414
+ `scoutline read` participates in `--provider` selection. Z.AI advertises the
415
+ `reader` Capability and supplies it through the Z.AI Reader Adapter. MiniMax
416
+ does not advertise it; selecting MiniMax returns `UNSUPPORTED_CAPABILITY`
417
+ before descriptor configuration, Adapter creation, credential resolution for
418
+ use, cache identity, or transport construction, with no fallback to Z.AI.
419
+
420
+ ### Breaking data-mode migration (v0.2 → v1)
421
+
422
+ `scoutline read` returns **schema-version-1 structured values** in every
423
+ output mode. This is an intentional breaking change from the v0.2 raw content
424
+ string and the bare extract array:
425
+
426
+ | Read shape | v0.2 (legacy, now obsolete) | v1 (current) |
427
+ | --- | --- | --- |
428
+ | Content read (default) | Raw content string | `{schemaVersion, url, finalUrl, title, content, contentFormat, truncated, originalContentLength}` |
429
+ | Extract read (`--extract <mode>`) | Bare JSON array of items | `{schemaVersion, url, finalUrl, mode, items, truncated, originalItemCount}` |
430
+
431
+ `url` is exactly what the caller passed; `finalUrl` is the URL the operation
432
+ actually fetched, which differs only when a Provider-side rewrite occurred
433
+ (e.g. `gist.github.com/<id>` → `gist.github.com/<id>/raw`). The v0.2 stderr
434
+ rewrite notice is gone — the signal now lives in `finalUrl`.
435
+
436
+ The four `--extract` modes (`code`, `links`, `tables`, `headings`) and the
437
+ shape of each item are unchanged from v0.2. Only the outer envelope changed
438
+ (bare array → schema-versioned object with `items`).
439
+
440
+ #### Output-mode behavior
441
+
442
+ | Mode | Content read | Extract read |
443
+ | --- | --- | --- |
444
+ | `data` | The content-read envelope object | The extract-read envelope object |
445
+ | `json` | `{success: true, data: <envelope>, timestamp}` (indent 0) | same |
446
+ | `pretty` | `{success: true, data: <envelope>, timestamp}` (indent 2) | same |
447
+ | `compact` | The `content` string directly | JSON fallback (the envelope object) |
448
+ | `markdown` | The `content` string directly | JSON fallback |
449
+ | `refs` | The `content` string directly | JSON fallback |
450
+ | `tty` | The `content` string directly | JSON fallback |
451
+
452
+ `compact`, `markdown`, `refs`, and `tty` exist to give operators a prose form
453
+ for human reading. A content read supplies one (the page body). An extract
454
+ read does not — extracted items are data, not prose — so those modes fall
455
+ back to JSON. This is the deliberate asymmetry from `repo`, whose results are
456
+ always structured data.
457
+
458
+ **Scripting impact:** any consumer that did `scoutline read URL > file.md`,
459
+ `scoutline read URL | jq -r .content`, or `scoutline read URL --extract code |
460
+ jq -c .[]` against v0.2 output must switch to the v1 envelope. `--max-chars`
461
+ still truncates the content-read `content`; it is **ignored on extract reads**
462
+ (extract reports `originalItemCount` instead). The deprecated
463
+ `--full-envelope` flag is silently accepted and ignored — the envelope is
464
+ always returned at v1.
465
+
466
+ ### URL rewrite as `finalUrl`
467
+
468
+ A Provider-side URL rewrite (today: gist URLs to their raw form) is recorded
469
+ as `finalUrl` in the v1 result. The rewrite is idempotent on URLs already
470
+ ending in `/raw` and preserves fragments. The v0.2 stderr notice is removed.
471
+
472
+ ### Cache namespace
473
+
474
+ Reader results share the partitioned cache namespace and use the
475
+ `reader-fetch` operation suffix:
476
+
477
+ ```text
478
+ v2.reader-reader-fetch.<provider>.<credential-hash>.<request-hash>.json
479
+ ```
480
+
481
+ The Adapter resolves its credential once. The canonical request URL is the
482
+ **rewritten** URL so two requests that normalize to the same fetched URL share
483
+ one cache entry. Legacy v0.2 Z.AI entries are reconstructed from the same
484
+ Adapter-resolved credential using the exact v0.2 args-order algorithm and
485
+ remain read-only; a valid hit is written through to the new key; legacy files
486
+ are never migrated, rewritten, or deleted. `--no-cache` performs no reads or
487
+ writes. Injected credentials drive the fingerprint and legacy-key
488
+ construction; ambient `process.env` is never reread.
489
+
490
+ ### Errors and lifecycle
491
+
492
+ Encoded MCP error envelopes are recognized before success parsing. The same
493
+ taxonomy that governs `repo` applies: exhausted WebReader quota (code `1310`
494
+ or explicit exhausted-limit meaning) surfaces as a normalized `QUOTA_ERROR`
495
+ 429 and is terminal; transient 429/5xx and a malformed envelope retry once;
496
+ auth 401/403 and other 4xx are terminal. Raw Provider body, reset metadata,
497
+ and error-text strings are discarded. Transport close is best-effort and
498
+ called once per constructed attempt; a close failure never masks a primary
499
+ result. Cache hits construct and close no transport.
500
+
501
+ ### Diagnostics inventory
502
+
503
+ `sharedCapabilities` and `zaiOnlyCapabilities` are derived from descriptor
504
+ metadata. `reader` is therefore `zaiOnlyCapabilities` while still
505
+ participating in Provider selection, and Doctor help names MiniMax as
506
+ unsupported for `read`.
507
+
508
+ ### Non-goals
509
+
510
+ This release does not add a MiniMax Reader Adapter, automatic summarization,
511
+ removal of the deprecated `--full-envelope` flag, or a future `--max-items`
512
+ truncation policy for extract reads.
291
513
 
292
514
  ## Contributing
293
515
 
@@ -1,34 +1,40 @@
1
1
  /**
2
- * Diagnostics Capability Contract (DESIGN.md §14, P4-04).
2
+ * Diagnostics Capability Contract (DESIGN.md §14, P4-04, P6-06).
3
3
  *
4
4
  * Defines the schema-version-1 diagnostics report every `doctor`
5
5
  * invocation returns, plus the capability contract each Provider
6
6
  * Adapter implements so its connectivity can be probed without a
7
7
  * generative request.
8
8
  *
9
- * The report is built by the doctor command from static descriptor
10
- * metadata plus the success/failure of each configured Provider probe.
11
- * Each Adapter performs exactly ONE connectivity attempt; shared
12
- * execution owns the retry policy.
9
+ * The report is built by the doctor command from descriptor-derived
10
+ * inventory plus the success/failure of each configured Provider
11
+ * probe. Each Adapter performs exactly ONE connectivity attempt;
12
+ * shared execution owns the retry policy.
13
13
  *
14
14
  * Boundary rules (ARCHITECTURE.md §2):
15
- * - Imports Provider identity types, shared errors, the Vision
16
- * operation→capability mapping, and the MiniMax specialized Vision
17
- * conformance registry metadata (a pure snapshot, no transport).
18
- * - Imports no Provider transport, no Provider Adapter, no command
19
- * presentation.
15
+ * - Imports Provider identity and metadata types (`ProviderCapability`,
16
+ * `ProviderDescriptor`, `ProviderId`) and shared errors. P6-06 keeps
17
+ * the inventory descriptor-derived; no concrete Adapter, no
18
+ * Provider transport, no production registry import lives here.
19
+ * - Imports no Provider transport, no Provider Adapter, no Vision
20
+ * operation→capability mapping, no MiniMax specialized-vision
21
+ * conformance registry, no command presentation. The previous
22
+ * hand-maintained inventory required those imports; the
23
+ * descriptor-derived inventory does not.
20
24
  *
21
- * Normalization rules (DESIGN.md §14):
22
- * - Z.AI connectivity uses tool discovery (list tools from UTCP).
23
- * - MiniMax connectivity uses a raw single-attempt quota probe
24
- * (Adapter-local quota client), NOT `QuotaCapability.invoke()`,
25
- * because it authenticates without a generative request.
26
- * - For the base release `sharedCapabilities` is Search, general
27
- * single-image interpretation, quota, and diagnostics.
28
- * - `zaiOnlyCapabilities` is Reader, repository exploration, raw
29
- * provider tools, Code Mode, image diff, and video analysis.
25
+ * Inventory derivation (P6-06):
26
+ * - `sharedCapabilities` is the intersection across every descriptor
27
+ * passed to `buildDiagnosticsReport`, preserving deterministic
28
+ * canonical order from the FIRST descriptor.
29
+ * - `zaiOnlyCapabilities` is the Z.AI descriptor's capabilities
30
+ * minus the union of every OTHER built-in descriptor's
31
+ * capabilities, preserving Z.AI descriptor order. Values are
32
+ * descriptor capability IDs only no hand-maintained aliases.
33
+ * - `repository-exploration` is excluded from shared while any
34
+ * built-in lacks it, and included in Z.AI-only the moment Z.AI
35
+ * advertises it and another built-in does not.
30
36
  */
31
- import type { ProviderCapability, ProviderId } from "../providers/types.js";
37
+ import type { ProviderCapability, ProviderDescriptor, ProviderId } from "../providers/types.js";
32
38
  import { type ScoutlineErrorCode } from "../lib/errors.js";
33
39
  export interface DiagnosticOptions {
34
40
  readonly probe: boolean;
@@ -55,6 +61,19 @@ export interface DiagnosticsReport {
55
61
  readonly visionMcpCompatible: boolean;
56
62
  };
57
63
  readonly providers: readonly ProviderDiagnostic[];
64
+ /**
65
+ * One-line cache summary embedded by the CLI handler
66
+ * (`Cache: enabled, 47 response entries (12.3 MB), 1 tool entry
67
+ * (8.2 KB), ~/.scoutline/`). Optional: present when the dispatcher
68
+ * passes a pre-formatted `cacheSummary` through
69
+ * `DoctorDiagnosticsDependencies`. The Doctor report builder NEVER
70
+ * formats this itself (L1 fix); it only embeds what the caller
71
+ * supplied. Older callers that omit the dependency produce a report
72
+ * without this field.
73
+ */
74
+ readonly cache?: {
75
+ readonly summary: string;
76
+ };
58
77
  }
59
78
  /**
60
79
  * Capability each Provider Adapter implements for connectivity
@@ -68,21 +87,40 @@ export interface DiagnosticsCapability {
68
87
  invoke(options: DiagnosticOptions): Promise<void>;
69
88
  }
70
89
  /**
71
- * Capabilities supported by every Provider in the base release (DESIGN.md
72
- * §14, §15). The four base capabilities are static; specialized Vision
73
- * operations move into shared metadata only when their compiled
74
- * conformance registry entry is supported. At P5-02 every specialized
75
- * entry is pending, so this list reduces to the base four. P5-03's
76
- * attested mappings automatically extend this list through the single
77
- * registry query there is no parallel support list to maintain.
90
+ * Derive the shared Capabilities inventory: the intersection of every
91
+ * descriptor's `capabilities()` set, preserving deterministic
92
+ * canonical order from the FIRST descriptor in the list.
93
+ *
94
+ * Edge cases:
95
+ * - Empty descriptor list: returns an empty array.
96
+ * - Single descriptor: returns that descriptor's capabilities in
97
+ * their declared order (the intersection of one set is itself).
98
+ * - Two or more: keeps a capability from the first descriptor iff
99
+ * every other descriptor also advertises it.
100
+ *
101
+ * The returned array is frozen so callers cannot mutate the cached
102
+ * derivation in place.
78
103
  */
79
- export declare const SHARED_CAPABILITIES: readonly ProviderCapability[];
104
+ export declare function deriveSharedCapabilities(descriptors: readonly ProviderDescriptor[]): readonly ProviderCapability[];
80
105
  /**
81
- * Capabilities that remain Z.AI-only in the base release (DESIGN.md §14),
82
- * in the documented order: Reader, repository exploration, raw provider
83
- * tools, Code Mode, image diff, and video analysis.
106
+ * Derive the Z.AI-only Capabilities inventory: capabilities advertised
107
+ * by the Z.AI descriptor minus the union of capabilities advertised by
108
+ * every OTHER descriptor in the list. Preserves Z.AI descriptor order.
109
+ *
110
+ * Edge cases:
111
+ * - Empty descriptor list: returns an empty array.
112
+ * - Z.AI absent from the list: returns an empty array.
113
+ * - Z.AI present as the only descriptor: returns its capabilities
114
+ * verbatim (the "minus nothing" case).
115
+ * - Z.AI with other descriptors: each capability in Z.AI descriptor
116
+ * order is kept iff no other descriptor advertises it.
117
+ *
118
+ * The returned values are descriptor capability IDs only. No
119
+ * hand-maintained aliases, no parallel base-release list, no
120
+ * invented names. `repository-exploration` lands here naturally the
121
+ * moment Z.AI advertises it and another built-in does not.
84
122
  */
85
- export declare const ZAI_ONLY_CAPABILITIES: readonly string[];
123
+ export declare function deriveZaiOnlyCapabilities(descriptors: readonly ProviderDescriptor[]): readonly ProviderCapability[];
86
124
  /**
87
125
  * Map a thrown error into a normalized diagnostic error entry. The
88
126
  * caller is responsible for recursive redaction before the entry
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/capabilities/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAQ3E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,kBAAkB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,UAAU,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC3D,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;KACvC,CAAC;IACF,QAAQ,CAAC,SAAS,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAMD;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,EAAE,SAAS,kBAAkB,EAA8B,CAAC;AAU5F;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAOlD,CAAC;AAMF;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG;IACxD,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAYA"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/capabilities/diagnostics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAChG,OAAO,EAAkB,KAAK,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAM3E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,UAAU,CAAC;IAC9B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,SAAS,kBAAkB,EAAE,CAAC;IACrD,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,OAAO,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,MAAM,CAAC,EAAE,gBAAgB,GAAG,gBAAgB,CAAC;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,IAAI,EAAE,kBAAkB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/E;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1B,QAAQ,CAAC,iBAAiB,EAAE,UAAU,CAAC;IACvC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAC3D,QAAQ,CAAC,mBAAmB,EAAE,SAAS,MAAM,EAAE,CAAC;IAChD,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,mBAAmB,EAAE,OAAO,CAAC;KACvC,CAAC;IACF,QAAQ,CAAC,SAAS,EAAE,SAAS,kBAAkB,EAAE,CAAC;IAClD;;;;;;;;;OASG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE;QAAE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAC/C;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnD;AAYD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,SAAS,kBAAkB,EAAE,GACzC,SAAS,kBAAkB,EAAE,CAc/B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,WAAW,EAAE,SAAS,kBAAkB,EAAE,GACzC,SAAS,kBAAkB,EAAE,CAoB/B;AAMD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG;IACxD,IAAI,EAAE,kBAAkB,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAYA"}
@@ -1,69 +1,120 @@
1
1
  /**
2
- * Diagnostics Capability Contract (DESIGN.md §14, P4-04).
2
+ * Diagnostics Capability Contract (DESIGN.md §14, P4-04, P6-06).
3
3
  *
4
4
  * Defines the schema-version-1 diagnostics report every `doctor`
5
5
  * invocation returns, plus the capability contract each Provider
6
6
  * Adapter implements so its connectivity can be probed without a
7
7
  * generative request.
8
8
  *
9
- * The report is built by the doctor command from static descriptor
10
- * metadata plus the success/failure of each configured Provider probe.
11
- * Each Adapter performs exactly ONE connectivity attempt; shared
12
- * execution owns the retry policy.
9
+ * The report is built by the doctor command from descriptor-derived
10
+ * inventory plus the success/failure of each configured Provider
11
+ * probe. Each Adapter performs exactly ONE connectivity attempt;
12
+ * shared execution owns the retry policy.
13
13
  *
14
14
  * Boundary rules (ARCHITECTURE.md §2):
15
- * - Imports Provider identity types, shared errors, the Vision
16
- * operation→capability mapping, and the MiniMax specialized Vision
17
- * conformance registry metadata (a pure snapshot, no transport).
18
- * - Imports no Provider transport, no Provider Adapter, no command
19
- * presentation.
15
+ * - Imports Provider identity and metadata types (`ProviderCapability`,
16
+ * `ProviderDescriptor`, `ProviderId`) and shared errors. P6-06 keeps
17
+ * the inventory descriptor-derived; no concrete Adapter, no
18
+ * Provider transport, no production registry import lives here.
19
+ * - Imports no Provider transport, no Provider Adapter, no Vision
20
+ * operation→capability mapping, no MiniMax specialized-vision
21
+ * conformance registry, no command presentation. The previous
22
+ * hand-maintained inventory required those imports; the
23
+ * descriptor-derived inventory does not.
20
24
  *
21
- * Normalization rules (DESIGN.md §14):
22
- * - Z.AI connectivity uses tool discovery (list tools from UTCP).
23
- * - MiniMax connectivity uses a raw single-attempt quota probe
24
- * (Adapter-local quota client), NOT `QuotaCapability.invoke()`,
25
- * because it authenticates without a generative request.
26
- * - For the base release `sharedCapabilities` is Search, general
27
- * single-image interpretation, quota, and diagnostics.
28
- * - `zaiOnlyCapabilities` is Reader, repository exploration, raw
29
- * provider tools, Code Mode, image diff, and video analysis.
25
+ * Inventory derivation (P6-06):
26
+ * - `sharedCapabilities` is the intersection across every descriptor
27
+ * passed to `buildDiagnosticsReport`, preserving deterministic
28
+ * canonical order from the FIRST descriptor.
29
+ * - `zaiOnlyCapabilities` is the Z.AI descriptor's capabilities
30
+ * minus the union of every OTHER built-in descriptor's
31
+ * capabilities, preserving Z.AI descriptor order. Values are
32
+ * descriptor capability IDs only no hand-maintained aliases.
33
+ * - `repository-exploration` is excluded from shared while any
34
+ * built-in lacks it, and included in Z.AI-only the moment Z.AI
35
+ * advertises it and another built-in does not.
30
36
  */
31
37
  import { ScoutlineError } from "../lib/errors.js";
32
- import { visionOperationToCapability } from "./vision.js";
33
- import { listSupportedMiniMaxVisionOperations } from "../providers/minimax/vision-conformance.js";
34
38
  // ---------------------------------------------------------------------------
35
- // Static base-release metadata
39
+ // Descriptor-derived inventory (P6-06).
40
+ //
41
+ // Pure calculations over the exact descriptor list passed to
42
+ // `buildDiagnosticsReport`. No descriptor.create(), no transport, no
43
+ // production registry import. Empty/single/missing-ZAI lists are
44
+ // handled deterministically; the algorithms never rely on array
45
+ // indexing accidents.
36
46
  // ---------------------------------------------------------------------------
37
47
  /**
38
- * Capabilities supported by every Provider in the base release (DESIGN.md
39
- * §14, §15). The four base capabilities are static; specialized Vision
40
- * operations move into shared metadata only when their compiled
41
- * conformance registry entry is supported. At P5-02 every specialized
42
- * entry is pending, so this list reduces to the base four. P5-03's
43
- * attested mappings automatically extend this list through the single
44
- * registry query there is no parallel support list to maintain.
48
+ * Derive the shared Capabilities inventory: the intersection of every
49
+ * descriptor's `capabilities()` set, preserving deterministic
50
+ * canonical order from the FIRST descriptor in the list.
51
+ *
52
+ * Edge cases:
53
+ * - Empty descriptor list: returns an empty array.
54
+ * - Single descriptor: returns that descriptor's capabilities in
55
+ * their declared order (the intersection of one set is itself).
56
+ * - Two or more: keeps a capability from the first descriptor iff
57
+ * every other descriptor also advertises it.
58
+ *
59
+ * The returned array is frozen so callers cannot mutate the cached
60
+ * derivation in place.
45
61
  */
46
- export const SHARED_CAPABILITIES = buildSharedCapabilities();
47
- function buildSharedCapabilities() {
48
- const caps = ["search", "vision.interpret-image", "quota", "diagnostics"];
49
- for (const op of listSupportedMiniMaxVisionOperations()) {
50
- caps.push(visionOperationToCapability(op));
62
+ export function deriveSharedCapabilities(descriptors) {
63
+ if (descriptors.length === 0)
64
+ return Object.freeze([]);
65
+ const [first, ...rest] = descriptors;
66
+ if (rest.length === 0) {
67
+ return Object.freeze([...first.capabilities()]);
68
+ }
69
+ const otherSets = rest.map((d) => d.capabilities());
70
+ const out = [];
71
+ for (const cap of first.capabilities()) {
72
+ if (otherSets.every((set) => set.has(cap))) {
73
+ out.push(cap);
74
+ }
51
75
  }
52
- return Object.freeze(caps);
76
+ return Object.freeze(out);
53
77
  }
54
78
  /**
55
- * Capabilities that remain Z.AI-only in the base release (DESIGN.md §14),
56
- * in the documented order: Reader, repository exploration, raw provider
57
- * tools, Code Mode, image diff, and video analysis.
79
+ * Derive the Z.AI-only Capabilities inventory: capabilities advertised
80
+ * by the Z.AI descriptor minus the union of capabilities advertised by
81
+ * every OTHER descriptor in the list. Preserves Z.AI descriptor order.
82
+ *
83
+ * Edge cases:
84
+ * - Empty descriptor list: returns an empty array.
85
+ * - Z.AI absent from the list: returns an empty array.
86
+ * - Z.AI present as the only descriptor: returns its capabilities
87
+ * verbatim (the "minus nothing" case).
88
+ * - Z.AI with other descriptors: each capability in Z.AI descriptor
89
+ * order is kept iff no other descriptor advertises it.
90
+ *
91
+ * The returned values are descriptor capability IDs only. No
92
+ * hand-maintained aliases, no parallel base-release list, no
93
+ * invented names. `repository-exploration` lands here naturally the
94
+ * moment Z.AI advertises it and another built-in does not.
58
95
  */
59
- export const ZAI_ONLY_CAPABILITIES = [
60
- "reader",
61
- "repository-exploration",
62
- "raw-provider-tools",
63
- "code-mode",
64
- "image-diff",
65
- "video-analysis",
66
- ];
96
+ export function deriveZaiOnlyCapabilities(descriptors) {
97
+ const zai = descriptors.find((d) => d.id === "zai");
98
+ if (!zai)
99
+ return Object.freeze([]);
100
+ const others = descriptors.filter((d) => d.id !== "zai");
101
+ if (others.length === 0) {
102
+ return Object.freeze([...zai.capabilities()]);
103
+ }
104
+ const union = new Set();
105
+ for (const descriptor of others) {
106
+ for (const cap of descriptor.capabilities()) {
107
+ union.add(cap);
108
+ }
109
+ }
110
+ const out = [];
111
+ for (const cap of zai.capabilities()) {
112
+ if (!union.has(cap)) {
113
+ out.push(cap);
114
+ }
115
+ }
116
+ return Object.freeze(out);
117
+ }
67
118
  // ---------------------------------------------------------------------------
68
119
  // Failure normalization
69
120
  // ---------------------------------------------------------------------------