wicked-crew-api-types 0.167.0 → 0.172.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/index.d.ts +1237 -5
  2. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -359,6 +359,58 @@ export interface HealthCapabilities {
359
359
  * the model are on this machine) is {@link VoiceStatus.ready}. ABSENT on an older daemon: read as `false`.
360
360
  */
361
361
  voice?: boolean;
362
+ /**
363
+ * (api-types 0.168.0) `GET /workflows/catalog` is served: the categorised workflow catalog with the
364
+ * requesting operator's usage ({@link WorkflowCatalogResponse}). ABSENT on an older daemon: read as `false`.
365
+ */
366
+ workflowCatalog?: boolean;
367
+ /**
368
+ * (api-types 0.169.0, DES-CHAT-ARTIFACTS-001 §14) What a chat can do with artifacts, one flag per operation, each
369
+ * true only once its slice ships. `context`: `ChatMessageBody.context` is accepted ({@link ChatMessageContext}),
370
+ * `POST /chats {seedFromArtifact}` is served, and each turn's seats get the open artifact, the runs the person names
371
+ * and the ids they may use, recorded per turn as a `context` transcript record. `proposals` (api-types 0.170.0,
372
+ * slice A2): a seat's `wicked-make` block becomes a crew-resolved proposal card answered by a person. The operations
373
+ * (`create`, `refine`, `theme`, `demo`) are later slices and are `false` until then (all `true` with `stub: true` in
374
+ * the test configuration). ABSENT on an older daemon: read every flag as `false`, and send no `context` (the message
375
+ * body is strict there, so the field would be refused).
376
+ */
377
+ chatArtifacts?: ChatArtifactsCapabilities;
378
+ /**
379
+ * (api-types 0.172.0, DES-APPS-001) PRESENT means the app store is served: `GET|POST /apps`, `GET /apps/:app`, and
380
+ * the human-only edits, undo, redo and restore. The flags of {@link AppsCapabilities} are about the OTHER routes:
381
+ * each says whether that route serves for real yet (rather than `501`). A skin shows Apps only when this is
382
+ * present. ABSENT on an older daemon.
383
+ */
384
+ apps?: AppsCapabilities;
385
+ }
386
+
387
+ /** {@link HealthCapabilities.chatArtifacts} (api-types 0.169.0). */
388
+ export interface ChatArtifactsCapabilities {
389
+ context: boolean;
390
+ /**
391
+ * (api-types 0.170.0, slice A2) A seat's `wicked-make` block becomes a crew-resolved proposal ({@link ArtifactProposal},
392
+ * transcript kinds `proposal` / `proposal_accepting` / `proposal_decided`, `/ws` {@link ChatProposalFrame}) and
393
+ * `POST /chats/:id/proposals/:pid` answers it. Seats are told only about the operations below that are `true`, and
394
+ * the resolver and the accept route refuse the others, so with every operation `false` no card is made.
395
+ */
396
+ proposals: boolean;
397
+ create: boolean;
398
+ refine: boolean;
399
+ theme: boolean;
400
+ demo: boolean;
401
+ /**
402
+ * (api-types 0.170.0) The daemon runs the chat-artifacts TEST configuration (DES §13 A2): every operation resolves,
403
+ * and Go on a proposal of a kind in {@link stubOps} records a stub dispatch and starts NOTHING (a skin says so on that
404
+ * card). With `stubOps` absent (a daemon before 0.171.0), that is every kind. ABSENT in production.
405
+ */
406
+ stub?: true;
407
+ /**
408
+ * (api-types 0.171.0, slice A3) The operations whose Go records a stub dispatch and starts nothing under the TEST
409
+ * configuration; every other advertised operation starts real work (A3: `create` is real, `refine`, `theme` and
410
+ * `demo` stay stubs until their slices). ABSENT in production. A skin says "starts nothing" only for these kinds;
411
+ * before this field, `stub: true` meant every kind.
412
+ */
413
+ stubOps?: ArtifactProposalKind[];
362
414
  }
363
415
 
364
416
  // ── A run's working document (program DES-POWER-MODE §8.3, §9; api-types 0.148.0) ──────────────────
@@ -1220,6 +1272,22 @@ export interface AgentSession {
1220
1272
  * before the field: check `GET /health.capabilities.runChatId` before grouping runs by chat.
1221
1273
  */
1222
1274
  chat_id?: string;
1275
+ /**
1276
+ * (api-types 0.171.0, DES-CHAT-ARTIFACTS-001 §4.3, slice A3) The chat proposal this run answers
1277
+ * (`<chatId>:<turnId>`, or a crew-made `<chatId>:retry:<runId>`), read back from the `run.launched`
1278
+ * entry after a restart. ABSENT on a run no proposal started.
1279
+ */
1280
+ proposal_id?: string;
1281
+ /** (api-types 0.171.0) The artifact a proposal-bound run makes (`<mount>/<doc>`, the mount a project id or `_unfiled`),
1282
+ * so a skin finds an Unfiled run's document. ABSENT on a run no proposal started. */
1283
+ artifact_ref?: string;
1284
+ /**
1285
+ * (api-types 0.171.0, §6.2) A proposal-bound attempt ended without landing: the engine run failed or was stopped,
1286
+ * crew's landing check failed (no file, the document floor, the announce), or a daemon restart interrupted it
1287
+ * ("interrupted by a restart"). The chat and the document are released, and the chat holds a retry proposal
1288
+ * (`<chatId>:retry:<runId>`: Try again into the same document). ABSENT when the attempt landed or is still going.
1289
+ */
1290
+ landing_failed?: { reason: string; at: number };
1223
1291
  /**
1224
1292
  * (crew#552) The onboarding run this run was CHAINED from (a `capture-learnings` run the daemon
1225
1293
  * launched when that onboarding completed) — read back from the `run.launched` entry after a
@@ -7295,6 +7363,115 @@ export interface PresetResponse {
7295
7363
  preset: Preset;
7296
7364
  }
7297
7365
 
7366
+ // ── The workflow catalog (api-types 0.168.0, lane wf-catalog) ───────────────────────────────────
7367
+
7368
+ /** A catalog category id. `yours` is listed only when the operator has saved presets. */
7369
+ export type WorkflowCatalogCategoryId = 'create' | 'resolve' | 'analyze' | 'test' | 'more' | 'yours';
7370
+
7371
+ /** One category of the catalog, in display order, with its ordered subcategories (`[]` for `more` and `yours`). */
7372
+ export interface WorkflowCatalogCategory {
7373
+ id: WorkflowCatalogCategoryId;
7374
+ title: string;
7375
+ /** One line under the heading. */
7376
+ summary: string;
7377
+ subcategories: { id: string; title: string }[];
7378
+ }
7379
+
7380
+ /** What a catalog entry needs before it can start. */
7381
+ export type WorkflowCatalogNeed = 'repo' | 'project' | 'running_app' | 'requirements' | 'document' | 'runs' | 'nothing';
7382
+
7383
+ /** One field the launch asks for, in order. */
7384
+ export interface WorkflowCatalogInput {
7385
+ id: string;
7386
+ label: string;
7387
+ kind: 'text' | 'repo' | 'project' | 'url' | 'requirements' | 'seats';
7388
+ required: boolean;
7389
+ }
7390
+
7391
+ /**
7392
+ * How a client starts an entry: `run` = `POST /runs {workflow}`; `demo` = `POST /projects/:id/demo`;
7393
+ * `compose` = `POST /projects/:id/product/compose` (draft epics); `document` = a new interactive document
7394
+ * of that style (its draft run); `chat` = a chat on its own page; `none` = nothing to launch (coming soon).
7395
+ */
7396
+ export type WorkflowCatalogLaunch =
7397
+ | { kind: 'run'; workflow: string }
7398
+ | { kind: 'demo' }
7399
+ | { kind: 'compose' }
7400
+ | { kind: 'document'; style: 'web' | 'doc' | 'ppt' | 'brochure' }
7401
+ | { kind: 'chat' }
7402
+ | { kind: 'none' };
7403
+
7404
+ /** The requesting operator's use of an entry (scope: {@link WorkflowCatalogResponse.usage}). */
7405
+ export interface WorkflowCatalogUsage {
7406
+ /** Runs this operator launched under the entry's workflow (a retry is not a new launch). */
7407
+ launches: number;
7408
+ /** ISO 8601 launch time of the newest such run, or null. */
7409
+ lastUsedAt: string | null;
7410
+ /** Median wall time in minutes of those runs that COMPLETED, or null when none did. */
7411
+ medianMinutes: number | null;
7412
+ }
7413
+
7414
+ /** One card of the catalog. */
7415
+ export interface WorkflowCatalogEntry {
7416
+ /**
7417
+ * Unique, opaque card id. For `launch.kind === 'run'` it is `launch.workflow`, prefixed `run:` when a non-run
7418
+ * card already holds that id (a saved preset called `presentation`). Launch by `launch.workflow`, never by `id`.
7419
+ */
7420
+ id: string;
7421
+ category: WorkflowCatalogCategoryId;
7422
+ /** A subcategory id of its category, or null. */
7423
+ subcategory: string | null;
7424
+ title: string;
7425
+ /** What it does, one line. */
7426
+ summary: string;
7427
+ /** What you get, one line. */
7428
+ outcome: string;
7429
+ /** 0–4 short phrases saying when to pick it. */
7430
+ useWhen: string[];
7431
+ needs: WorkflowCatalogNeed[];
7432
+ inputs: WorkflowCatalogInput[];
7433
+ /** Static hint in minutes `[low, high]` from observed runs; null = not known. */
7434
+ typicalMinutes: [number, number] | null;
7435
+ /** Relative cost hint (agent phases and seats); null = not known. */
7436
+ cost: 'low' | 'medium' | 'high' | null;
7437
+ /** Shown as chips; searchable. */
7438
+ tags: string[];
7439
+ /** Searchable only. */
7440
+ synonyms: string[];
7441
+ status: 'available' | 'coming_soon' | 'unavailable';
7442
+ /** Why coming soon or unavailable; null when available. */
7443
+ statusNote: string | null;
7444
+ launch: WorkflowCatalogLaunch;
7445
+ /** The workflow requires QE acceptance before it delivers. */
7446
+ requiresQe: boolean;
7447
+ delivers: 'pr' | 'document' | 'video' | 'report' | 'answer' | 'none';
7448
+ /** Where the definition lives: the engine's built-in preset, a crew workflow, the operator's saved preset, studio's own flow, or none yet. */
7449
+ source: 'builtin' | 'crew' | 'saved' | 'studio' | 'none';
7450
+ usage: WorkflowCatalogUsage;
7451
+ }
7452
+
7453
+ /**
7454
+ * `GET /workflows/catalog[?projectId=]` (api-types 0.168.0): the workflows as a categorised catalog with
7455
+ * the requesting operator's usage, derived from the runs the daemon already records (the `run.launched`
7456
+ * trail's principal, the run's resolved workflow). Additive: `GET /workflows` is unchanged.
7457
+ */
7458
+ export interface WorkflowCatalogResponse {
7459
+ categories: WorkflowCatalogCategory[];
7460
+ /** By category order, then subcategory order, then catalog order. */
7461
+ entries: WorkflowCatalogEntry[];
7462
+ /** "Your top": up to 6 available entry ids with launches, by launches then most recent. */
7463
+ top: string[];
7464
+ usage: {
7465
+ /** `operator`: every run this principal launched; `project`: only those filed in `projectId`. */
7466
+ scope: 'operator' | 'project';
7467
+ projectId: string | null;
7468
+ /** The principal the usage is for (`Actor.id`; `local` on a loopback daemon). */
7469
+ actorId: string;
7470
+ /** Runs counted across every entry. */
7471
+ launches: number;
7472
+ };
7473
+ }
7474
+
7298
7475
  // ── Projects (DES-PROJECT-001) — the experience-plane keystone ─────────────────
7299
7476
 
7300
7477
  /** Project lifecycle: `active ⇄ archived`, no hard delete (ADR §1.3). */
@@ -7473,6 +7650,16 @@ export interface InteractiveDemoStepDraft {
7473
7650
  export interface InteractiveDocCreateRequest {
7474
7651
  /** The doc name; the bridge slugifies it (409 when taken or retired). */
7475
7652
  name?: string;
7653
+ /**
7654
+ * (api-types 0.171.0, DES-CHAT-ARTIFACTS-001 §5.2) The chat a proposal's create comes from. Accepted ONLY with
7655
+ * `proposal_id`, and only when that chat holds a `proposal_accepting` record of the caller's whose identity names
7656
+ * this document in this mount and whose stored resolution lists every `repo_ref(s)`: then an Unfiled document may
7657
+ * name those repositories (`unfiled_doc_repo` is lifted through the record, nothing else). Anything else is a 400
7658
+ * `proposal_grant_refused` and nothing is created. Crew sends these itself on a proposal's Go.
7659
+ */
7660
+ chat_id?: string;
7661
+ /** (api-types 0.171.0) The proposal (`<chatId>:<turnId>` or `<chatId>:retry:<runId>`); see `chat_id`. */
7662
+ proposal_id?: string;
7476
7663
  /** Omitted = a plain html doc (`html` required); `source` = generated from brief/sources; `demo` = a recording. */
7477
7664
  kind?: 'source' | 'demo';
7478
7665
  /** A plain html doc's content (or a `source` doc's seed). */
@@ -7534,9 +7721,11 @@ export interface InteractiveDocCreateRefusal {
7534
7721
  * Unfiled mount; `invalid_repo_ref` — junk spelling / shape / over the cap; `ambiguous_repo_ref`
7535
7722
  * — a name or basename shared by several member repos (name it by id); `project_mismatch` — the
7536
7723
  * body's `project` is not the route's project (or is present on the Unfiled mount). The proxy
7537
- * canonicalizes an OMITTED `project` from the route.
7724
+ * canonicalizes an OMITTED `project` from the route. `proposal_grant_refused` (api-types 0.171.0) — `chat_id`
7725
+ * without `proposal_id` (or the reverse), or no `proposal_accepting` record of the caller's in that chat that names
7726
+ * this document, this mount and every requested repository.
7538
7727
  */
7539
- code: 'repo_not_in_project' | 'unfiled_doc_repo' | 'invalid_repo_ref' | 'ambiguous_repo_ref' | 'project_mismatch';
7728
+ code: 'repo_not_in_project' | 'unfiled_doc_repo' | 'invalid_repo_ref' | 'ambiguous_repo_ref' | 'project_mismatch' | 'proposal_grant_refused';
7540
7729
  /** The refs the request named, as spelled (best-effort stringified when malformed). */
7541
7730
  requested: string[];
7542
7731
  /** The subset of `requested` that matched no member repo (`repo_not_in_project` only). */
@@ -7905,6 +8094,18 @@ export interface ChatOpenBody {
7905
8094
  * engine without it is a 409. Send it only when `GET /health.capabilities.chatWorkflows` names it.
7906
8095
  */
7907
8096
  workflow?: string;
8097
+ /**
8098
+ * (api-types 0.169.0, DES-CHAT-ARTIFACTS-001 §6.3) "Ask about this" on a document no open chat holds: the
8099
+ * artifact ref (`<mount>/<doc>`). Allowed for a chat with no `projectId`, or for a project chat whose project is the
8100
+ * document's mount (checked before anything is read; else 400 `seed_out_of_scope`). Crew then checks the document
8101
+ * exists (404 `seed_not_found`) and, for a project chat, that every repository it was made about belongs to the
8102
+ * project (else 400 `seed_out_of_scope`). When the request names no `repoRefs` and its `scopeKind` is not
8103
+ * `everything` or `project`, the chat reads the repositories the document was made about (its recorded binding);
8104
+ * a `system` or `none` kind then becomes the repository scope. A document made about no repository seeds nothing.
8105
+ * A non-person actor is refused (403 `seed_refused`). Send only when
8106
+ * `GET /health.capabilities.chatArtifacts.context === true`.
8107
+ */
8108
+ seedFromArtifact?: string;
7908
8109
  }
7909
8110
 
7910
8111
  /** The scope a `POST /chats` caller may name (api-types 0.39.0, studio#323 R4). */
@@ -8205,7 +8406,17 @@ export interface ChatUsage {
8205
8406
  */
8206
8407
  export type ChatTranscriptRecord =
8207
8408
  /** `attachments` (api-types 0.137.0): the files the message carried, ABSENT when none. */
8208
- | { at: number; turnId: string; kind: 'user'; text: string; seats: string[]; attachments?: ChatAttachment[] }
8409
+ | {
8410
+ at: number;
8411
+ turnId: string;
8412
+ kind: 'user';
8413
+ text: string;
8414
+ seats: string[];
8415
+ attachments?: ChatAttachment[];
8416
+ /** (api-types 0.170.0) Sent by the chat's owner, a person (the message route's own check). Only these words count
8417
+ * as the person's for a proposal (a demo address, a document they named). ABSENT for any other sender. */
8418
+ owner?: true;
8419
+ }
8209
8420
  | { at: number; turnId: string; kind: 'seat'; cliKey: string; text: string; ok: boolean; usage: ChatUsage | null }
8210
8421
  /**
8211
8422
  * The citation verdicts of the `seat` record with the same `turnId` + `cliKey` (api-types 0.68.0,
@@ -8238,7 +8449,235 @@ export type ChatTranscriptRecord =
8238
8449
  * 0.85.0, DC-S7): the rules remembered since they last heard from crew. Disclosed here so the
8239
8450
  * operator sees exactly what crew added; the `user` record keeps the operator's own words.
8240
8451
  */
8241
- | { at: number; turnId: string; kind: 'system'; text: string };
8452
+ | { at: number; turnId: string; kind: 'system'; text: string }
8453
+ /**
8454
+ * What the seats of `turnId` were given beside the operator's words (api-types 0.169.0, DES-CHAT-ARTIFACTS-001
8455
+ * §5.1): the open artifact's copies, the run briefs, and what was refused. Written with the turn's `user` record.
8456
+ */
8457
+ | ({ at: number; turnId: string; kind: 'context' } & ChatContextBinding)
8458
+ /**
8459
+ * (api-types 0.170.0, DES-CHAT-ARTIFACTS-001 §4.2) A proposal crew resolved from a seat's `wicked-make` block in the
8460
+ * reply of `turnId`. A later `proposal` record with the same `proposal.pid` REPLACES it (a re-resolution after the
8461
+ * document's head moved); a newer pid in the same chat supersedes an open one.
8462
+ */
8463
+ | { at: number; turnId: string; kind: 'proposal'; proposal: ArtifactProposal }
8464
+ /** (api-types 0.170.0, §4.3 step 2) A person said go: what was accepted, written before anything starts. */
8465
+ | ({ at: number; turnId: string; kind: 'proposal_accepting' } & ProposalAccepting)
8466
+ /** (api-types 0.170.0) The proposal's answer: go (with what it started) or not now. */
8467
+ | ({ at: number; turnId: string; kind: 'proposal_decided' } & ProposalDecided);
8468
+
8469
+ // ── Chat proposals (api-types 0.170.0, DES-CHAT-ARTIFACTS-001 §4, slice A2) ─────────────────────────────────────────
8470
+ //
8471
+ // A seat proposes an artifact job in a fenced `wicked-make` block. Crew cuts it out of the reply (it never reaches a
8472
+ // reader), resolves it against its own records, and appends a `proposal` transcript record plus a `chatProposal`
8473
+ // frame. The card's facts are crew's, never the seat's. Only `POST /chats/:id/proposals/:pid` (a person, the chat's
8474
+ // owner) starts anything. Capabilities: `GET /health.capabilities.chatArtifacts.proposals` and one flag per operation.
8475
+
8476
+ /** What a proposal makes. `create` and `demo` come from a catalog entry; `refine` changes a document; `theme` is a
8477
+ * studio appearance patch. */
8478
+ export type ArtifactProposalKind = 'create' | 'refine' | 'theme' | 'demo';
8479
+
8480
+ /** One "From" chip: a source the block named, as crew resolved and authorized it. */
8481
+ export interface ProposalSource {
8482
+ /** The token as crew reads it: `chat`, `page`, `message:<turnId>`, `run:<full id>`, `doc:<mount>/<doc>`,
8483
+ * `attachment:<id>` (a run prefix the seat wrote is resolved to the full id when it can be). */
8484
+ token: string;
8485
+ /** Plain words: "this conversation", "run 29bbbfb1 · stopped", "Release note v4". */
8486
+ label: string;
8487
+ /** `struck`: the draft never sees it; `reason` says why ("run 1a2b3c4d · not named in this chat"). */
8488
+ status: 'ok' | 'struck';
8489
+ reason?: string;
8490
+ /** The project the source belongs to (`null`: Unfiled, or not a project's); absent for `chat` and `page`. A form that
8491
+ * changes the project strikes a chip whose project differs. */
8492
+ projectId?: string | null;
8493
+ }
8494
+
8495
+ /** "Codex and Claude Code: one writes, the other checks": two distinct eligible seats (never who writes). */
8496
+ export interface ProposalHelpers {
8497
+ seats: string[];
8498
+ label: string;
8499
+ }
8500
+
8501
+ /** "About 10–20 min · 3 agent steps · median of 4 past documents here: 9 min, $0.62". Unknown parts are left out. */
8502
+ export interface ProposalTime {
8503
+ /** The catalog entry's `typicalMinutes`. */
8504
+ minutes: [number, number] | null;
8505
+ /** The catalog entry's relative cost. */
8506
+ cost: 'low' | 'medium' | 'high' | null;
8507
+ /** Agent steps of the workflow. */
8508
+ steps: number | null;
8509
+ /** Over completed runs of the same workflow on this daemon. `costUsd` only from at least 3 runs whose every agent
8510
+ * step reported a cost (`costRuns` of them); else `null`. */
8511
+ median: { runs: number; minutes: number; costUsd: number | null; costRuns: number } | null;
8512
+ label: string;
8513
+ }
8514
+
8515
+ /** A proposal as crew resolved it. Every line is crew's: the seat chose only the block's words. */
8516
+ export interface ArtifactProposal {
8517
+ /** `<chatId>:<turnId>` for a turn's proposal (later slices add `<chatId>:redo:<runId>` / `:retry:`). */
8518
+ pid: string;
8519
+ chatId: string;
8520
+ /** The turn whose reply carried the block; `null` for a crew-made proposal (`sourceRunId`). */
8521
+ turnId: string | null;
8522
+ sourceRunId?: string;
8523
+ kind: ArtifactProposalKind;
8524
+ /** The seat that wrote the block. */
8525
+ seat: string;
8526
+ /** The "What" line: `label` is "Slides · Domain coverage for new engineers"; `outcome` is the catalog entry's. */
8527
+ what: { entry: string | null; title: string; label: string; outcome: string | null };
8528
+ /** The brief (create), the ask (refine), or what to show (demo), as the seat wrote it. */
8529
+ brief: string | null;
8530
+ reason: string | null;
8531
+ sources: ProposalSource[];
8532
+ /** Repositories (names), each in the chat's scope. */
8533
+ repos: string[];
8534
+ /** (api-types 0.171.0) The same repositories by registry id, in the same order (two checkouts may share a name). */
8535
+ repoRefs?: string[];
8536
+ /** (api-types 0.171.0) A crew-made retry (`sourceRunId`): the document it fills again (`<mount>/<doc>`), the same
8537
+ * artifact as the failed attempt's. ABSENT on a turn's proposal (its document name derives from the pid). */
8538
+ artifactRef?: string;
8539
+ /** `null` for a theme (no run). */
8540
+ helpers: ProposalHelpers | null;
8541
+ /** `null` for a theme. */
8542
+ time: ProposalTime | null;
8543
+ /** "Stops for you if the check fails. The plan or an escalation may also ask." `null` for a theme. */
8544
+ stops: string | null;
8545
+ /** The project the result is filed in; `label` is the project's name or "Not in a project · this chat". */
8546
+ filedIn: { projectId: string | null; label: string };
8547
+ /** refine: the document (`<mount>/<doc>`), its title, and the head it was proposed on. */
8548
+ target?: { ref: string; title: string; base: number };
8549
+ /** refine: the parts the seat pointed at (`data-wid`s); hand edits are kept unless the person says otherwise. */
8550
+ touches?: string[];
8551
+ /** theme: the partial appearance patch and the keys it changes. */
8552
+ theme?: { name: string; patch: Record<string, string | number | boolean>; changes: string[] };
8553
+ /** demo: the app address (from the person's words), the audience and what to show. */
8554
+ demo?: { url: string; audience: string | null; show: string | null };
8555
+ /** Go is refused with this reason ("Needs a second helper to check it"); the card shows it. `null` = Go is open. */
8556
+ blocked: string | null;
8557
+ }
8558
+
8559
+ /**
8560
+ * What a person may change before Go (typed per kind; crew re-checks every edit with the rules of §4.2). Crew takes
8561
+ * `title`, `brief`, `sources`, `helpers`, `repos` and `project` on a create (api-types 0.171.0, slice A3: New document's
8562
+ * "answer this proposal" mode), `ask` and `helpers` on a refine, `name` and `patch` on a theme, and `audience`, `show`
8563
+ * and `helpers` on a demo; `url` and `allow` are refused (400 `edits_invalid`) until the slices whose forms edit them.
8564
+ * `project` changes only in a chat filed in no project: then every source, repository and turn of the conversation is
8565
+ * re-checked against the target project, and what is outside it is struck (a `message:` source whole, when any of its
8566
+ * records is outside). `repos` must be in the chat's scope (and, with a target project, members of it). A source or
8567
+ * repository the person adds that cannot be used is refused, never dropped. An interrupted (accepting) proposal
8568
+ * resumes with its accepted choices: only its `helpers` may change.
8569
+ */
8570
+ export interface ProposalEdits {
8571
+ /** create */
8572
+ title?: string;
8573
+ brief?: string;
8574
+ sources?: string[];
8575
+ repos?: string[];
8576
+ project?: string | null;
8577
+ /** refine */
8578
+ ask?: string;
8579
+ allow?: string[];
8580
+ /** theme */
8581
+ name?: string;
8582
+ patch?: Record<string, string | number | boolean>;
8583
+ /** demo */
8584
+ url?: string;
8585
+ audience?: string;
8586
+ show?: string;
8587
+ /** any kind with a run */
8588
+ helpers?: string[];
8589
+ }
8590
+
8591
+ /** What the accepted proposal is named by: the artifact, and the run derived from the pid. */
8592
+ export interface ProposalIdentity {
8593
+ /** `<mount>/<doc>`: the document a create makes (its name derived from the pid) or a refine changes; `null` for a
8594
+ * theme or a demo. */
8595
+ artifactRef: string | null;
8596
+ /** A name-based UUID of the pid: the launch's `sessionId`, so a retry can only adopt it. */
8597
+ runId: string;
8598
+ }
8599
+
8600
+ /** What Go started. */
8601
+ export interface ProposalResult {
8602
+ runId: string;
8603
+ artifactRef: string | null;
8604
+ /** Found already launched (a retry, a second tab) and adopted rather than started again. */
8605
+ adopted: boolean;
8606
+ /** The test configuration's recording stub took it: nothing was started. */
8607
+ stub: boolean;
8608
+ }
8609
+
8610
+ export interface ProposalAccepting {
8611
+ pid: string;
8612
+ /** The person's actor id. */
8613
+ actor: string;
8614
+ edits: ProposalEdits | null;
8615
+ /** The effective proposal after the edits were applied and checked. */
8616
+ resolution: ArtifactProposal;
8617
+ identity: ProposalIdentity;
8618
+ }
8619
+
8620
+ export interface ProposalDecided {
8621
+ pid: string;
8622
+ actor: string;
8623
+ decision: 'go' | 'not_now';
8624
+ /** `null` for not now. */
8625
+ result: ProposalResult | null;
8626
+ }
8627
+
8628
+ /** A proposal folded from its records. */
8629
+ export interface ChatProposalView {
8630
+ proposal: ArtifactProposal;
8631
+ /** `accepting`: Go was recorded and its result was not (an interrupted Go: "Go again" resumes the same identity). */
8632
+ state: 'open' | 'accepting' | 'decided' | 'superseded';
8633
+ decision?: 'go' | 'not_now';
8634
+ result?: ProposalResult | null;
8635
+ /** Unix millis of the decision. */
8636
+ decidedAt?: number;
8637
+ }
8638
+
8639
+ /** `POST /chats/:id/proposals/:pid` body. */
8640
+ export interface ChatProposalDecisionBody {
8641
+ decision: 'go' | 'not_now';
8642
+ edits?: ProposalEdits;
8643
+ }
8644
+
8645
+ /**
8646
+ * `POST /chats/:id/proposals/:pid` → 200: the proposal as decided (a repeat of the same decision answers the stored
8647
+ * result again, so a lost response is safe to retry). Only a person answers: under `auth: required` a worker, agent or
8648
+ * system token is refused; under `auth: off` every request is the local person (the daemon's documented local-mode
8649
+ * posture, as for gates). Refusals carry `code`: 403 `proposal_refused` (not a person),
8650
+ * 404 (no such chat or proposal, or a chat that is not the caller's under `auth: required`: its existence is not
8651
+ * disclosed), 409 `proposal_superseded` | `proposal_decided` | `proposal_blocked` | `operation_unavailable` |
8652
+ * `chat_busy` | `document_busy` | `source_gone` | `helper_unavailable` | `chat_gone` | `proposal_reresolved` (the
8653
+ * document moved: `view.proposal` is the fresh one), 400 `edits_invalid`, 502 `launch_failed` (accepting stays
8654
+ * recorded; Go again resumes the same identity). A 409 carries the current `view` when there is one.
8655
+ */
8656
+ export interface ChatProposalDecisionResponse {
8657
+ view: ChatProposalView;
8658
+ }
8659
+
8660
+ /**
8661
+ * `/ws` (api-types 0.170.0): a proposal record was appended to chat `chat` (the same record the transcript holds).
8662
+ * `project_id`: the chat's project, when it has one. A `type` alias (like the daemon's other synthetic frames) so it
8663
+ * satisfies {@link CoreEvent}'s index signature.
8664
+ */
8665
+ export type ChatProposalFrame = {
8666
+ type: 'chatProposal';
8667
+ chat: string;
8668
+ turn_id: string;
8669
+ record: Extract<ChatTranscriptRecord, { kind: 'proposal' | 'proposal_accepting' | 'proposal_decided' }>;
8670
+ project_id?: string;
8671
+ };
8672
+
8673
+ /**
8674
+ * `/ws` (api-types 0.171.0, DES-CHAT-ARTIFACTS-001 §6.2, slice A3): a proposal-bound attempt ended without landing — the
8675
+ * engine run failed or was stopped, crew's landing check refused it (no draft file, the document floor, a source file
8676
+ * changed during the run, the announce), or a daemon restart interrupted it. The run's DTO carries the same
8677
+ * `landing_failed`, the chat and the document are released, and the chat holds a retry proposal
8678
+ * (`<chatId>:retry:<session>`, a `chatProposal` frame).
8679
+ */
8680
+ export type RunLandingFailedFrame = { type: 'landingFailed'; session: string; artifact_ref: string; reason: string };
8242
8681
 
8243
8682
  // ── System chat: history, close, attachments (api-types 0.137.0, DES-SYSTEM-CHAT "Crew") ────────
8244
8683
  //
@@ -8365,6 +8804,84 @@ export interface ChatMessageBody {
8365
8804
  * file: a path it can open (images as images where the CLI reads them) plus, for text and pdf,
8366
8805
  * the extracted text. Send only when `GET /health.capabilities.chatAttachments === true`. */
8367
8806
  attachments?: string[];
8807
+ /**
8808
+ * (api-types 0.169.0, DES-CHAT-ARTIFACTS-001 §5.1) What studio shows beside the message: the artifact open on the
8809
+ * surface it was sent from, the tab's appearance, and studio's page summary. Send only when
8810
+ * `GET /health.capabilities.chatArtifacts.context === true`. Crew checks the chat's owner and scope before it
8811
+ * reads anything; the seat gets labelled copies (data, never instructions), and the turn's `context` transcript
8812
+ * record says what it was given.
8813
+ */
8814
+ context?: ChatMessageContext;
8815
+ }
8816
+
8817
+ /** {@link ChatMessageBody.context} (api-types 0.169.0). */
8818
+ export interface ChatMessageContext {
8819
+ /** The artifact on the surface the message was sent from (inline, pane, Made or full screen). */
8820
+ artifact?: ChatArtifactContext;
8821
+ /** The tab's effective appearance (DES §9), sent when `chatArtifacts.theme` is advertised. */
8822
+ appearance?: ChatContextAppearance;
8823
+ /**
8824
+ * Studio's page summary (the context pack), kept apart from the person's typed words. Crew labels it as studio's
8825
+ * and reads from it only the run of the page it describes and the runs added as chips. At most 32,768 characters.
8826
+ */
8827
+ page?: string;
8828
+ }
8829
+
8830
+ /** {@link ChatMessageContext.artifact} (api-types 0.169.0). */
8831
+ export interface ChatArtifactContext {
8832
+ /** `<mount>/<doc>`: the mount is a project id or `_unfiled`; the doc is the interactive document name. */
8833
+ ref: string;
8834
+ /** The version the person is looking at (≥ 0: a document's first version is 0), read after the editor's in-flight
8835
+ * writes finished. */
8836
+ version: number;
8837
+ /** The `data-wid` of each part the person selected (at most 16). */
8838
+ anchors?: string[];
8839
+ }
8840
+
8841
+ /** {@link ChatMessageContext.appearance} (api-types 0.169.0): studio's appearance scalars, plus an active preview. */
8842
+ export interface ChatContextAppearance {
8843
+ theme?: string;
8844
+ accent_h?: number;
8845
+ accent_s?: number;
8846
+ accent_l?: number;
8847
+ ambient?: boolean;
8848
+ ambient_still?: boolean;
8849
+ /** The name of the theme proposal being previewed, when one is. */
8850
+ previewOf?: string;
8851
+ }
8852
+
8853
+ /**
8854
+ * What one turn's seats were given (api-types 0.169.0, DES-CHAT-ARTIFACTS-001 §5.1 "Bindings kept"): the `context`
8855
+ * transcript record. Paths are relative to the chat's scratch root. A reader folds it onto the turn's `user` record; a
8856
+ * skin that does not know this kind ignores it.
8857
+ */
8858
+ export interface ChatContextBinding {
8859
+ /** The open artifact, when the message carried one in scope. */
8860
+ artifact?: {
8861
+ ref: string;
8862
+ title: string;
8863
+ kind: string;
8864
+ /** The version the person was looking at, and the latest when the turn was sent. */
8865
+ viewed: number;
8866
+ head: number;
8867
+ /** `null` for an Unfiled document. */
8868
+ projectId: string | null;
8869
+ /** The copies the seat read: `open/<mount>__<doc>-v<n>.html`. When the version they look at is missing or could
8870
+ * not be copied, only the latest's copy is here, and `viewed` still says what they asked about. */
8871
+ copies: string[];
8872
+ anchors: string[];
8873
+ };
8874
+ /** The run briefs the seat was given: `context/run-<id>-<sha8>.md` (named by content, so a later brief of the same
8875
+ * run never replaces these bytes), with the sha256 of the brief as written. */
8876
+ runs: Array<{ id: string; status: string; brief: string; sha256: string }>;
8877
+ /** What was named but not read, and why ("not in this chat's project", "no such run on this daemon"). */
8878
+ refused: Array<{ token: string; reason: string }>;
8879
+ /** Every run and document the person has named in this chat up to this turn. On later turns a run is kept while it
8880
+ * is still on this daemon and in the chat's scope, a document while it is in the chat's scope; an earlier brief is
8881
+ * offered again only while its file still holds the bytes its sha256 records. */
8882
+ named: { runs: string[]; docs: string[] };
8883
+ /** Studio's page summary rode as `context.page` (true), never mixed into the person's words. */
8884
+ page: boolean;
8368
8885
  }
8369
8886
 
8370
8887
  /** `GET /chats/:id` → 200. */
@@ -9811,7 +10328,9 @@ export interface SystemRun {
9811
10328
  liveness: SystemRunLiveness;
9812
10329
  /** Plain-words why ("waiting on you: plan approval", "no event for 42 min"). */
9813
10330
  reason: string;
9814
- /** The cursor unit's phase (`phase_ref`, else `stage`); `null` before planning. */
10331
+ /** The step the cursor unit is on, as display words: the step id its unit id names (`answer-1` → `answer 1`),
10332
+ * else a named `phase_ref`, else its catalog entry, else its `stage`; an engine-numbered step (`unit-N`, `uN`)
10333
+ * never shows. `null` before planning. */
9815
10334
  phase: string | null;
9816
10335
  /** The cursor unit's ord; `null` when none is in flight. */
9817
10336
  unitOrd: number | null;
@@ -13806,3 +14325,716 @@ export interface PutVoiceDashboardBody {
13806
14325
  layout: string[];
13807
14326
  widgets: VoiceWidgetManifest[];
13808
14327
  }
14328
+
14329
+ // ── Apps (DES-APPS-001 rev 11, slice K2a; api-types 0.172.0) ──────────────────────────────────────
14330
+ //
14331
+ // An app is a git repo crew owns under `~/.wicked/apps/<slug>`: `apps.json` (an {@link AppSpec}),
14332
+ // `queries/<id>.<sql|graphql|json>` (each query's text) and `apps.lock.json` (an {@link AppLock},
14333
+ // written by crew's check, never by an agent or a person). Every version is one commit on the repo's
14334
+ // `main`; crew keeps, outside the repo, a RECEIPT per (app, tree) saying how the tree was accepted, and
14335
+ // only a tree holding one ever runs against a source.
14336
+
14337
+ /** The `schema` value of every v1 `apps.json`. */
14338
+ export type AppSpecSchemaId = 'wicked.apps/v1';
14339
+
14340
+ /**
14341
+ * `apps.json`, the whole app: pages of components on a 12-column grid, the named queries and actions
14342
+ * they bind, the theme and the hosts a link may open. No app code: the `wicked-apps` runtime interprets
14343
+ * this document and nothing else (DES §7.1).
14344
+ *
14345
+ * Identifiers: page and component ids match `^[A-Za-z][A-Za-z0-9_-]{0,63}$` and are unique across the
14346
+ * app; query and action ids match `^[a-z][a-z0-9_]{0,63}$` (a query id is a file name, so lower case
14347
+ * only) and are unique among queries and among actions. crew refuses an unknown key anywhere in the
14348
+ * document, so a field a newer studio writes reaches an older daemon as a refused edit, never as a
14349
+ * silently dropped one.
14350
+ *
14351
+ * The whole document is at most 2 MB of JSON, nests at most 16 deep, holds no list longer than its stated
14352
+ * limit (200 for any list not given one) and no object with more than 256 keys, and never uses the key
14353
+ * `__proto__`.
14354
+ */
14355
+ export interface AppSpec {
14356
+ schema: AppSpecSchemaId;
14357
+ /** The app's title (1-120 characters). */
14358
+ name: string;
14359
+ /** At most 2,000 characters. */
14360
+ description?: string;
14361
+ theme?: AppTheme;
14362
+ /** 1-20 pages; the first is the one an app opens on. */
14363
+ pages: AppPage[];
14364
+ /** At most 100. */
14365
+ queries: AppQuery[];
14366
+ /** At most 50. */
14367
+ actions: AppAction[];
14368
+ /**
14369
+ * Exact host names (lower case, no scheme, no port) a link component may open, over https only. The
14370
+ * host shows the full URL before opening it (DES §7.1). At most 50.
14371
+ */
14372
+ linkHosts: string[];
14373
+ }
14374
+
14375
+ /** Colours are `#rrggbb`. */
14376
+ export interface AppTheme {
14377
+ /** `auto` follows the viewer's setting (the default). */
14378
+ mode?: 'light' | 'dark' | 'auto';
14379
+ accent?: string;
14380
+ /** Chart series colours in order (at most 12). */
14381
+ palette?: string[];
14382
+ density?: 'comfortable' | 'compact';
14383
+ }
14384
+
14385
+ export interface AppPage {
14386
+ id: string;
14387
+ /** 1-80 characters. */
14388
+ title: string;
14389
+ /** At most 100 per page. */
14390
+ components: AppComponent[];
14391
+ }
14392
+
14393
+ /**
14394
+ * A component's place on its page's 12-column grid: column `x` (0-11), row `y` (0-999), `w` columns
14395
+ * (1-12, `x + w <= 12`) and `h` rows (1-50). Below 640 px the runtime stacks components by (`y`, `x`).
14396
+ */
14397
+ export interface AppBox {
14398
+ x: number;
14399
+ y: number;
14400
+ w: number;
14401
+ h: number;
14402
+ }
14403
+
14404
+ /** What every component carries. */
14405
+ export interface AppComponentBase {
14406
+ id: string;
14407
+ box: AppBox;
14408
+ /** At most 120 characters; shown above the component. */
14409
+ title?: string;
14410
+ /** Hidden below 640 px. */
14411
+ hideOnPhone?: boolean;
14412
+ }
14413
+
14414
+ export type AppComponent =
14415
+ | AppTableComponent
14416
+ | AppChartComponent
14417
+ | AppKpiComponent
14418
+ | AppTextComponent
14419
+ | AppFilterComponent
14420
+ | AppFormComponent
14421
+ | AppButtonComponent
14422
+ | AppImageComponent
14423
+ | AppLinkComponent;
14424
+
14425
+ export type AppComponentType = AppComponent['type'];
14426
+
14427
+ /**
14428
+ * Where a query or action parameter's value comes from on the page (DES §4 "Binding"):
14429
+ * - `filter.<id>`: the value of filter component `<id>` (a `date-range` filter gives
14430
+ * `filter.<id>.from` and `filter.<id>.to`);
14431
+ * - `form.<field>`: a field of the form the binding sits on;
14432
+ * - `row.<column>`: a column of the row a table's row action was pressed on;
14433
+ * - `url.<name>`: a parameter of the page's URL.
14434
+ * Viewer values are never bindings: a parameter declared `from: 'viewer.*'` is filled by the server.
14435
+ */
14436
+ export type AppBindingRef = `filter.${string}` | `form.${string}` | `row.${string}` | `url.${string}`;
14437
+ // The template type is the shape; crew's grammar is the rule: `filter.<component id>` with an optional
14438
+ // `.from`/`.to`, `form.<field name>`, `row.<output column>`, `url.<^[A-Za-z_][A-Za-z0-9_]{0,63}$>`.
14439
+
14440
+ /** A component reading a query: the query's id, and a binding for any of its parameters. */
14441
+ export interface AppQueryBinding {
14442
+ query: string;
14443
+ params?: Record<string, AppBindingRef>;
14444
+ }
14445
+
14446
+ /** A control that runs an action: its label, the action's id, and a binding for any of its parameters. */
14447
+ export interface AppActionBinding {
14448
+ /** 1-60 characters. */
14449
+ label: string;
14450
+ action: string;
14451
+ params?: Record<string, AppBindingRef>;
14452
+ }
14453
+
14454
+ /** How a value is shown. `decimals` is 0-10; a currency names its ISO 4217 code. */
14455
+ export type AppFormat =
14456
+ | { kind: 'currency'; currency: string; decimals?: number }
14457
+ | { kind: 'text' | 'number' | 'percent' | 'date' | 'datetime' | 'duration'; decimals?: number };
14458
+
14459
+ export interface AppTableColumn {
14460
+ /** A declared output column of the table's query. */
14461
+ column: string;
14462
+ label?: string;
14463
+ format?: AppFormat;
14464
+ }
14465
+
14466
+ export interface AppTableComponent extends AppComponentBase {
14467
+ type: 'table';
14468
+ data: AppQueryBinding;
14469
+ /** Shown columns in order; absent = every declared output column. */
14470
+ columns?: AppTableColumn[];
14471
+ /** A button on every row (at most 4); its parameters may bind `row.<column>`. */
14472
+ rowActions?: AppActionBinding[];
14473
+ /** Rows per page, 5-500. */
14474
+ pageSize?: number;
14475
+ }
14476
+
14477
+ export interface AppChartSeries {
14478
+ /** A declared output column of the chart's query. */
14479
+ column: string;
14480
+ label?: string;
14481
+ color?: string;
14482
+ }
14483
+
14484
+ export interface AppChartComponent extends AppComponentBase {
14485
+ type: 'chart';
14486
+ chart: 'line' | 'bar' | 'area' | 'pie';
14487
+ data: AppQueryBinding;
14488
+ /** The category (x axis, or pie slice) column. */
14489
+ x: string;
14490
+ /** 1-12 series; a pie has exactly one. */
14491
+ series: AppChartSeries[];
14492
+ stacked?: boolean;
14493
+ }
14494
+
14495
+ export interface AppKpiComponent extends AppComponentBase {
14496
+ type: 'kpi';
14497
+ data: AppQueryBinding;
14498
+ /** The column of the first row shown as the figure. */
14499
+ value: string;
14500
+ format?: AppFormat;
14501
+ /** A column of the first row shown as the change beside the figure. */
14502
+ delta?: string;
14503
+ /** A line under the figure, at most 120 characters ("vs previous 90 days"). */
14504
+ caption?: string;
14505
+ }
14506
+
14507
+ /** Author text, always rendered as text (never HTML or markdown). At most 4,000 characters. */
14508
+ export interface AppTextComponent extends AppComponentBase {
14509
+ type: 'text';
14510
+ text: string;
14511
+ style?: 'heading' | 'body' | 'caption';
14512
+ }
14513
+
14514
+ /** `value` and `label` are at most 200 characters each. */
14515
+ export interface AppFilterOption {
14516
+ value: string;
14517
+ label?: string;
14518
+ }
14519
+
14520
+ /** A select filter's choices: a query's columns, or a fixed list of at most 200. */
14521
+ export type AppFilterOptions =
14522
+ | { kind: 'query'; query: AppQueryBinding; value: string; label?: string }
14523
+ | { kind: 'values'; values: AppFilterOption[] };
14524
+
14525
+ export interface AppFilterComponent extends AppComponentBase {
14526
+ type: 'filter';
14527
+ filter: 'select' | 'date-range' | 'search';
14528
+ label?: string;
14529
+ /** `select` only: the choices, from a query (its `value` and optional `label` columns) or a fixed list. */
14530
+ options?: AppFilterOptions;
14531
+ /** The value before the viewer picks one: a `select`/`search` string, or `{ lastDays }` for a `date-range`. */
14532
+ default?: string | { lastDays: number };
14533
+ }
14534
+
14535
+ export interface AppFormField {
14536
+ /** `^[a-z][a-z0-9_]{0,63}$`; bound as `form.<name>`. */
14537
+ name: string;
14538
+ label?: string;
14539
+ input: 'text' | 'textarea' | 'number' | 'date' | 'checkbox' | 'select';
14540
+ required?: boolean;
14541
+ /** `select` only (at most 200). */
14542
+ options?: AppFilterOption[];
14543
+ }
14544
+
14545
+ export interface AppFormComponent extends AppComponentBase {
14546
+ type: 'form';
14547
+ /** 1-30 fields. */
14548
+ fields: AppFormField[];
14549
+ /** The action the form submits; its parameters bind `form.<name>`. */
14550
+ submit: AppActionBinding;
14551
+ }
14552
+
14553
+ export interface AppButtonComponent extends AppComponentBase {
14554
+ type: 'button';
14555
+ action: AppActionBinding;
14556
+ }
14557
+
14558
+ /**
14559
+ * An image carried in the spec: `src` is a `data:image/(png|jpeg|gif|webp);base64,…` URI whose DECODED
14560
+ * payload is at most 256 KB (262,144 bytes; the URI itself is about a third longer); `alt` is at most
14561
+ * 300 characters. No network images (the frame's CSP has none) and no SVG.
14562
+ */
14563
+ export interface AppImageComponent extends AppComponentBase {
14564
+ type: 'image';
14565
+ src: string;
14566
+ alt: string;
14567
+ }
14568
+
14569
+ /**
14570
+ * Where a link goes: another page of the app (with URL parameters bound like a query's), or out to an
14571
+ * `https://` URL whose host is in {@link AppSpec.linkHosts} (no credentials, no port).
14572
+ */
14573
+ export type AppLinkTarget = { kind: 'page'; page: string; params?: Record<string, AppBindingRef> } | { kind: 'href'; href: string };
14574
+
14575
+ export interface AppLinkComponent extends AppComponentBase {
14576
+ type: 'link';
14577
+ label: string;
14578
+ to: AppLinkTarget;
14579
+ }
14580
+
14581
+ /** A source kind a query reads; it names the text file: `sql` `.sql`, `graphql` `.graphql`, else `.json`. */
14582
+ export type AppQueryKind = 'sql' | 'graphql' | 'rest' | 'mcp' | 'crew';
14583
+
14584
+ /** A declared output column's type. A value the source cannot confirm is validated on every row (DES §7.2). */
14585
+ export type AppColumnType = 'string' | 'number' | 'integer' | 'boolean' | 'date' | 'datetime';
14586
+
14587
+ export interface AppOutputColumn {
14588
+ /** `^[A-Za-z_][A-Za-z0-9_-]{0,127}$` (alias an SQL expression, e.g. `AS order_total`). */
14589
+ name: string;
14590
+ type: AppColumnType;
14591
+ nullable?: boolean;
14592
+ }
14593
+
14594
+ /** `load` (the default; cached 5 s), `every` N seconds (an integer, 15-86,400), or `events` (the built-in `crew` source only). */
14595
+ export type AppRefresh = { mode: 'load' } | { mode: 'every'; seconds: number } | { mode: 'events' };
14596
+
14597
+ /**
14598
+ * One parameter, a JSON Schema subset (no `pattern`: an agent-written regular expression would run on
14599
+ * the server). Names match `^[A-Za-z_][A-Za-z0-9_]{0,63}$`. `from` makes it VIEWER-BOUND: the server fills
14600
+ * it from verified claims and refuses a client value for it, and no component may bind it.
14601
+ *
14602
+ * Constraints apply to their type only: `format`, `minLength` and `maxLength` to strings, `minimum` and
14603
+ * `maximum` to numbers and integers; an `array` takes neither `enum` nor `default`. Bounds are not
14604
+ * reversed; lengths count Unicode code points, as JSON Schema does. A `default` and every `enum` value
14605
+ * satisfy the whole declaration (a `date` is a real calendar date, years 0001-9999; a `date-time` is
14606
+ * RFC 3339 with seconds 00-59, any fraction, `Z` or an offset); strings in a `default` or an `enum` are at
14607
+ * most 1,000 and 200 characters. A REQUIRED parameter with neither `from` nor a `default` must be bound at every
14608
+ * component that uses its query or action.
14609
+ */
14610
+ export interface AppParamSchema {
14611
+ type: 'string' | 'number' | 'integer' | 'boolean' | 'array';
14612
+ /** `array` only. */
14613
+ items?: { type: 'string' | 'number' | 'integer' };
14614
+ format?: 'date' | 'date-time' | 'uuid' | 'email';
14615
+ enum?: Array<string | number>;
14616
+ minimum?: number;
14617
+ maximum?: number;
14618
+ minLength?: number;
14619
+ maxLength?: number;
14620
+ default?: string | number | boolean;
14621
+ description?: string;
14622
+ from?: 'viewer.email' | 'viewer.name' | 'viewer.id' | 'viewer.groups';
14623
+ }
14624
+
14625
+ /** A query's or action's parameters: a JSON Schema object (at most 30 properties). */
14626
+ export interface AppParamsSchema {
14627
+ type: 'object';
14628
+ properties: Record<string, AppParamSchema>;
14629
+ required?: string[];
14630
+ }
14631
+
14632
+ /**
14633
+ * A named read (DES §4 "Query"). Its text is `queries/<id>.<ext>` ({@link AppQueryKind}); the browser
14634
+ * only ever sends the id and parameter values.
14635
+ */
14636
+ export interface AppQuery {
14637
+ id: string;
14638
+ /** The source's name in crew's registry, or `crew` (the built-in source). */
14639
+ source: string;
14640
+ kind: AppQueryKind;
14641
+ description?: string;
14642
+ params?: AppParamsSchema;
14643
+ /** 1-200 declared output columns; the result is projected to exactly these. */
14644
+ output: AppOutputColumn[];
14645
+ /** REST, GraphQL, MCP and crew (never SQL): where the rows are (a JSONPath subset, e.g. `$.items[*]`). */
14646
+ rowPath?: string;
14647
+ /** REST, GraphQL, MCP and crew (never SQL): where "another page exists" is read. */
14648
+ morePath?: string;
14649
+ refresh?: AppRefresh;
14650
+ /** A public app may hold only public queries (DES §9.3). */
14651
+ public?: boolean;
14652
+ }
14653
+
14654
+ /** A value an action sends: a parameter, a literal fixed by the app (a string of at most 1,000 characters), a viewer claim, or crew's `now` or new uuid. */
14655
+ export type AppValue =
14656
+ | AppParamValue
14657
+ | { kind: 'literal'; value: string | number | boolean | null }
14658
+ | { kind: 'viewer'; claim: 'email' | 'name' | 'id' }
14659
+ | { kind: 'crew'; value: 'now' | 'uuid' };
14660
+
14661
+ /** A declared parameter of the action. */
14662
+ export interface AppParamValue {
14663
+ kind: 'param';
14664
+ name: string;
14665
+ }
14666
+
14667
+ /**
14668
+ * An API action (DES §4 "Action"): one REST operation or MCP write tool by its registry tool name, or
14669
+ * one GraphQL mutation document with one root field (at most 16 KB of UTF-8).
14670
+ */
14671
+ export interface AppApiAction {
14672
+ id: string;
14673
+ kind: 'api';
14674
+ source: string;
14675
+ /** The mutation's single root field is the source stage's check. */
14676
+ call: { kind: 'tool'; tool: string } | { kind: 'mutation'; mutation: string };
14677
+ /** At most 50. */
14678
+ args: Record<string, AppValue>;
14679
+ params?: AppParamsSchema;
14680
+ /** Shown on the card as "The app says" (at most 500 characters). */
14681
+ description?: string;
14682
+ }
14683
+
14684
+ /**
14685
+ * A declared Postgres row write (DES §7.4): crew generates the one statement. `table` is
14686
+ * schema-qualified (`support.tickets`, plain identifiers); `key` binds every primary-key column to a
14687
+ * REQUIRED parameter; `set` is every column for an insert (each key column set from its own key
14688
+ * parameter), the changed columns for an update (never a key column), and absent for a delete. Whether
14689
+ * the columns are the table's real ones is the source stage's check.
14690
+ */
14691
+ export interface AppRowWriteAction {
14692
+ id: string;
14693
+ kind: 'row-write';
14694
+ source: string;
14695
+ table: string;
14696
+ op: 'insert' | 'update' | 'delete';
14697
+ /** At most 200 columns. */
14698
+ set?: Record<string, AppValue>;
14699
+ /** 1-16 columns. */
14700
+ key: Record<string, AppParamValue>;
14701
+ params?: AppParamsSchema;
14702
+ description?: string;
14703
+ }
14704
+
14705
+ export type AppAction = AppApiAction | AppRowWriteAction;
14706
+
14707
+ /**
14708
+ * `apps.lock.json`, written by crew's check and committed with the tree it checked; never by an agent or
14709
+ * a person (an edit that names it is refused). It pins what each query and action USES (DES §4):
14710
+ * per source the kind, connection descriptor and its hash; per query the hash of its text and its
14711
+ * contract hash; per action its declaration hash and contract hash; per upload the snapshot digest.
14712
+ * Never a secret, never snapshot bytes. Hashes are lower-case hex SHA-256.
14713
+ */
14714
+ export interface AppLock {
14715
+ schema: 'wicked.apps.lock/v1';
14716
+ sources: Record<string, AppLockSource>;
14717
+ queries: Record<string, AppLockQuery>;
14718
+ actions: Record<string, AppLockAction>;
14719
+ uploads: Record<string, { digest: string }>;
14720
+ }
14721
+
14722
+ export interface AppLockSource {
14723
+ kind: string;
14724
+ /** The non-secret connection descriptor (DES §4 "Source"). */
14725
+ descriptor: Record<string, unknown>;
14726
+ descriptorHash: string;
14727
+ }
14728
+
14729
+ export interface AppLockQuery {
14730
+ source: string;
14731
+ textHash: string;
14732
+ contractHash: string;
14733
+ }
14734
+
14735
+ export interface AppLockAction {
14736
+ source: string;
14737
+ actionHash: string;
14738
+ contractHash: string;
14739
+ }
14740
+
14741
+ // ── Apps: the store and its routes (DES §6.4, §7.6, §9.1) ────────────────────────────────────────
14742
+
14743
+ /**
14744
+ * How a tree was accepted (DES §9.1). Only `you`, `reviewed` and `restored` make a tree part of the
14745
+ * app's ACCEPTED HISTORY (undo, redo and restore move only within it); `published` binds a tree that
14746
+ * already held one of those to a publish manifest.
14747
+ */
14748
+ export type AppReceiptKind = 'you' | 'reviewed' | 'restored' | 'published';
14749
+
14750
+ /**
14751
+ * crew's record that a tree of THIS app passed a check and was accepted. Keyed by (app, tree): the same
14752
+ * tree in another app holds nothing there. Minted only by crew: on a human actor's edit, undo, redo or
14753
+ * restore, on an `apps-turn` run whose review by a distinct seat named the tree, and on a consented
14754
+ * publish. Never stored in the app repo (agent seats mount it).
14755
+ */
14756
+ export interface AppReceipt {
14757
+ kind: AppReceiptKind;
14758
+ tree: string;
14759
+ /** The version that landed with it. */
14760
+ version: number;
14761
+ /** Unix millis. */
14762
+ at: number;
14763
+ /** Who accepted it: the person, or for `reviewed` the actor who launched the turn. */
14764
+ actor: Actor;
14765
+ /**
14766
+ * `restored`: the version restored, and that version's own provenance followed through any chain of
14767
+ * restores (`originKind` is `you` or `reviewed`, `originVersion` the version that first held it).
14768
+ */
14769
+ restoredFrom?: { version: number; originKind: 'you' | 'reviewed'; originVersion: number };
14770
+ /** `reviewed`: the turn's run and the seats that built and reviewed it (always distinct). */
14771
+ review?: { runId: string; builder: string; reviewer: string };
14772
+ /** `published`: the publish run, the manifest digest and each produced artifact's digest. */
14773
+ publish?: { runId: string; manifestDigest: string; artifacts: Record<string, string> };
14774
+ }
14775
+
14776
+ /** Which checks ran before a version landed (DES §6.2 "Checks are staged by what changed"). */
14777
+ export interface AppCheckSummary {
14778
+ /**
14779
+ * `spec`: schema, bindings, link hosts and file confinement only (a layout, prop, theme or label
14780
+ * change). `data`: those plus the read policy and the per-kind source check for what changed.
14781
+ * `full`: those for every query and action (an undo, redo or restore, against today's sources).
14782
+ */
14783
+ level: 'spec' | 'data' | 'full';
14784
+ /** Queries and actions the source stage checked (empty at `spec`). */
14785
+ checked: string[];
14786
+ }
14787
+
14788
+ /** One version: one commit on the app repo's `main`. */
14789
+ export interface AppVersion {
14790
+ /** 1, 2, 3, … per app (the strip's `v14`). */
14791
+ version: number;
14792
+ commit: string;
14793
+ tree: string;
14794
+ /** The version it was made on; `null` for the first. */
14795
+ base: number | null;
14796
+ /** Unix millis. */
14797
+ at: number;
14798
+ /** A person (`human`), a seat that built a turn (`seat`), or crew itself (`crew`, an app's first version). */
14799
+ author: { kind: 'human' | 'seat' | 'crew'; id: string };
14800
+ /** The receipt it landed with; `null` = this tree never runs against a source. */
14801
+ receipt: AppReceipt | null;
14802
+ /** One line for the strip ("Moved the KPIs above the charts"), at most 200 characters. */
14803
+ summary: string;
14804
+ check: AppCheckSummary;
14805
+ }
14806
+
14807
+ /**
14808
+ * The undo pointer (DES §6.4), kept per app by crew so every tab agrees. `states` are the versions of
14809
+ * the accepted history in order and `pointer` indexes the current one. Undo lands `states[pointer-1]`'s
14810
+ * tree as a new version and moves the pointer back; redo lands `states[pointer+1]`'s; a new edit,
14811
+ * landed turn or restore drops everything after the pointer and appends itself.
14812
+ */
14813
+ export interface AppHistory {
14814
+ states: number[];
14815
+ pointer: number;
14816
+ canUndo: boolean;
14817
+ canRedo: boolean;
14818
+ }
14819
+
14820
+ /** A turn in flight: the app is read-only for edits, undo, redo and restore until it ends (`409 turn_in_flight`). */
14821
+ export interface AppTurnState {
14822
+ runId: string;
14823
+ /** Unix millis. */
14824
+ startedAt: number;
14825
+ /** The version the turn builds on. */
14826
+ base: number;
14827
+ /** The actor id that launched it. */
14828
+ actor: string;
14829
+ }
14830
+
14831
+ /** `GET /apps` item. */
14832
+ export interface AppSummary {
14833
+ /** `^[a-z][a-z0-9-]{0,47}$`; `check` and `uploads` are reserved. */
14834
+ slug: string;
14835
+ name: string;
14836
+ projectId: string;
14837
+ /** The app repo's id in crew's repo registry. */
14838
+ repoId: string;
14839
+ /** Unix millis. */
14840
+ createdAt: number;
14841
+ head: { version: number; tree: string; receipt: AppReceiptKind | null };
14842
+ turn: AppTurnState | null;
14843
+ }
14844
+
14845
+ /** `GET /apps` (observer); `?project=<id>` keeps one project's apps. */
14846
+ export interface AppsListResponse {
14847
+ apps: AppSummary[];
14848
+ }
14849
+
14850
+ /** `POST /apps` (operator): a new app, an empty repo in the project with crew's first version. */
14851
+ export interface CreateAppBody {
14852
+ slug: string;
14853
+ /** The app's title; defaults to the slug. */
14854
+ name?: string;
14855
+ projectId: string;
14856
+ }
14857
+
14858
+ /** The app at head: the spec, the lock, every query's text, the versions and the undo pointer. */
14859
+ export interface AppDetail extends AppSummary {
14860
+ spec: AppSpec;
14861
+ lock: AppLock;
14862
+ /** Query id → its text at head. */
14863
+ queryText: Record<string, string>;
14864
+ /** Oldest first. */
14865
+ versions: AppVersion[];
14866
+ history: AppHistory;
14867
+ }
14868
+
14869
+ /** `GET /apps/:app` (observer), and `POST /apps` (201). */
14870
+ export interface AppDetailResponse {
14871
+ app: AppDetail;
14872
+ }
14873
+
14874
+ /**
14875
+ * One change in an edit burst. `spec` replaces `apps.json`; `query-text` writes the text of a query the
14876
+ * resulting spec declares (crew removes the text of a query the spec no longer declares). Nothing else
14877
+ * in the repo can be written: `apps.lock.json` is crew's.
14878
+ */
14879
+ export type AppEditOp = { op: 'spec'; spec: AppSpec } | { op: 'query-text'; query: string; text: string };
14880
+
14881
+ /**
14882
+ * `POST /apps/:app/edits` (HUMAN operator only): one `you` version from one edit burst (1-50 ops). `base`
14883
+ * is the head version the edit was made on: a stale one is `409 stale_base`, and while a turn runs every
14884
+ * edit is `409 turn_in_flight`.
14885
+ */
14886
+ export interface AppEditsBody {
14887
+ base: number;
14888
+ ops: AppEditOp[];
14889
+ /** The strip's line; crew writes one when absent. */
14890
+ summary?: string;
14891
+ }
14892
+
14893
+ /** `POST /apps/:app/undo` and `/redo` (HUMAN operator only). */
14894
+ export interface AppHistoryMoveBody {
14895
+ base: number;
14896
+ }
14897
+
14898
+ /**
14899
+ * `POST /apps/:app/restore` (HUMAN operator only): land `version` (a version number, or a commit of the
14900
+ * app repo) as a new version, only when its tree holds an accepted receipt for this app.
14901
+ */
14902
+ export interface AppRestoreBody {
14903
+ base: number;
14904
+ version: number | string;
14905
+ }
14906
+
14907
+ /** The answer to an edit, undo, redo or restore that landed: the app after it, and the version it made. */
14908
+ export interface AppLandedResponse {
14909
+ app: AppDetail;
14910
+ landed: AppVersion;
14911
+ }
14912
+
14913
+ /**
14914
+ * `POST /apps/:app/turns` (operator): one chat message becomes one governed `apps-turn` run (Build → Check →
14915
+ * Review) on the head version. While it runs the app is read-only for edits, undo, redo and restore, and a second
14916
+ * turn is `409 turn_in_flight`.
14917
+ */
14918
+ export interface AppTurnBody {
14919
+ /** The head version the message was written on; a stale one is `409 stale_base`. */
14920
+ base: number;
14921
+ /** 1-8,000 characters. */
14922
+ message: string;
14923
+ /** Component ids the message is about (the composer's chips), at most 20. */
14924
+ about?: string[];
14925
+ }
14926
+
14927
+ /** `POST /apps/:app/turns` 202. */
14928
+ export interface AppTurnResponse {
14929
+ runId: string;
14930
+ turn: AppTurnState;
14931
+ }
14932
+
14933
+ /** `POST /apps/:app/query` (observer): run one named query of an accepted version (DES §7.2). */
14934
+ export interface AppQueryBody {
14935
+ /** A version number, or a commit of the app repo; its tree must hold an accepted receipt. */
14936
+ version: number | string;
14937
+ query: string;
14938
+ params?: Record<string, unknown>;
14939
+ }
14940
+
14941
+ /** One cell: values are rendered as text by the runtime. */
14942
+ export type AppCell = string | number | boolean | null;
14943
+
14944
+ /** `POST /apps/:app/query` 200 (DES §7.2). `rows` are aligned with `columns`. */
14945
+ export interface AppQueryResponse {
14946
+ columns: AppOutputColumn[];
14947
+ rows: AppCell[][];
14948
+ truncated: boolean;
14949
+ hasMore: boolean;
14950
+ ms: number;
14951
+ /** Unix millis of the cached answer, `null` when it ran now. */
14952
+ cachedAt: number | null;
14953
+ }
14954
+
14955
+ /** One reason a check refused a tree. `path` is a JSON path into `apps.json`, or a repo path. */
14956
+ export interface AppCheckFinding {
14957
+ stage: 'spec' | 'files' | 'policy' | 'source';
14958
+ code: string;
14959
+ path: string;
14960
+ message: string;
14961
+ }
14962
+
14963
+ /**
14964
+ * Every refusal the app routes answer, by `code`:
14965
+ * - 400 `bad_request`; 403 `human_only` (the route needs a human operator: an agent-kind token is refused);
14966
+ * - 404 `unknown_app`, `unknown_version`, `unknown_query`, `unknown_project`;
14967
+ * - 409 `stale_base` (with `head`), `turn_in_flight` (with `turn`), `not_accepted` (the tree holds no
14968
+ * accepted receipt for this app), `nothing_to_undo`, `nothing_to_redo`, `slug_taken` (with
14969
+ * `suggestion`), `source_check_unavailable` (the tree has queries or actions and this daemon cannot
14970
+ * run the source stage yet, so nothing lands);
14971
+ * - 422 `check_failed` (with `findings`), `reserved_slug`;
14972
+ * - 429 `rate_limited` (with `retryAfterMs`: a query queue or a per-viewer rate is full, DES §7.5);
14973
+ * - 501 `not_implemented` (a route a later slice serves; nothing happened);
14974
+ * - 503 `apps_unavailable`.
14975
+ */
14976
+ export interface AppErrorBody {
14977
+ code:
14978
+ | 'bad_request'
14979
+ | 'human_only'
14980
+ | 'unknown_app'
14981
+ | 'unknown_version'
14982
+ | 'unknown_query'
14983
+ | 'unknown_project'
14984
+ | 'stale_base'
14985
+ | 'turn_in_flight'
14986
+ | 'not_accepted'
14987
+ | 'nothing_to_undo'
14988
+ | 'nothing_to_redo'
14989
+ | 'slug_taken'
14990
+ | 'source_check_unavailable'
14991
+ | 'check_failed'
14992
+ | 'reserved_slug'
14993
+ | 'not_implemented'
14994
+ | 'rate_limited'
14995
+ | 'apps_unavailable';
14996
+ error: string;
14997
+ /** `rate_limited`: when to try again (also sent as `Retry-After`). */
14998
+ retryAfterMs?: number;
14999
+ head?: number;
15000
+ turn?: AppTurnState;
15001
+ findings?: AppCheckFinding[];
15002
+ suggestion?: string;
15003
+ }
15004
+
15005
+ /**
15006
+ * {@link HealthCapabilities.apps} (api-types 0.172.0): present when crew serves the app store
15007
+ * (`/api/v1/apps`, list, add, read, edits, undo, redo, restore); each flag is true only once its slice
15008
+ * serves that route for real rather than answering `501`.
15009
+ */
15010
+ export interface AppsCapabilities {
15011
+ /** `POST /apps/:app/query` runs queries (until then it checks the receipt, then answers `501`). */
15012
+ query: boolean;
15013
+ /** `POST /apps/:app/actions/:id/prepare|run|grant`, `GET …/actions/records/:id`. */
15014
+ actions: boolean;
15015
+ /** `POST /apps/:app/turns` launches `apps-turn` runs. */
15016
+ turns: boolean;
15017
+ /** `POST /apps/:app/publish`. */
15018
+ publish: boolean;
15019
+ /** `POST /apps/uploads`. */
15020
+ uploads: boolean;
15021
+ /** The read policy and the per-kind source check run, so a change to queries or actions can land. */
15022
+ sourceCheck: boolean;
15023
+ }
15024
+
15025
+ /**
15026
+ * `POST /repos` body. `create: true` (api-types 0.172.0, DES-APPS-001): crew makes `<apps root>/<name>`
15027
+ * (`~/.wicked/apps` by default), runs `git init` with one empty commit and registers it; no onboarding
15028
+ * run starts (`onboardRunId: null`), and `rootPath`/`gitUrl` are refused beside it. A taken name is
15029
+ * `409 slug_taken` with a `suggestion`.
15030
+ */
15031
+ export interface RegisterRepoBody {
15032
+ name: string;
15033
+ rootPath?: string;
15034
+ gitUrl?: string;
15035
+ create?: true;
15036
+ projectId?: string;
15037
+ channel?: 'studio' | 'cli' | 'api';
15038
+ actor?: string;
15039
+ autoCapture?: boolean;
15040
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wicked-crew-api-types",
3
- "version": "0.167.0",
3
+ "version": "0.172.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",