wicked-crew-api-types 0.20.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +844 -4
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -255,6 +255,33 @@ export interface AgentSession {
|
|
|
255
255
|
* `GET /campaigns`.
|
|
256
256
|
*/
|
|
257
257
|
group_label?: string;
|
|
258
|
+
/**
|
|
259
|
+
* When this run was LAUNCHED — unix SECONDS (home command-center run metrics; api-types 0.24.0).
|
|
260
|
+
* Daemon-derived at DTO assembly on BOTH `GET /runs` and `GET /runs/:id` from the `run.launched`
|
|
261
|
+
* audit entry's timestamp (the daemon's durable system-of-record for run provenance — task #88),
|
|
262
|
+
* joined exactly like {@link retry_of} / {@link guidance} and hydrated from the SAME trail scan
|
|
263
|
+
* at boot, so it survives a daemon restart.
|
|
264
|
+
*
|
|
265
|
+
* This is the ONE launch instant the daemon records: "created" and "started" are the SAME event
|
|
266
|
+
* here — the engine's `AgentSession` (wicked-core `domain.rs`) carries no launch time, so there
|
|
267
|
+
* is no separate queued-vs-started split to expose. Unlocks time-bucketed KPI deltas and a real
|
|
268
|
+
* 7/30/90-day range on the home dashboard.
|
|
269
|
+
*
|
|
270
|
+
* ABSENT — never `null`, never fabricated — when the daemon has no `run.launched` entry for the
|
|
271
|
+
* run: a run launched OFF the `POST /runs` path (a repo's onboarding run, a campaign's own
|
|
272
|
+
* DAG-node runs), a run predating this field, or a `--db`-isolated daemon reading a trail that
|
|
273
|
+
* never saw the launch. Read with `typeof === 'number'`; a bucketed KPI or a 7/30/90-day range
|
|
274
|
+
* must EXCLUDE an undated run, never date it with a fabricated now.
|
|
275
|
+
*
|
|
276
|
+
* COST (deferred, not faked): a per-run cost/spend figure is deliberately NOT joined onto this
|
|
277
|
+
* DTO. Cost exists only on the per-unit {@link CliUsageEvent} (`costUsd`), streamed over `/ws`
|
|
278
|
+
* and never accumulated per run daemon-side — a reliable per-run total would require replaying
|
|
279
|
+
* the durable event log per run on this hot path (the unbounded cost the GET /runs delivery-cache
|
|
280
|
+
* architecture forbids) or a new live cliUsage accumulator plus a durable per-run store. A burn
|
|
281
|
+
* chart should sum `cliUsage.costUsd` from the live `/ws` stream (or `GET /runs/:id/events`) for
|
|
282
|
+
* now; a joined `AgentSession.cost` is a follow-up once that store exists.
|
|
283
|
+
*/
|
|
284
|
+
created_at?: number;
|
|
258
285
|
}
|
|
259
286
|
|
|
260
287
|
/** An ordered unit of work within a run (`WorkUnit`). */
|
|
@@ -448,6 +475,16 @@ export interface GateDecision {
|
|
|
448
475
|
amend?: string;
|
|
449
476
|
}
|
|
450
477
|
|
|
478
|
+
/**
|
|
479
|
+
* Body of `POST /runs/:id/reassign` — the manual operator lever that reassigns a wedged run's
|
|
480
|
+
* cursor unit through the same engine path the stall-watchdog's automatic escalation uses
|
|
481
|
+
* (crew#442). `cli` omitted lets the engine's council re-pick the seat; when present it must be
|
|
482
|
+
* a seat in the run's own pool (`SessionView.clis`) or the route answers 400.
|
|
483
|
+
*/
|
|
484
|
+
export interface ReassignRequest {
|
|
485
|
+
cli?: string;
|
|
486
|
+
}
|
|
487
|
+
|
|
451
488
|
/**
|
|
452
489
|
* A CoreEvent frame as delivered verbatim over `/ws` — a tagged-JSON object
|
|
453
490
|
* discriminated on `type` (wicked-core-ts `event_to_json`). The named optional
|
|
@@ -1119,12 +1156,34 @@ export interface ConformanceRule {
|
|
|
1119
1156
|
excludes?: string[];
|
|
1120
1157
|
/** Ordering within a severity band + gate priority. Engine default: 1.0. */
|
|
1121
1158
|
weight?: number;
|
|
1122
|
-
/**
|
|
1123
|
-
|
|
1159
|
+
/**
|
|
1160
|
+
* Enforcement half (from the retired policy model). Absent ⇒ the rule is RECALL-ONLY: it is
|
|
1161
|
+
* surfaced to workers but never enters decide()/select(), so it can neither block a gate nor
|
|
1162
|
+
* `catch` an eval sample — which is why a store of effect-less rules evals to "every bad
|
|
1163
|
+
* sample is a gap" (api-types 0.27.0, the #394/#395 companion). `deny` blocks (the only effect
|
|
1164
|
+
* the evals credit as a catch); `warn` surfaces without blocking (the operator-authorable
|
|
1165
|
+
* middle band); `allow_with_conditions` permits with obligations; `allow` permits outright.
|
|
1166
|
+
*/
|
|
1167
|
+
effect?: 'deny' | 'warn' | 'allow_with_conditions' | 'allow';
|
|
1124
1168
|
trigger?: { contains?: string };
|
|
1125
1169
|
obligations?: string[];
|
|
1126
1170
|
/** The frozen acceptance-criteria text (becomes a claim's `criteria` when the rule decides a gate). */
|
|
1127
1171
|
criteria?: string;
|
|
1172
|
+
/**
|
|
1173
|
+
* Unix-SECONDS timestamp of when this rule FIRST entered the store — the "added" time the Steering
|
|
1174
|
+
* surface sorts + date-filters on (NOT the latest-edit time), stamped once by the engine on first
|
|
1175
|
+
* create and preserved across updates (api-types 0.23.0).
|
|
1176
|
+
*
|
|
1177
|
+
* WIRED-BUT-PENDING A CORE-TS BUMP: the field exists on the engine's `ConformanceRule`
|
|
1178
|
+
* (wicked-core `conformance.rs`, serialized with `skip_serializing_if = Option::is_none`), but it
|
|
1179
|
+
* landed AFTER the published `core-ts-v0.7.16` this daemon builds against — so a rule served by
|
|
1180
|
+
* the current binding carries NO `created_at`. The daemon passes the field through verbatim
|
|
1181
|
+
* (`listConformanceRules` returns the parsed rows unchanged), so it begins flowing the moment
|
|
1182
|
+
* crew re-pins to a core-ts that includes it — no daemon change needed. ABSENT — never
|
|
1183
|
+
* fabricated — on any rule the engine did not stamp (pre-field rows, and every row from a
|
|
1184
|
+
* pre-bump binding), so a `since`/`until` filter cannot assert an undated rule in range.
|
|
1185
|
+
*/
|
|
1186
|
+
created_at?: number;
|
|
1128
1187
|
}
|
|
1129
1188
|
|
|
1130
1189
|
/** Facet query for `GET /governance/rules/preview`. All fields are optional. */
|
|
@@ -1160,6 +1219,21 @@ export interface RuleBrowseQuery {
|
|
|
1160
1219
|
* contradict, and picking a winner silently would answer a question the caller didn't ask).
|
|
1161
1220
|
*/
|
|
1162
1221
|
include_retired?: 'true' | 'false';
|
|
1222
|
+
/**
|
|
1223
|
+
* Inclusive LOWER date bound (api-types 0.23.0): keep only rules with `created_at >= since`.
|
|
1224
|
+
* Unix SECONDS, a non-negative integer (a present-but-non-integer value is a 400). Applied
|
|
1225
|
+
* server-side over the listed rows, alongside the facet filters. A rule with NO `created_at`
|
|
1226
|
+
* is EXCLUDED whenever `since` (or `until`) is set — see {@link ConformanceRule.created_at}: on
|
|
1227
|
+
* a pre-bump core-ts binding NO rule carries the field, so this filter narrows to empty until the
|
|
1228
|
+
* engine surfaces it. Omitted ⇒ no lower bound.
|
|
1229
|
+
*/
|
|
1230
|
+
since?: string;
|
|
1231
|
+
/**
|
|
1232
|
+
* Inclusive UPPER date bound (api-types 0.23.0): keep only rules with `created_at <= until`.
|
|
1233
|
+
* Same units/validation/undated-exclusion as {@link RuleBrowseQuery.since}. Omitted ⇒ no upper
|
|
1234
|
+
* bound.
|
|
1235
|
+
*/
|
|
1236
|
+
until?: string;
|
|
1163
1237
|
}
|
|
1164
1238
|
|
|
1165
1239
|
// ── Steering management (STEERING program) ──────────────────────────────────────
|
|
@@ -1252,6 +1326,371 @@ export interface SteeringLandingResult {
|
|
|
1252
1326
|
error?: string;
|
|
1253
1327
|
}
|
|
1254
1328
|
|
|
1329
|
+
// ── Skills — the daemon-owned garden plugin root, published as immutable snapshots (api-types 0.28.0) ──
|
|
1330
|
+
//
|
|
1331
|
+
// Skills are files (skills keystone, design v3 + amendments v3.1/v3.2). The daemon owns ONE
|
|
1332
|
+
// effective `wicked-garden`-shaped plugin root — `<state home>/skills/effective/`, the dependency
|
|
1333
|
+
// closure of the installed plugin: `.claude-plugin/{plugin.json,archetypes.json,components.json}`,
|
|
1334
|
+
// `skills/**` (nested layout verbatim), `scripts/**` minus CI + dev tools, `schemas/`,
|
|
1335
|
+
// `docs/examples/`, `pyproject.toml`, `uv.lock` — seeded from the LIVE installed plugin into a
|
|
1336
|
+
// content-addressed `baseline/<contentHash>/`. The operator edits files in place, replaces a
|
|
1337
|
+
// skill, adds one, disables one (manifest state — the files stay), resets one (content from the
|
|
1338
|
+
// baseline; never enablement). Nothing a worker runs is read from `effective/`: PUBLISHING
|
|
1339
|
+
// validates the whole tree and writes an IMMUTABLE, read-only `snapshots/<gen>/` (enabled skills
|
|
1340
|
+
// only, closure included, `snapshot.json`, and the generated delivery views —
|
|
1341
|
+
// `views/copilot/.github/skills/<name>/` holding the enabled PORTABLE skills, part of the content
|
|
1342
|
+
// hash), then flips `current -> snapshots/<gen>`; the engine receives the resolved snapshot path
|
|
1343
|
+
// as `WICKED_SKILLS_SNAPSHOT`. The daemon NEVER writes into the user's own CLI directories
|
|
1344
|
+
// (`~/.codex`, `~/.pi`, `~/.copilot`, `~/.config/opencode`, `~/.claude`): skills reach non-Claude
|
|
1345
|
+
// workers only through per-launch, wicked-owned delivery core performs from the snapshot (v3.2) —
|
|
1346
|
+
// a CLI without a lever (codex today) runs without wicked skills, and a unit on such a seat that
|
|
1347
|
+
// requires one is REFUSED at launch (no proceed-with-disclosure setting exists).
|
|
1348
|
+
// `/skills` is a file manager whose EVERY mutation is CAS-guarded (`expectedRevision`) and
|
|
1349
|
+
// answers 2xx `{verdict, findings[], revision}` — a `blocked` verdict is a normal response,
|
|
1350
|
+
// including a publish refused because one is in flight (`publish-in-flight`) or aborted because the
|
|
1351
|
+
// root changed under it (`root-changed`), neither of which wrote anything. 409 (`{error, revision}`)
|
|
1352
|
+
// is EXCLUSIVELY a stale `expectedRevision` (a CAS conflict).
|
|
1353
|
+
|
|
1354
|
+
/** What a skill IS, from its frontmatter — fork-first: `context: fork` → fork worker (a subagent
|
|
1355
|
+
* body); else `user-invocable: true` → router (an operator-facing entry point); else module (a
|
|
1356
|
+
* nested reference skill routers/workers pull in). */
|
|
1357
|
+
export type SkillKind = 'router' | 'fork-worker' | 'module';
|
|
1358
|
+
|
|
1359
|
+
/** `shipped` = every own file byte-identical to the baseline; `override` = a shipped skill with
|
|
1360
|
+
* edited/replaced files; `user-added` = no baseline (added through the API, or upstream dropped
|
|
1361
|
+
* it while the operator's edits were kept). */
|
|
1362
|
+
export type SkillProvenance = 'shipped' | 'override' | 'user-added';
|
|
1363
|
+
|
|
1364
|
+
/** Where a baseline was captured from. `claude-plugin-cache` covers the marketplace cache AND the
|
|
1365
|
+
* hand-installed `plugins/wicked-garden` copy (both are "the installed plugin"); `checkout` is a
|
|
1366
|
+
* git working tree; `directory` any other explicit plugin-shaped directory. */
|
|
1367
|
+
export type SkillSourceKind = 'claude-plugin-cache' | 'checkout' | 'directory';
|
|
1368
|
+
|
|
1369
|
+
/** The per-baseline `uv sync` state (`<baseline>/.venv`, provisioned once per content hash — the
|
|
1370
|
+
* publish that needs it AWAITS it — and shared read-only by every snapshot that links it):
|
|
1371
|
+
* `pending` until a successful publish records it, `synced` on success (the snapshot carries a
|
|
1372
|
+
* `.venv` link), `skipped` when the bundle carries no `pyproject.toml` (nothing to provision; no
|
|
1373
|
+
* link). `failed` (uv missing or a sync error) is BLOCKING for the publish (`venv-failed`) and is
|
|
1374
|
+
* therefore never the recorded state of a published baseline — the record keeps its previous
|
|
1375
|
+
* value; a later publish retries. */
|
|
1376
|
+
export type SkillVenvState = 'pending' | 'synced' | 'failed' | 'skipped';
|
|
1377
|
+
|
|
1378
|
+
/** One captured baseline — keyed in `SkillManifest.baselines` by the content hash of its bundle
|
|
1379
|
+
* (sorted relative paths + sha256 digests), never by the version string alone: two installs of
|
|
1380
|
+
* "12.32.0" with different bytes are two baselines. */
|
|
1381
|
+
export interface SkillBaselineRecord {
|
|
1382
|
+
/** `version` from the source's `.claude-plugin/plugin.json`. */
|
|
1383
|
+
plugin_version: string;
|
|
1384
|
+
source: { kind: SkillSourceKind; path: string };
|
|
1385
|
+
/** HEAD sha for a `checkout` source; `null` otherwise (or when git could not answer). */
|
|
1386
|
+
git_sha: string | null;
|
|
1387
|
+
/** ISO-8601 instant the baseline was captured. */
|
|
1388
|
+
captured_at: string;
|
|
1389
|
+
venv: SkillVenvState;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
export interface SkillEntry {
|
|
1393
|
+
/** Plugin-relative directory, nested layout preserved (`skills/engineering/frontend`). Never
|
|
1394
|
+
* renamed — sibling `../` links depend on it. */
|
|
1395
|
+
dir: string;
|
|
1396
|
+
kind: SkillKind;
|
|
1397
|
+
/** Core-by-reference: in the registered-reference closure — a `skill_ref` of a workflow the
|
|
1398
|
+
* daemon knows (core drop-ins, crew-generated, user-registered) or a skill one of those names in
|
|
1399
|
+
* its SKILL.md (repo-learn → search, mem). Disabling or renaming it is blocking. */
|
|
1400
|
+
core: boolean;
|
|
1401
|
+
/** `false` when the skill's files resolve `${CLAUDE_PLUGIN_ROOT}`, invoke a script relative to
|
|
1402
|
+
* the cwd (`python3 -u scripts/x.py`, `./scripts/x`, …), or link `../` — Claude-only by nature:
|
|
1403
|
+
* excluded from the snapshot's `views/copilot/` and from the per-launch skill lists core builds
|
|
1404
|
+
* for the other CLIs. `portable` is the admission key for every non-Claude view (design v3.2). */
|
|
1405
|
+
portable: boolean;
|
|
1406
|
+
/** Manifest state, orthogonal to content: a disabled skill's files stay in `effective/` and are
|
|
1407
|
+
* excluded from the next published snapshot. Reset never flips it. */
|
|
1408
|
+
enabled: boolean;
|
|
1409
|
+
provenance: SkillProvenance;
|
|
1410
|
+
/** ISO-8601 instant of the last edit/replace/add through the API; `null` when untouched. */
|
|
1411
|
+
editedAt: string | null;
|
|
1412
|
+
/** A refreshed baseline changed a file this skill overrides — the new side is readable as
|
|
1413
|
+
* `?side=baseline` for diffing; the operator's content is kept. */
|
|
1414
|
+
upgradeAvailable: boolean;
|
|
1415
|
+
/** `upgradeAvailable`, or the last refresh found a name collision (upstream now ships a skill
|
|
1416
|
+
* under this user-added name at another dir). Cleared by reset / a later refresh. */
|
|
1417
|
+
conflict: boolean;
|
|
1418
|
+
/** The held-back UPSTREAM skill's directory in the current baseline when the last refresh found a
|
|
1419
|
+
* name collision (upstream ships this name at another dir than the operator's skill); `null`
|
|
1420
|
+
* otherwise. `GET /skills/:name/files/*path?side=baseline` reads THIS directory for such a skill,
|
|
1421
|
+
* so the two sides of the collision are comparable — the answer's `path` names the file actually
|
|
1422
|
+
* read (api-types 0.28.0). Re-derived by every refresh. */
|
|
1423
|
+
upstreamDir: string | null;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/** One managed file (plugin-relative path → hashes). `baselineHash` `null` = user-added file;
|
|
1427
|
+
* `effectiveHash` `null` = a baseline file absent from `effective/` (removed by a direct
|
|
1428
|
+
* filesystem edit — reset restores it; the skill reads `override` until then). */
|
|
1429
|
+
export interface SkillFileRecord {
|
|
1430
|
+
baselineHash: string | null;
|
|
1431
|
+
effectiveHash: string | null;
|
|
1432
|
+
/** The hash this file had in the most recent published snapshot; `null` = never published. */
|
|
1433
|
+
lastPublishedHash: string | null;
|
|
1434
|
+
/** The last refresh saw BOTH sides change (kept the effective side). */
|
|
1435
|
+
conflict: boolean;
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
/** The most recent publish. */
|
|
1439
|
+
export interface SkillPublishedRecord {
|
|
1440
|
+
gen: number;
|
|
1441
|
+
/** Hash over the snapshot's files (sorted relative paths + sha256 digests), `snapshot.json` excluded. */
|
|
1442
|
+
contentHash: string;
|
|
1443
|
+
/** ISO-8601 instant. */
|
|
1444
|
+
at: string;
|
|
1445
|
+
/** sha256 of the exact `snapshot.json` bytes publish wrote. `snapshot.json` is excluded from the
|
|
1446
|
+
* content hash, so the crew-owned manifest AUTHENTICATES it: `current` verifies only the
|
|
1447
|
+
* generation this record names, with metadata hashing to this value (api-types 0.28.0). */
|
|
1448
|
+
snapshotHash: string;
|
|
1449
|
+
}
|
|
1450
|
+
|
|
1451
|
+
/** `<skills root>/manifest.json` — the state of the daemon-owned root. (The v3 mirror ledger is
|
|
1452
|
+
* withdrawn with the mirror — design v3.2 §1: the daemon never writes into the user's CLI dirs.) */
|
|
1453
|
+
export interface SkillManifest {
|
|
1454
|
+
version: 2;
|
|
1455
|
+
/** Monotonic; bumped by EVERY mutation. Every mutating request carries it as `expectedRevision`. */
|
|
1456
|
+
revision: number;
|
|
1457
|
+
/** Content hash of the current baseline (`baseline/<hash>/`). */
|
|
1458
|
+
baseline: string;
|
|
1459
|
+
baselines: Record<string, SkillBaselineRecord>;
|
|
1460
|
+
/** Keyed by frontmatter `name` (`wicked-garden-<dir segments joined by '-'>`). */
|
|
1461
|
+
skills: Record<string, SkillEntry>;
|
|
1462
|
+
/** Every managed file under `effective/`, plugin-relative. */
|
|
1463
|
+
files: Record<string, SkillFileRecord>;
|
|
1464
|
+
published: SkillPublishedRecord | null;
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
/** `GET /skills` 200 body. 503 when the root is not seeded (no installed plugin was found) or when
|
|
1468
|
+
* `current` exists but fails verification (realpath outside `snapshots/`, malformed
|
|
1469
|
+
* `snapshot.json`, content hash mismatch) — a corrupt root is a loud error, never an empty catalog. */
|
|
1470
|
+
export interface SkillsManifestResponse {
|
|
1471
|
+
manifest: SkillManifest;
|
|
1472
|
+
revision: number;
|
|
1473
|
+
/** The resolved skills root on the daemon host (`<state home>/skills` by default). */
|
|
1474
|
+
root: string;
|
|
1475
|
+
/** The VERIFIED published snapshot `current` resolves to, or `null` before the first publish.
|
|
1476
|
+
* `path` is the absolute REAL path of `snapshots/<gen>` — byte-identical to the one input the
|
|
1477
|
+
* engine is handed (`WICKED_SKILLS_SNAPSHOT`; design v3.1 §2). */
|
|
1478
|
+
current: { gen: number; path: string } | null;
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
export interface SkillFileEntry {
|
|
1482
|
+
/** POSIX path relative to the skill dir. */
|
|
1483
|
+
path: string;
|
|
1484
|
+
size: number;
|
|
1485
|
+
sha256: string;
|
|
1486
|
+
/** This file's manifest record (`null` for a file present on disk but not yet recorded). */
|
|
1487
|
+
record: SkillFileRecord | null;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
/** `GET /skills/:name/files` 200 body — the skill's OWN files (a nested skill's files are its own). */
|
|
1491
|
+
export interface SkillFileTree {
|
|
1492
|
+
name: string;
|
|
1493
|
+
dir: string;
|
|
1494
|
+
enabled: boolean;
|
|
1495
|
+
files: SkillFileEntry[];
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
/** `GET /skills/:name/files/*path` and `GET /skills/support/*path` 200 body — a typed, capped read.
|
|
1499
|
+
* Save is disabled on `truncated` or `binary`. `?side=baseline` reads the baseline copy instead
|
|
1500
|
+
* (the "new side" of a refresh conflict). */
|
|
1501
|
+
export interface SkillReadResult {
|
|
1502
|
+
/** Plugin-relative path served. */
|
|
1503
|
+
path: string;
|
|
1504
|
+
/** UTF-8 text — the first 512 KB when `truncated`; `null` when `binary` (there is no text to show). */
|
|
1505
|
+
content: string | null;
|
|
1506
|
+
/** The file's FULL size in bytes. */
|
|
1507
|
+
size: number;
|
|
1508
|
+
truncated: boolean;
|
|
1509
|
+
binary: boolean;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
export type SkillFindingKind =
|
|
1513
|
+
| 'name-invalid'
|
|
1514
|
+
| 'name-collision'
|
|
1515
|
+
| 'name-mismatch'
|
|
1516
|
+
| 'frontmatter-invalid'
|
|
1517
|
+
| 'missing-skill-md'
|
|
1518
|
+
| 'unregistered-skill'
|
|
1519
|
+
| 'nested-skill-create'
|
|
1520
|
+
| 'core-disable'
|
|
1521
|
+
| 'core-rename'
|
|
1522
|
+
| 'core-missing'
|
|
1523
|
+
| 'support-file-edit'
|
|
1524
|
+
| 'non-portable'
|
|
1525
|
+
/** A `${CLAUDE_PLUGIN_ROOT}/<p>` or `../<p>` reference in an enabled skill's files that does not
|
|
1526
|
+
* resolve inside the would-be snapshot. Severity follows the TARGET (design v3.4 §1): a reference
|
|
1527
|
+
* that ESCAPES the plugin root (`..` climbing out — it reaches whatever lies beside the snapshot on
|
|
1528
|
+
* the worker host) is `blocking`; one whose target is MISSING (it normalizes inside the root but
|
|
1529
|
+
* the snapshot does not carry it — a file the bundle omits, or a skill that is disabled) is a
|
|
1530
|
+
* `warning`: a content bug the skill's author owns, published as found — a publish with only
|
|
1531
|
+
* warnings answers `verdict: 'warnings'` with the findings AND a written snapshot (api-types
|
|
1532
|
+
* 0.28.0). */
|
|
1533
|
+
| 'unresolved-ref'
|
|
1534
|
+
/** A path the store refuses BY NAME rather than reads through: a shape that could leave its root
|
|
1535
|
+
* (`..`, an absolute piece), a component that crosses a symlink, or — under `effective/`, where
|
|
1536
|
+
* every entry is classified — a symlink or a special node (socket, fifo, device) anywhere,
|
|
1537
|
+
* the contents of a pruned directory (`.venv`, `node_modules`, `__pycache__`) INCLUDED: such a
|
|
1538
|
+
* directory is never copied or hashed, but what it holds is still judged, and a link inside one
|
|
1539
|
+
* blocks with the reason (a provisioned environment lives under `baseline/`, not the editable
|
|
1540
|
+
* root). Blocking; `file` names the entry, `skill` its owning skill when it has one. */
|
|
1541
|
+
| 'path-invalid'
|
|
1542
|
+
| 'unknown-skill'
|
|
1543
|
+
| 'no-baseline'
|
|
1544
|
+
| 'fs-drift'
|
|
1545
|
+
| 'refresh-conflict'
|
|
1546
|
+
| 'empty-snapshot'
|
|
1547
|
+
/** `.claude-plugin/plugin.json`, `archetypes.json` or `components.json` is absent from `effective/`
|
|
1548
|
+
* — the plugin manifest + the runtime catalogs are REQUIRED snapshot members (blocking at
|
|
1549
|
+
* publish; api-types 0.28.0). */
|
|
1550
|
+
| 'missing-plugin-manifest'
|
|
1551
|
+
/** The plugin manifest or a runtime catalog is present but not what its reader expects — does
|
|
1552
|
+
* not parse as JSON, is not a JSON object, or `archetypes.json` lacks its `archetypes`
|
|
1553
|
+
* collection (blocking at publish; api-types 0.28.0). A manifest without `name` is
|
|
1554
|
+
* `name-mismatch`. */
|
|
1555
|
+
| 'catalog-invalid'
|
|
1556
|
+
/** The baseline's shared read-only Python env could not be provisioned (uv missing, `uv sync`
|
|
1557
|
+
* failed, or the env could not be locked) while the bundle carries a `pyproject.toml` — the env
|
|
1558
|
+
* is REQUIRED, so the publish is blocked and nothing (the provisioning state included) is
|
|
1559
|
+
* persisted (api-types 0.28.0). */
|
|
1560
|
+
| 'venv-failed'
|
|
1561
|
+
/** A publish was refused because one is already running (one at a time) — nothing was written, so
|
|
1562
|
+
* it is a 2xx `blocked` envelope, not a 409 (409 is only a stale `expectedRevision`; api-types
|
|
1563
|
+
* 0.28.0). Re-read `GET /skills` and retry against the revision it answers. */
|
|
1564
|
+
| 'publish-in-flight'
|
|
1565
|
+
/** A publish was aborted because the skills root changed under it — nothing was written to either
|
|
1566
|
+
* root; a 2xx `blocked` envelope (api-types 0.28.0). Re-read `GET /skills` and retry. */
|
|
1567
|
+
| 'root-changed'
|
|
1568
|
+
/** A path outside the bundle closure — the ONE allowlist of what the seed copies, what the support
|
|
1569
|
+
* API may address, and what a snapshot may carry: the five runtime catalogs under `.claude-plugin`
|
|
1570
|
+
* BY NAME (`plugin.json`, `archetypes.json`, `components.json`, `specialist.json`,
|
|
1571
|
+
* `stack-registry.json` — `marketplace.json`, a publish-time listing, is outside), everything under
|
|
1572
|
+
* `skills`, `schemas` and `docs/examples`, everything under `scripts` except the `ci` and `wg`
|
|
1573
|
+
* directories and any `wg-`-prefixed dev tooling, plus `pyproject.toml` and `uv.lock`. A support
|
|
1574
|
+
* add/PUT there is a 2xx `blocked` envelope (nothing written); a file found there under
|
|
1575
|
+
* `effective/` at publish/analyze (a direct filesystem edit) is a BLOCKING finding naming the path
|
|
1576
|
+
* — a snapshot never ships it (api-types 0.28.0). */
|
|
1577
|
+
| 'outside-closure'
|
|
1578
|
+
/** A content-addressed `baseline/<hash>` whose tree does not hash to its name (a bundle file
|
|
1579
|
+
* modified, planted or removed, or a symlink inside), or a baseline file whose bytes do not match
|
|
1580
|
+
* the hash the manifest recorded for it. Baselines are re-verified before EVERY reuse: a refresh
|
|
1581
|
+
* refuses to reuse it (2xx `blocked`, nothing copied), reset refuses to restore from it (nothing
|
|
1582
|
+
* written), publish/analyze report it blocking (before AND after the env was provisioned in it).
|
|
1583
|
+
* Read-only mode bits on the baseline are a guard, never the integrity boundary — the hash is
|
|
1584
|
+
* (api-types 0.28.0). */
|
|
1585
|
+
| 'baseline-corrupt';
|
|
1586
|
+
|
|
1587
|
+
export type SkillFindingSeverity = 'warning' | 'blocking';
|
|
1588
|
+
|
|
1589
|
+
/** `blocked` = at least one blocking finding (nothing was written / published); `warnings` =
|
|
1590
|
+
* proceeded with warnings; `clear` = nothing to say. */
|
|
1591
|
+
export type SkillVerdict = 'clear' | 'warnings' | 'blocked';
|
|
1592
|
+
|
|
1593
|
+
export interface SkillConflictFinding {
|
|
1594
|
+
kind: SkillFindingKind;
|
|
1595
|
+
severity: SkillFindingSeverity;
|
|
1596
|
+
/** The skill this finding is about, or `null` for a catalog / support-file finding. */
|
|
1597
|
+
skill: string | null;
|
|
1598
|
+
/** Plugin-relative file the finding names, or `null`. */
|
|
1599
|
+
file: string | null;
|
|
1600
|
+
/** 1-based line in `file`, when the finding is anchored to one. */
|
|
1601
|
+
line: number | null;
|
|
1602
|
+
/** The OTHER catalog skill this finding is against (a collision, a core guard), or `null`. */
|
|
1603
|
+
againstSkill: string | null;
|
|
1604
|
+
againstIsCore: boolean;
|
|
1605
|
+
/** The concrete fact (names, paths, the parse error). */
|
|
1606
|
+
evidence: string;
|
|
1607
|
+
/** Why it matters. */
|
|
1608
|
+
explanation: string;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
/** `POST /skills/analyze` 200 body (a PURE dry run of the publish validation: nothing persisted,
|
|
1612
|
+
* `revision` unchanged — drift it observes is reported, and recorded only by the publish that
|
|
1613
|
+
* ships it) — and the base of every mutation result. */
|
|
1614
|
+
export interface SkillAnalyzeResult {
|
|
1615
|
+
verdict: SkillVerdict;
|
|
1616
|
+
findings: SkillConflictFinding[];
|
|
1617
|
+
revision: number;
|
|
1618
|
+
}
|
|
1619
|
+
|
|
1620
|
+
/** Every `/skills` mutation's 200 body. `blocked` ⇒ nothing was written and `revision` is unchanged. */
|
|
1621
|
+
export interface SkillMutationResult extends SkillAnalyzeResult {
|
|
1622
|
+
skill?: SkillEntry & { name: string };
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
/** `POST /skills/publish` 200 body. `snapshot` is `null` when the verdict is `blocked` — and then
|
|
1626
|
+
* `revision` is unchanged (a blocked publish persists nothing — not the drift it observed, not the
|
|
1627
|
+
* baseline's provisioning state). `path` is the absolute REAL path of the locked, read-only
|
|
1628
|
+
* generation; its `snapshot.json` carries the skill rows and the `views` block (`views.copilot`:
|
|
1629
|
+
* the `views/copilot` dir + the portable skills laid out in it). One publish runs at a time — a
|
|
1630
|
+
* concurrent one wrote nothing and answers a 2xx `blocked` `publish-in-flight` envelope
|
|
1631
|
+
* (`snapshot: null`), not a 409. */
|
|
1632
|
+
export interface SkillPublishResult extends SkillAnalyzeResult {
|
|
1633
|
+
snapshot: { gen: number; path: string; contentHash: string; skills: number } | null;
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
/** `POST /skills/refresh-baseline` 200 body — the three-way merge per FILE (baseline_old /
|
|
1637
|
+
* baseline_new / effective): unchanged → take new; user-modified & upstream-unchanged → keep;
|
|
1638
|
+
* both changed → keep + `conflict` (new side readable as `?side=baseline`); user-DELETED &
|
|
1639
|
+
* upstream-changed → the deletion is kept + `conflict` (a deletion is a modification; reset
|
|
1640
|
+
* restores upstream); upstream-deleted & user-unmodified → remove; upstream-deleted &
|
|
1641
|
+
* user-modified → keep as user-added; a new upstream skill whose path-derived name is a
|
|
1642
|
+
* user-added skill's → conflict. 502 when no plugin is installed. */
|
|
1643
|
+
export interface SkillRefreshResult extends SkillAnalyzeResult {
|
|
1644
|
+
previous_baseline: string;
|
|
1645
|
+
baseline: string;
|
|
1646
|
+
plugin_version: string;
|
|
1647
|
+
/** Skills whose files were replaced from the new baseline. */
|
|
1648
|
+
taken: string[];
|
|
1649
|
+
/** Skills that kept operator content (upstream unchanged, or a flagged conflict). */
|
|
1650
|
+
kept: string[];
|
|
1651
|
+
added: string[];
|
|
1652
|
+
removed: string[];
|
|
1653
|
+
/** Skills flagged `conflict` by this refresh. */
|
|
1654
|
+
conflicts: string[];
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
/** The 409 body of a `/skills` mutation whose `expectedRevision` is stale — a CAS conflict, the
|
|
1658
|
+
* ONLY thing that answers 409. A publish refused because another is in flight, or aborted because
|
|
1659
|
+
* the skills root changed under it, wrote nothing and instead answers a 2xx `blocked` findings
|
|
1660
|
+
* envelope (`publish-in-flight` / `root-changed`; api-types 0.28.0), never a 409. */
|
|
1661
|
+
export interface SkillRevisionConflict {
|
|
1662
|
+
error: string;
|
|
1663
|
+
/** The current revision — re-read `GET /skills` (or use this) and retry. */
|
|
1664
|
+
revision: number;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
/** The body of `POST /skills/:name/{enable,disable,reset}`, `POST /skills/publish` and
|
|
1668
|
+
* `POST /skills/refresh-baseline`. */
|
|
1669
|
+
export interface SkillRevisionBody {
|
|
1670
|
+
expectedRevision: number;
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
/** `PUT /skills/:name/files/*path` and `PUT /skills/support/*path` body. `content` is UTF-8 text,
|
|
1674
|
+
* at most 512 KB. */
|
|
1675
|
+
export interface PutSkillFileBody {
|
|
1676
|
+
content: string;
|
|
1677
|
+
expectedRevision: number;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
/** `POST /skills` body — add a user skill. `files` maps skill-relative POSIX paths to UTF-8 text
|
|
1681
|
+
* and must include `SKILL.md`; the skill lands at `skills/<name minus "wicked-garden-">`. */
|
|
1682
|
+
export interface AddSkillBody {
|
|
1683
|
+
name: string;
|
|
1684
|
+
files: Record<string, string>;
|
|
1685
|
+
expectedRevision: number;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
/** `POST /skills/:name/replace` body — replace the skill's OWN files wholesale (nested skills untouched). */
|
|
1689
|
+
export interface ReplaceSkillBody {
|
|
1690
|
+
files: Record<string, string>;
|
|
1691
|
+
expectedRevision: number;
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1255
1694
|
// ── Governance wiki management (scoreboard + meta) ─────────────────────────────
|
|
1256
1695
|
|
|
1257
1696
|
/**
|
|
@@ -1398,7 +1837,17 @@ export interface GovernanceEvalNearestRule {
|
|
|
1398
1837
|
* remediation pointer for "which rule needs sharpening".
|
|
1399
1838
|
*/
|
|
1400
1839
|
export interface GovernanceEvalResult {
|
|
1401
|
-
sample: Pick<GovernanceEvalSample, 'id' | 'description' | 'kind' | 'steering_type'
|
|
1840
|
+
sample: Pick<GovernanceEvalSample, 'id' | 'description' | 'kind' | 'steering_type'> & {
|
|
1841
|
+
/**
|
|
1842
|
+
* The sample's PAYLOAD identity — `sha256:` over the canonical JSON of its full payload (id,
|
|
1843
|
+
* description, kind, steering_type, signals). The engine echoes only the four fields above
|
|
1844
|
+
* (input `signals` never ride a result row), so this is stamped by a PRODUCER that held the
|
|
1845
|
+
* samples it staged (the internal-corpus `run`); a row without it cannot be proven to be the
|
|
1846
|
+
* same action as a row with the same id in another run, and the offline comparison reports
|
|
1847
|
+
* such a pair as unverified rather than comparable.
|
|
1848
|
+
*/
|
|
1849
|
+
payload_hash?: string;
|
|
1850
|
+
};
|
|
1402
1851
|
expected: 'deny' | 'allow';
|
|
1403
1852
|
fired: string[];
|
|
1404
1853
|
verdict: 'caught' | 'gap' | 'false_positive';
|
|
@@ -1413,15 +1862,73 @@ export interface GovernanceEvalSummary {
|
|
|
1413
1862
|
false_positives: number;
|
|
1414
1863
|
}
|
|
1415
1864
|
|
|
1865
|
+
/**
|
|
1866
|
+
* One steering rule in the judged store that NO sample of the run exercised — the inverse blind
|
|
1867
|
+
* spot of `summary.gaps` (core #394): a gap is a sample nothing caught, but a rule with zero
|
|
1868
|
+
* exercising samples produces no result row at all and so is invisible to the summary. Each
|
|
1869
|
+
* entry names the rule and the Steering sub-page it belongs to, so the corpus can grow a sample
|
|
1870
|
+
* for it (or the rule can be retired as untestable).
|
|
1871
|
+
*/
|
|
1872
|
+
export interface GovernanceEvalUnexercisedRule {
|
|
1873
|
+
rule_id: string;
|
|
1874
|
+
steering_type: SteeringType;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/**
|
|
1878
|
+
* One steering type's row of {@link GovernanceEvalRuleCoverage.per_type} (evals.rs `TypeCoverage`):
|
|
1879
|
+
* how many of the run's eligible decide-lane rules OF THAT TYPE fired for at least one sample, and
|
|
1880
|
+
* how many fired for none. `unexercised` here is a COUNT — the ids are the parent's `unexercised`
|
|
1881
|
+
* list, whose rows each carry their `steering_type`, so the two reconcile per type.
|
|
1882
|
+
*/
|
|
1883
|
+
export interface GovernanceEvalTypeCoverage {
|
|
1884
|
+
exercised: number;
|
|
1885
|
+
unexercised: number;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
/**
|
|
1889
|
+
* Rule-side coverage of an eval run (core #394/#395 — evals.rs `RuleCoverage`): the decide-lane
|
|
1890
|
+
* rules ELIGIBLE for the run (every active effect-bearing rule, narrowed to the run's `--type`
|
|
1891
|
+
* slice when one was given) partitioned into `exercised` (fired — blocking or not — for at least
|
|
1892
|
+
* one sample; a COUNT) and `unexercised` (fired for none; the ids, each with its steering type).
|
|
1893
|
+
* Together with `summary` it separates "the corpus lacks a behavior" from "the store lacks a rule"
|
|
1894
|
+
* — the two readings a bare gap count conflates. `recall_only` counts the active rules in the slice
|
|
1895
|
+
* carrying NO effect — outside the partition because the gate never fires them (core #395).
|
|
1896
|
+
* `per_type` is the same partition per steering type — all seven keys, zeros included (the engine
|
|
1897
|
+
* pins the shape).
|
|
1898
|
+
*
|
|
1899
|
+
* Every engine that emits `rule_coverage` at all (core #394 onward) serializes all four fields on
|
|
1900
|
+
* every report (serde, no skip). `recall_only` and `per_type` are declared optional on the CONTRACT
|
|
1901
|
+
* only because the daemon persists a run's coverage VERBATIM and validates none of its fields, so a
|
|
1902
|
+
* stored record is exactly as complete as its producer made it and the contract never promises what
|
|
1903
|
+
* was never checked: a consumer reads an absent `per_type` as "the engine reports no per-type
|
|
1904
|
+
* coverage" (the offline comparison says exactly that), never as zeros. `exercised` and
|
|
1905
|
+
* `unexercised` are required — no engine ever emitted one without the other.
|
|
1906
|
+
*
|
|
1907
|
+
* A run's `results[].fired` is NOT this partition's evidence: `fired` is the BLOCKING subset of the
|
|
1908
|
+
* firings, and under a type filter it may name rules of OTHER types — the filter slices the samples
|
|
1909
|
+
* and this denominator, never the gate (evals.rs `run_evals` / `evaluate_sample`).
|
|
1910
|
+
*/
|
|
1911
|
+
export interface GovernanceEvalRuleCoverage {
|
|
1912
|
+
exercised: number;
|
|
1913
|
+
unexercised: GovernanceEvalUnexercisedRule[];
|
|
1914
|
+
recall_only?: number;
|
|
1915
|
+
per_type?: Record<SteeringType, GovernanceEvalTypeCoverage>;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1416
1918
|
/**
|
|
1417
1919
|
* The `POST /testing/evals/run` 200 body — the engine's serde report passed through VERBATIM
|
|
1418
1920
|
* (snake_case field names, `degraded` spelled `null` when the run was full-fidelity;
|
|
1419
|
-
* `'facet-only'` when the
|
|
1921
|
+
* `'facet-only'` when the gap-hint embedder was unavailable and hints fell back to lexical
|
|
1922
|
+
* matching — the verdicts are the same either way, only `nearest_rules` degrades).
|
|
1923
|
+
*
|
|
1924
|
+
* `rule_coverage` is OPTIONAL on purpose: an engine that predates core #394 emits a report
|
|
1925
|
+
* without it, and that report still validates — the daemon never fabricates the field.
|
|
1420
1926
|
*/
|
|
1421
1927
|
export interface GovernanceEvalReport {
|
|
1422
1928
|
results: GovernanceEvalResult[];
|
|
1423
1929
|
summary: GovernanceEvalSummary;
|
|
1424
1930
|
degraded: 'facet-only' | null;
|
|
1931
|
+
rule_coverage?: GovernanceEvalRuleCoverage;
|
|
1425
1932
|
}
|
|
1426
1933
|
|
|
1427
1934
|
/** The `POST /testing/corpora/import` request body — a named eval corpus for later runs. */
|
|
@@ -1442,6 +1949,72 @@ export interface ImportEvalCorpusResponse {
|
|
|
1442
1949
|
embedded: boolean;
|
|
1443
1950
|
}
|
|
1444
1951
|
|
|
1952
|
+
// ── Eval run history (crew-side EvalRunStore) — the Evals section's real list ───
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* One steering type's slice of an eval run's rollup — the same four counts as
|
|
1956
|
+
* {@link GovernanceEvalSummary}, derived from the run's results grouped by `sample.steering_type`.
|
|
1957
|
+
* This is what makes "which steering type carries the gaps" a stored field, not a per-request
|
|
1958
|
+
* recompute over the full results.
|
|
1959
|
+
*/
|
|
1960
|
+
export interface EvalRunPerTypeCount {
|
|
1961
|
+
total: number;
|
|
1962
|
+
caught: number;
|
|
1963
|
+
gaps: number;
|
|
1964
|
+
false_positives: number;
|
|
1965
|
+
}
|
|
1966
|
+
|
|
1967
|
+
/**
|
|
1968
|
+
* One PERSISTED eval run — the listable rollup row (`GET /testing/evals`). A run is one
|
|
1969
|
+
* `POST /testing/evals/run` and its report, stamped with WHO ran it, WHAT it ran against, and
|
|
1970
|
+
* WHEN, so the Evals section is a real history instead of a single session-local report that a
|
|
1971
|
+
* reload loses. The per-sample `results` are NOT here — they live on {@link EvalRunDetail} (the
|
|
1972
|
+
* drilldown), so listing the history stays cheap no matter how large a custom corpus is.
|
|
1973
|
+
*
|
|
1974
|
+
* Evals take no repo/project (they judge a STORE, not a workspace), so this is a single GLOBAL
|
|
1975
|
+
* feed per daemon — unlike recon campaign runs, which file into projects.
|
|
1976
|
+
*/
|
|
1977
|
+
export interface EvalRunSummary {
|
|
1978
|
+
/** Server-minted run id. */
|
|
1979
|
+
id: string;
|
|
1980
|
+
/** Unix SECONDS the run was recorded (the crew `created_at` convention — see AgentSession). */
|
|
1981
|
+
created_at: number;
|
|
1982
|
+
/** The authenticated actor who ran it. */
|
|
1983
|
+
actor: string;
|
|
1984
|
+
/** The corpus scope (`evals:<name>`), or null for the engine's built-in dev-behaviors corpus. */
|
|
1985
|
+
corpus: string | null;
|
|
1986
|
+
/** The single steering type the run was narrowed to, or null when it ran all seven. */
|
|
1987
|
+
type_filter: SteeringType | null;
|
|
1988
|
+
/** The steering rule store the run was judged against (the daemon's `--db` steering store). */
|
|
1989
|
+
rule_store: string;
|
|
1990
|
+
/** The report's rollup, verbatim ({@link GovernanceEvalSummary}). */
|
|
1991
|
+
summary: GovernanceEvalSummary;
|
|
1992
|
+
/** Per-steering-type rollup, derived from the results — present only for the types the run covered. */
|
|
1993
|
+
per_type: Partial<Record<SteeringType, EvalRunPerTypeCount>>;
|
|
1994
|
+
/** `'facet-only'` when the run degraded to facet matching; null when it ran full-fidelity. */
|
|
1995
|
+
degraded: 'facet-only' | null;
|
|
1996
|
+
/**
|
|
1997
|
+
* The report's {@link GovernanceEvalReport.rule_coverage}, persisted VERBATIM (like `degraded`,
|
|
1998
|
+
* never recomputed daemon-side). ABSENT — not null — when the engine that ran the eval predates
|
|
1999
|
+
* core #394 and emitted no coverage, so a history row honestly shows which runs measured it.
|
|
2000
|
+
*/
|
|
2001
|
+
rule_coverage?: GovernanceEvalRuleCoverage;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
/**
|
|
2005
|
+
* One eval run WITH its full per-sample results (`GET /testing/evals/:id`) — the drilldown behind a
|
|
2006
|
+
* history row. `results` is the same verbatim {@link GovernanceEvalResult} array the run's original
|
|
2007
|
+
* `POST /testing/evals/run` returned, so the existing report view renders it unchanged.
|
|
2008
|
+
*/
|
|
2009
|
+
export interface EvalRunDetail extends EvalRunSummary {
|
|
2010
|
+
results: GovernanceEvalResult[];
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
/** The `GET /testing/evals` 200 body — the eval run history, newest first. */
|
|
2014
|
+
export interface ListEvalRunsResponse {
|
|
2015
|
+
runs: EvalRunSummary[];
|
|
2016
|
+
}
|
|
2017
|
+
|
|
1445
2018
|
/**
|
|
1446
2019
|
* The `POST /testing/recon` request body (api-types 0.15.0) — the Testing page's campaign-recon
|
|
1447
2020
|
* trigger. `problem` is the recon brief, passed to every launched run VERBATIM (the client owns
|
|
@@ -1836,6 +2409,15 @@ export interface SystemSettings {
|
|
|
1836
2409
|
* the engine default `~/.wicked-worker`.
|
|
1837
2410
|
*/
|
|
1838
2411
|
worker_config_root?: string;
|
|
2412
|
+
/*
|
|
2413
|
+
* There is NO skills setting (skills keystone, api-types 0.28.0). The daemon-owned skills root is
|
|
2414
|
+
* `<state home>/skills` — the directory holding `manifest.json`, `baseline/<hash>/`, `effective/`,
|
|
2415
|
+
* `snapshots/<gen>/` and the `current` symlink — full stop: not configurable (a `skills_root` a
|
|
2416
|
+
* client sends is dropped and named in the audit entry's `ignored`; design v3.1 §1 one storage
|
|
2417
|
+
* root, v3.2 §1 never a user CLI directory), and there is no mirror knob either (the daemon never
|
|
2418
|
+
* writes into the user's own CLI directories). `GET /skills` → `root` and `GET /diagnostics` →
|
|
2419
|
+
* `skills.root` report where it is.
|
|
2420
|
+
*/
|
|
1839
2421
|
/**
|
|
1840
2422
|
* The repo-scoped launch delivery DEFAULT (crew#393; api-types 0.18.0, additive). What a
|
|
1841
2423
|
* `POST /runs` with `repoRef` + a CODE-WORK `workflow` (a def with at least one
|
|
@@ -2119,6 +2701,32 @@ export interface InteractiveDocDeleteResponse {
|
|
|
2119
2701
|
ledger: InteractiveDocDeleteLedgerReport;
|
|
2120
2702
|
}
|
|
2121
2703
|
|
|
2704
|
+
/**
|
|
2705
|
+
* One row of `GET /projects/:projectId/interactive/api/docs` (crew#472) — the bridge's own
|
|
2706
|
+
* `GET /api/docs` row (interactive's `listDocs` shape, relayed field-for-field) stamped with the
|
|
2707
|
+
* project whose mount it was listed under. Docs roots are partitioned per project (the `default`
|
|
2708
|
+
* project keeps the legacy shared root), so `projectId` is the attribution a client rendering
|
|
2709
|
+
* docs from several projects at once cannot otherwise recover.
|
|
2710
|
+
*/
|
|
2711
|
+
export interface InteractiveDocSummary {
|
|
2712
|
+
/** The doc name (slug). */
|
|
2713
|
+
name: string;
|
|
2714
|
+
/** Manifest `kind`; a manifest without one lists as `doc`. */
|
|
2715
|
+
kind: 'doc' | 'html' | 'source' | 'demo';
|
|
2716
|
+
/** Head version. */
|
|
2717
|
+
head: number;
|
|
2718
|
+
/** Lineage size. */
|
|
2719
|
+
versions: number;
|
|
2720
|
+
/** ISO-8601 timestamp of the head version, or null when the lineage is empty. */
|
|
2721
|
+
updated_at: string | null;
|
|
2722
|
+
/** Present only on a retired (tombstoned) row, which lists only with `?includeRetired=1`. */
|
|
2723
|
+
retired?: true;
|
|
2724
|
+
/** ISO-8601 retirement timestamp; present with `retired`. */
|
|
2725
|
+
retired_at?: string;
|
|
2726
|
+
/** The crew project this row was listed under — the mount's `:projectId`. */
|
|
2727
|
+
projectId: string;
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2122
2730
|
/**
|
|
2123
2731
|
* One normalized entry of the merged project activity feed
|
|
2124
2732
|
* (`GET /projects/:id/activity`, ADR §5.2): core events of member runs/chats
|
|
@@ -2754,4 +3362,236 @@ export interface DiagnosticsResponse {
|
|
|
2754
3362
|
/** Bounded tail of the daemon's own error-level log lines, newest first. */
|
|
2755
3363
|
recentErrors: DiagnosticsRecentError[];
|
|
2756
3364
|
acp: AcpDiagnostics;
|
|
3365
|
+
/** The skills seam's last outcome (api-types 0.28.0) — see `DiagnosticsSkills`. */
|
|
3366
|
+
skills: DiagnosticsSkills;
|
|
3367
|
+
}
|
|
3368
|
+
|
|
3369
|
+
/** The skills seam's state as `GET /diagnostics` reports it (skills keystone, api-types 0.28.0). */
|
|
3370
|
+
export type DiagnosticsSkillsState = 'published' | 'fallback' | 'blocked' | 'config-error' | 'disabled';
|
|
3371
|
+
|
|
3372
|
+
/** One finding the skills degradation ladder produced (design v3 §3). */
|
|
3373
|
+
export interface DiagnosticsSkillsFinding {
|
|
3374
|
+
/** `skills.fallback` = no wicked-garden installed (engine input left unset / restored to the boot
|
|
3375
|
+
* value — the engine resolves the live cache itself); `skills.blocked` = the first publish is
|
|
3376
|
+
* blocked (engine input points at a non-existent refusal path — launches fail loudly until the
|
|
3377
|
+
* catalog is fixed); `skills.config` = the configured root is corrupt/unusable (same refusal;
|
|
3378
|
+
* `error`), or — as a `warning` on the `published` state — the root lies OUTSIDE the daemon
|
|
3379
|
+
* state home, so core's fence cross-check (`WICKED_CREW_STATE_HOME`) refuses every launch. */
|
|
3380
|
+
kind: 'skills.fallback' | 'skills.blocked' | 'skills.config';
|
|
3381
|
+
severity: 'warning' | 'error';
|
|
3382
|
+
message: string;
|
|
3383
|
+
}
|
|
3384
|
+
|
|
3385
|
+
/**
|
|
3386
|
+
* `GET /diagnostics` → `skills`: whether the engine is being handed a verified snapshot, and if not,
|
|
3387
|
+
* why — the one read-only answer to "why do launches refuse the skills snapshot". `disabled` is a
|
|
3388
|
+
* daemon booted without the seam (the manifest collector, some tests).
|
|
3389
|
+
*/
|
|
3390
|
+
export interface DiagnosticsSkills {
|
|
3391
|
+
state: DiagnosticsSkillsState;
|
|
3392
|
+
/** The resolved skills root on the daemon host; `null` when `disabled`. */
|
|
3393
|
+
root: string | null;
|
|
3394
|
+
/** The VERIFIED published snapshot (`current` realpath-contained, `snapshot.json` + content hash
|
|
3395
|
+
* checked), or `null`. `path` is the absolute REAL path of `snapshots/<gen>`. */
|
|
3396
|
+
current: { gen: number; path: string } | null;
|
|
3397
|
+
/** What `WICKED_SKILLS_SNAPSHOT` — the engine's ONE skills input (v3.1 §2, v3.4 §2;
|
|
3398
|
+
* `WICKED_SKILLS_CURRENT` is never set, `WICKED_CREW_STATE_HOME` is not exported) — is exported as
|
|
3399
|
+
* right now: the real snapshot path, a `<root>/refused/…` refusal path, `""` when the process
|
|
3400
|
+
* booted with an explicitly EMPTY value (preserved — a configuration error core refuses, state
|
|
3401
|
+
* `config-error`; design v3.5 §4), or `null` = unset. */
|
|
3402
|
+
engineInput: string | null;
|
|
3403
|
+
/** DIAGNOSTICS-ONLY: the canonical realpath of the daemon state home, reported for humans. It is
|
|
3404
|
+
* NOT an engine input — `WICKED_CREW_STATE_HOME` is retired (v3.4 §2): core reads only
|
|
3405
|
+
* `WICKED_SKILLS_SNAPSHOT` and derives the state home from its `<state home>/skills/snapshots/<gen>`
|
|
3406
|
+
* layout. `null` only when `disabled`. */
|
|
3407
|
+
stateHome: string | null;
|
|
3408
|
+
findings: DiagnosticsSkillsFinding[];
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
// ── Memory proposal queue (DES-MEM-FACETED-001 §5.0, api-types 0.21.0) ─────────
|
|
3412
|
+
|
|
3413
|
+
/**
|
|
3414
|
+
* A proposal's lifecycle state. A proposal is INERT while `pending` (never recalled/applied);
|
|
3415
|
+
* `approve` promotes its payload to the active store and marks it `approved`, `reject` marks it
|
|
3416
|
+
* `rejected`.
|
|
3417
|
+
*/
|
|
3418
|
+
export type ProposalState = 'pending' | 'approved' | 'rejected';
|
|
3419
|
+
|
|
3420
|
+
/**
|
|
3421
|
+
* One entry in the estate proposal queue — a type-generic, inert write agents submit and operators
|
|
3422
|
+
* decide (estate #162). The shape is the estate MCP `proposal.list` wire form verbatim.
|
|
3423
|
+
*/
|
|
3424
|
+
export interface Proposal {
|
|
3425
|
+
/** Stable proposal-node id. */
|
|
3426
|
+
id: string;
|
|
3427
|
+
/** `"memory"` | `"policy:<steering_type>"` | future — the router key `approve` promotes by. */
|
|
3428
|
+
kind_type: string;
|
|
3429
|
+
/** The proposed content (a memory body, a policy rule, …). Opaque here; narrowed by `kind_type`. */
|
|
3430
|
+
payload: unknown;
|
|
3431
|
+
/** Agent-declared orthogonal facets (axis → value). */
|
|
3432
|
+
facets: Record<string, string>;
|
|
3433
|
+
/** Run/unit/agent attribution — AUTHORITY-STAMPED by the estate MCP from its launch env, never
|
|
3434
|
+
* taken from the submitter. Empty when the server had no `WICKED_RUN_*` env. */
|
|
3435
|
+
provenance: Record<string, string>;
|
|
3436
|
+
state: ProposalState;
|
|
3437
|
+
/** Unix seconds. */
|
|
3438
|
+
created_at: number;
|
|
3439
|
+
}
|
|
3440
|
+
|
|
3441
|
+
/** `GET /proposals` query filters — both optional; omitting a filter lists across that dimension. */
|
|
3442
|
+
export interface ListProposalsQuery {
|
|
3443
|
+
kind_type?: string;
|
|
3444
|
+
state?: ProposalState;
|
|
3445
|
+
}
|
|
3446
|
+
|
|
3447
|
+
/** `GET /proposals` → 200. */
|
|
3448
|
+
export interface ListProposalsResponse {
|
|
3449
|
+
proposals: Proposal[];
|
|
3450
|
+
}
|
|
3451
|
+
|
|
3452
|
+
/**
|
|
3453
|
+
* The result of landing an approved POLICY proposal as a steering rule (DES-MEM-FACETED-001 §5.2).
|
|
3454
|
+
* estate performs no rules-write for a policy proposal (the AW-11 invariant) — it hands the payload
|
|
3455
|
+
* to crew, the one governed rules-write path, which shapes a `ConformanceRule` and upserts it. Like
|
|
3456
|
+
* the steering-author landing (crew#388) the estate decision (approve) stands regardless, so a
|
|
3457
|
+
* landing that could not complete reports `outcome:"failed"` with a loud, operator-readable `error`
|
|
3458
|
+
* (recoverable by hand via `POST /governance/rules`) rather than turning the approve into a 500.
|
|
3459
|
+
*/
|
|
3460
|
+
export interface PolicyLandingResult {
|
|
3461
|
+
/** `landed` = the steering rule is in the store; `failed` = the proposal is approved but no rule
|
|
3462
|
+
* was written — `error` says why. */
|
|
3463
|
+
outcome: 'landed' | 'failed';
|
|
3464
|
+
/** The upserted rule's id (deterministic `proposal:<proposalId>`, idempotent on replay). Present on `landed`. */
|
|
3465
|
+
ruleId?: string;
|
|
3466
|
+
/** The steering type derived from the proposal's `kind_type` (`policy:<type>`). Present on `landed`. */
|
|
3467
|
+
steering_type?: SteeringType;
|
|
3468
|
+
/** Present iff `outcome:"failed"` — the loud reason the rule did not land. */
|
|
3469
|
+
error?: string;
|
|
3470
|
+
}
|
|
3471
|
+
|
|
3472
|
+
/**
|
|
3473
|
+
* `POST /proposals/:id/approve` → 200. A MEMORY proposal is `promoted` (its payload is now an
|
|
3474
|
+
* active memory, `active_id`) and is complete. A POLICY proposal is `handed_off` — its `payload`
|
|
3475
|
+
* (`{ rule, severity }`) is still returned verbatim, and `landing` now reports the steering rule
|
|
3476
|
+
* crew created from it (`landing.outcome:"landed"` with the `ruleId`), or the loud reason it could
|
|
3477
|
+
* not (`landing.outcome:"failed"`). `landing` is absent only from responses served by a daemon
|
|
3478
|
+
* predating the policy→steering landing (forward-additive, DES-MEM-FACETED-001 §5.2).
|
|
3479
|
+
*/
|
|
3480
|
+
export type ApproveProposalResponse =
|
|
3481
|
+
| { outcome: 'promoted'; active_id: string }
|
|
3482
|
+
| { outcome: 'handed_off'; payload: unknown; landing?: PolicyLandingResult };
|
|
3483
|
+
|
|
3484
|
+
/** `POST /proposals/:id/reject` → 200. */
|
|
3485
|
+
export interface RejectProposalResponse {
|
|
3486
|
+
ok: true;
|
|
3487
|
+
}
|
|
3488
|
+
|
|
3489
|
+
// ── Memory management (governed-knowledge surface, DES-MEM-FACETED-001, api-types 0.22.0) ──────
|
|
3490
|
+
//
|
|
3491
|
+
// The studio's surface for managing the memories that ALREADY exist in the operator store — the
|
|
3492
|
+
// counterpart to the proposal queue (which decides learnings not yet stored). Backed by the estate
|
|
3493
|
+
// MCP memory tools (memory.recall / memory.coverage / memory.erase) exposed over crew's /api/v1.
|
|
3494
|
+
// The granularity is estate's, surfaced honestly: browse is query-based (no "list all"), and
|
|
3495
|
+
// retire is SUBTREE-scoped (no per-id delete).
|
|
3496
|
+
|
|
3497
|
+
/**
|
|
3498
|
+
* One memory as browsed from the store (`GET /memory`, from estate `memory.recall`).
|
|
3499
|
+
*
|
|
3500
|
+
* estate `memory.recall` is query-based and token-budgeted — there is NO "list all" — and returns
|
|
3501
|
+
* `{ memory_id, scope, content, tier, score }` per item. It does NOT surface per-item `facets`, so
|
|
3502
|
+
* `facets` is presently always `{}` (kept in the shape for forward-compatibility and to mirror
|
|
3503
|
+
* `Proposal.facets`); `score` is the recall relevance score, absent when estate omits it.
|
|
3504
|
+
*/
|
|
3505
|
+
export interface MemoryItem {
|
|
3506
|
+
/** Stable memory-node id (estate `memory_id`). */
|
|
3507
|
+
id: string;
|
|
3508
|
+
/** The memory body. */
|
|
3509
|
+
content: string;
|
|
3510
|
+
/** Tier: `working` | `episodic` | `semantic` | `procedural` | `archival`. */
|
|
3511
|
+
tier: string;
|
|
3512
|
+
/** The memory's own hierarchical scope path (slash-separated `kind:id` segments; `""` = root). */
|
|
3513
|
+
scope: string;
|
|
3514
|
+
/** Orthogonal facets (axis → value) the memory is tagged with. Populated by estate `memory.list`
|
|
3515
|
+
* (the management browse) — this is what powers the surface's facet filter. `{}` for an
|
|
3516
|
+
* unfaceted memory (and for a `memory.recall`-sourced item, which carries no facets). */
|
|
3517
|
+
facets: Record<string, string>;
|
|
3518
|
+
/** Relevance score — only present on a `memory.recall` item; `memory.list` (the browse) is not
|
|
3519
|
+
* relevance-ranked, so it is absent there. */
|
|
3520
|
+
score?: number;
|
|
3521
|
+
/**
|
|
3522
|
+
* Unix-SECONDS timestamp of when this memory FIRST entered the store — the "added" time the
|
|
3523
|
+
* governance surface sorts + date-filters on (api-types 0.23.0). Populated from estate
|
|
3524
|
+
* `memory.list`, which returns `created_at` per item; ABSENT — never fabricated — on a memory
|
|
3525
|
+
* whose creation time the store did not report (and on a `memory.recall`-sourced item, which
|
|
3526
|
+
* carries none). Additive: absence is the one spelling of "unknown", so a `since`/`until` filter
|
|
3527
|
+
* cannot assert an undated memory in range.
|
|
3528
|
+
*/
|
|
3529
|
+
created_at?: number;
|
|
3530
|
+
}
|
|
3531
|
+
|
|
3532
|
+
/**
|
|
3533
|
+
* `GET /memory` query filters. All optional. The browse itself lists the COMPLETE in-scope set via
|
|
3534
|
+
* estate `memory.list`; `query`, `facets`, and `limit` are applied server-side as post-filters over
|
|
3535
|
+
* that set (they are NOT a relevance query). `scope_prefix` is the only estate-side argument — a
|
|
3536
|
+
* subtree filter where `""`/omitted matches EVERY memory (the same predicate as retire). `query` is
|
|
3537
|
+
* a case-insensitive CONTENT substring match. `facets` is a JSON-encoded object of axis→value
|
|
3538
|
+
* strings; an item is kept only if it carries every pair. `limit` caps the returned ROW count.
|
|
3539
|
+
*/
|
|
3540
|
+
export interface ListMemoriesQuery {
|
|
3541
|
+
/** Case-insensitive content substring filter over the complete set. */
|
|
3542
|
+
query?: string;
|
|
3543
|
+
scope_prefix?: string;
|
|
3544
|
+
/** JSON-encoded `Record<string, string>` (axis→value); a malformed value is a 400. */
|
|
3545
|
+
facets?: string;
|
|
3546
|
+
/** Positive integer; caps the returned row count. */
|
|
3547
|
+
limit?: string;
|
|
3548
|
+
/**
|
|
3549
|
+
* Inclusive LOWER date bound (api-types 0.23.0): keep only memories with `created_at >= since`.
|
|
3550
|
+
* Unix SECONDS, a non-negative integer (a present-but-non-integer value is a 400). Applied
|
|
3551
|
+
* server-side over the loaded set, alongside `query`/`facets`. A memory with NO `created_at`
|
|
3552
|
+
* is EXCLUDED whenever `since` (or `until`) is set — an unknown creation time is not asserted
|
|
3553
|
+
* in range. Omitted ⇒ no lower bound.
|
|
3554
|
+
*/
|
|
3555
|
+
since?: string;
|
|
3556
|
+
/**
|
|
3557
|
+
* Inclusive UPPER date bound (api-types 0.23.0): keep only memories with `created_at <= until`.
|
|
3558
|
+
* Same units/validation/undated-exclusion as {@link ListMemoriesQuery.since}. Omitted ⇒ no
|
|
3559
|
+
* upper bound.
|
|
3560
|
+
*/
|
|
3561
|
+
until?: string;
|
|
3562
|
+
}
|
|
3563
|
+
|
|
3564
|
+
/** `GET /memory` → 200. */
|
|
3565
|
+
export interface ListMemoriesResponse {
|
|
3566
|
+
memories: MemoryItem[];
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
/** `GET /memory/coverage` query — an optional subtree filter (`""`/omitted = global totals). */
|
|
3570
|
+
export interface MemoryCoverageQuery {
|
|
3571
|
+
scope_prefix?: string;
|
|
3572
|
+
}
|
|
3573
|
+
|
|
3574
|
+
/**
|
|
3575
|
+
* `GET /memory/coverage` → 200 (estate `memory.coverage`). Memory-node counts, optionally scoped to
|
|
3576
|
+
* a subtree: `total`, plus per-tier and per-kind breakdowns.
|
|
3577
|
+
*/
|
|
3578
|
+
export interface MemoryCoverageResponse {
|
|
3579
|
+
total: number;
|
|
3580
|
+
by_tier: Record<string, number>;
|
|
3581
|
+
by_kind: Record<string, number>;
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3584
|
+
/**
|
|
3585
|
+
* `POST /memory/retire` body. Retire is SUBTREE-scoped, never per-id: estate `memory.erase`
|
|
3586
|
+
* hard-deletes EVERY memory whose scope equals or descends from `scope_prefix`. `scope_prefix` is
|
|
3587
|
+
* required and must be non-empty (estate refuses a total wipe) — the surface must show the operator
|
|
3588
|
+
* the whole subtree it will remove, not a single row.
|
|
3589
|
+
*/
|
|
3590
|
+
export interface RetireMemoryBody {
|
|
3591
|
+
scope_prefix: string;
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3594
|
+
/** `POST /memory/retire` → 200. The number of memories the subtree wipe deleted. */
|
|
3595
|
+
export interface RetireMemoryResponse {
|
|
3596
|
+
erased: number;
|
|
2757
3597
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wicked-crew-api-types",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"description": "The wire contract of the wicked-crew daemon's /api/v1 REST surface and /ws CoreEvent frames \u2014 types only, zero runtime",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./index.d.ts",
|