@shipeasy/openapi 3.1.0 → 3.3.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 (45) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-V4ZESAIF.js → chunk-75Z4SQOM.js} +141 -1
  3. package/dist/chunk-75Z4SQOM.js.map +1 -0
  4. package/dist/{chunk-UV333BIK.js → chunk-ZYK74ZIW.js} +569 -119
  5. package/dist/chunk-ZYK74ZIW.js.map +1 -0
  6. package/dist/{client-yqGYju-M.d.ts → client-DAGqRpF1.d.ts} +2209 -401
  7. package/dist/client.d.ts +1 -1
  8. package/dist/client.js +33 -1
  9. package/dist/custom.d.ts +5 -5
  10. package/dist/custom.js +128 -84
  11. package/dist/custom.js.map +1 -1
  12. package/dist/index.d.ts +2 -2
  13. package/dist/index.js +34 -2
  14. package/dist/index.js.map +1 -1
  15. package/dist/schemas.d.ts +2007 -891
  16. package/dist/schemas.js +179 -1
  17. package/openapi-sdk.json +1769 -0
  18. package/openapi.json +5861 -2597
  19. package/openapi.yaml +2739 -452
  20. package/package.json +6 -4
  21. package/spec/components/responses.yaml +39 -0
  22. package/spec/components/schemas/alert-rules.yaml +405 -7
  23. package/spec/components/schemas/alerts.yaml +9 -7
  24. package/spec/components/schemas/common.yaml +2 -2
  25. package/spec/components/schemas/connectors.yaml +69 -6
  26. package/spec/components/schemas/events.yaml +20 -9
  27. package/spec/components/schemas/gates.yaml +87 -0
  28. package/spec/components/schemas/keys.yaml +32 -2
  29. package/spec/components/schemas/killswitches.yaml +64 -0
  30. package/spec/components/schemas/metrics.yaml +131 -251
  31. package/spec/components/schemas/ops.yaml +555 -22
  32. package/spec/components/schemas/projects.yaml +18 -0
  33. package/spec/components/schemas/tags.yaml +91 -0
  34. package/spec/openapi-sdk.yaml +86 -0
  35. package/spec/openapi.yaml +61 -0
  36. package/spec/paths/alerts.yaml +5 -3
  37. package/spec/paths/gates.yaml +282 -0
  38. package/spec/paths/i18n.yaml +2 -1
  39. package/spec/paths/keys.yaml +79 -0
  40. package/spec/paths/killswitches.yaml +96 -0
  41. package/spec/paths/metrics.yaml +26 -17
  42. package/spec/paths/ops.yaml +566 -10
  43. package/spec/paths/tags.yaml +270 -0
  44. package/dist/chunk-UV333BIK.js.map +0 -1
  45. package/dist/chunk-V4ZESAIF.js.map +0 -1
@@ -3,18 +3,31 @@
3
3
  operationId: listOpsItems
4
4
  summary: List the operational queue
5
5
  description: |-
6
- Returns the unified ops queue (bugs, feature requests, errors, alerts) in work order — highest priority first, oldest first within a priority — so consumers work it top-down. Filter by `type` and/or `status`, and cap with `limit`. Human-gated holding states (items awaiting human sign-off in the dashboard) are never returned by `all`/default status.
6
+ Returns the unified ops queue (bugs, feature requests, errors, alerts, measurement plans) in work order — highest priority first, oldest first within a priority — so consumers work it top-down. Every filter is applied in SQL before paging, and filters combine with AND. Human-gated holding states (items awaiting human sign-off in the dashboard) are never returned by `all`/default status.
7
+
8
+ **Filters:** `type` (one item type), `status` (one lifecycle status, or `unresolved` — everything not yet `resolved`/`wont_fix`), `priority` (one triage priority), `owner` (one person or agent), and `scope=ready` — the items ready to be picked up right now: `open`, with nobody working them (no open run), and either unowned or reopened (sent back to `open` after an earlier run finished).
9
+
10
+ **Paging:** `limit` is the page size and `offset` the number of items to skip. The order is stable, so walk the queue with `offset += limit`; a page shorter than `limit` is the last one.
7
11
 
8
12
  **Use case:** Pull the open queue to triage — e.g. every `bug` still `open` — before working items down one by one.
9
13
  tags:
10
14
  - Ops
11
15
  parameters:
12
16
  - $ref: ../components/parameters.yaml#/ProjectId
17
+ # This enum is the third hand-written copy of the item-type list (the two
18
+ # response schemas in components/schemas/ops.yaml hold the others — there is
19
+ # no OpsItemType component to $ref, since extracting one would retype `type`
20
+ # as a TS enum in every generated client). __tests__/spec.test.ts pins the
21
+ # copies together: it drifted once, and `measure_plan` reached both responses
22
+ # while the filter still rejected it.
13
23
  - name: type
14
24
  in: query
15
25
  required: false
16
- description: Filter by item type (`bug`/`feature_request`/`error`/`alert`), or `all`.
26
+ description: >-
27
+ Filter by item type, or `all` (the default). Every type a returned item
28
+ can carry is filterable, including the auto-filed ones.
17
29
  schema:
30
+ default: all
18
31
  anyOf:
19
32
  - type: string
20
33
  enum:
@@ -22,29 +35,92 @@
22
35
  - feature_request
23
36
  - error
24
37
  - alert
25
- description: Queue item type. `bug` and `feature_request` are user-fileable; `error` and `alert` tickets are auto-filed by the platform.
38
+ - measure_plan
39
+ description: Queue item type. `bug` and `feature_request` are user-fileable; `error`, `alert`, and `measure_plan` tickets are auto-filed by the platform.
26
40
  - type: string
27
41
  const: all
28
42
  - name: status
29
43
  in: query
30
44
  required: false
31
45
  description: >-
32
- Filter by lifecycle status, or `all`. The human-gated holding state
33
- (`pending_approval`) is excluded from `all`/default and returned only
34
- when requested as the exact status.
46
+ Filter by one lifecycle status, `unresolved` for every status that
47
+ is not `resolved` or `wont_fix`, or `all` for every status. Defaults
48
+ to `all` over HTTP; the CLI and MCP tool default to `unresolved`, so
49
+ pass `all` there to include resolved and won't-fix items. The
50
+ human-gated holding state (`pending_approval`) is excluded from
51
+ `unresolved` and `all`, and returned only when requested as the exact
52
+ status.
35
53
  schema:
54
+ default: all
36
55
  anyOf:
37
56
  - $ref: ../components/schemas/ops.yaml#/OpsItemStatus
57
+ - type: string
58
+ const: unresolved
38
59
  - type: string
39
60
  const: all
61
+ # The CLI and MCP send `unresolved` when the caller omits `status`: an
62
+ # agent asking for "the queue" wants the work still to do, not every
63
+ # resolved and won't-fix item ever filed. The HTTP API keeps `all` — the
64
+ # dashboard and the mobile app read the whole queue through it.
65
+ x-cli:
66
+ default: unresolved
67
+ x-mcp:
68
+ default: unresolved
69
+ - name: priority
70
+ in: query
71
+ required: false
72
+ description: >-
73
+ Filter by triage priority, or `all` (the default). Items with no
74
+ priority set are returned only under `all`.
75
+ schema:
76
+ default: all
77
+ anyOf:
78
+ - $ref: ../components/schemas/ops.yaml#/OpsItemPriority
79
+ - type: string
80
+ const: all
81
+ - name: scope
82
+ in: query
83
+ required: false
84
+ description: >-
85
+ `ready` narrows to the items ready to be picked up right now: status
86
+ `open`, no open run (nobody has acked it and is still working), and
87
+ either unowned (no person and no agent) or reopened (sent back to
88
+ `open` after an earlier run finished). Leave `status` unset with it.
89
+ `all` (the default) applies no such narrowing.
90
+ schema:
91
+ type: string
92
+ enum:
93
+ - all
94
+ - ready
95
+ default: all
40
96
  - name: limit
41
97
  in: query
42
98
  required: false
43
- description: Max items to return (1–500).
99
+ description: >-
100
+ Page size — max items to return (1–500). A page shorter than `limit`
101
+ is the last one.
44
102
  schema:
45
103
  type: integer
46
104
  minimum: 1
47
105
  maximum: 500
106
+ default: 200
107
+ # The MCP tool makes the page size a required input (and refuses the call
108
+ # without it) so an agent sizes its own page instead of silently pulling
109
+ # 200 hydrated rows into its context. The HTTP API and the CLI keep the
110
+ # default.
111
+ x-mcp:
112
+ required: true
113
+ - name: offset
114
+ in: query
115
+ required: false
116
+ description: >-
117
+ Number of items to skip before the page starts. Defaults to 0. The
118
+ order is stable, so `offset += limit` walks the queue page by page.
119
+ schema:
120
+ type: integer
121
+ minimum: 0
122
+ maximum: 100000
123
+ default: 0
48
124
  - name: owner
49
125
  in: query
50
126
  required: false
@@ -52,8 +128,7 @@
52
128
  Narrow to items owned by one person OR one agent. Matches a person by
53
129
  `users.id`, email, or display name, and an agent by connector id,
54
130
  display name, or kebab-case handle — e.g. `owner=Claude` or
55
- `owner=alice@acme.dev`. Case-insensitive exact match, applied over the
56
- returned page.
131
+ `owner=alice@acme.dev`. Case-insensitive exact match.
57
132
  schema:
58
133
  type: string
59
134
  responses:
@@ -99,6 +174,30 @@
99
174
  - FORBIDDEN
100
175
  x-cli:
101
176
  name: list
177
+ # The MCP tool description, verbatim — the generator otherwise keeps only the
178
+ # first paragraph (capped), which is too short to say how to filter and page.
179
+ x-mcp:
180
+ description: >-
181
+ List the operational queue (bugs, feature requests, errors, alerts,
182
+ measurement plans) in work order — highest priority first, oldest first
183
+ within a priority.
184
+ PAGING: `limit` (page size, 1–500) is REQUIRED — the call fails without
185
+ it. Start at `offset: 0` and add `limit` to `offset` for the next page;
186
+ a page shorter than `limit` is the last one.
187
+ FILTERS (all optional, combined with AND, applied before paging):
188
+ `type` = bug | feature_request | error | alert | measure_plan;
189
+ `status` = open | in_progress | investigating_by_ai | blocked |
190
+ ready_for_qa | resolved | wont_fix | pending_approval | unresolved |
191
+ all — DEFAULTS TO `unresolved` (omitted = items still being worked:
192
+ open, in_progress, blocked, ready_for_qa…; no resolved or wont_fix
193
+ ones). Pass `status: "all"` to include resolved and wont_fix too
194
+ (pending_approval is only returned when asked for exactly);
195
+ `priority` = critical | high | medium | nice_to_have;
196
+ `owner` = a person (id, email or name) or an agent (connector id, name
197
+ or handle, e.g. "Claude");
198
+ `scope: "ready"` = what to work on now — open items nobody is working
199
+ (no open run) that are unowned or were reopened after an earlier run.
200
+ Use `scope: "ready"` to pick the next item, then `ops_ack` it.
102
201
  post:
103
202
  operationId: createOpsItem
104
203
  summary: File a queue item
@@ -180,6 +279,187 @@
180
279
  summary: File a feature request.
181
280
  preset:
182
281
  type: feature_request
282
+ /ops/bug:
283
+ post:
284
+ operationId: createPublicBug
285
+ summary: File a bug
286
+ description: |-
287
+ Files one bug onto a project's queue, awaiting human approval. This is the **public** intake: it authenticates with a *client* SDK key rather than an admin key, so it can be called from a CLI, an installer script, a devtools overlay, or any shipped code — the same places a client key already lives.
288
+
289
+ Three gates decide whether a ticket is filed, and nothing else the caller sends can widen them:
290
+
291
+ 1. the key is a `client` key carrying the `tickets:public_create` scope,
292
+ 2. the key's project has public ticket creation enabled, and
293
+ 3. the item is filed as `pending_approval` — parked out of the work queue until a human promotes it in the dashboard.
294
+
295
+ The project is the key's own project; there is no `X-Project-Id` to pass and no way to file into someone else's queue. Repeat submissions of the same title dedupe against the open ticket already tracking it, which returns `200` with `deduped: true` instead of filing again.
296
+
297
+ Send a `dedupKey` to own that grain yourself — the same key from a monitor, a job, or a retried install re-triggers its open ticket: the ticket's fields are refreshed from the new payload and a "re-triggered" comment is appended (`200` with `deduped: true, updated: true`), so one recurring failure stays one ticket with its history intact.
298
+
299
+ This endpoint is served by the Shipeasy **edge worker** (`api.shipeasy.ai`), not the admin API — see `servers` below.
300
+
301
+ **Use case:** `shipeasy setup` fails on a customer's machine and self-reports the failure with the user's consent — `{ "title": "Setup failed at Feature installs", "stepsToReproduce": "…", "actualResult": "…" }`.
302
+ tags:
303
+ - Ops
304
+ servers:
305
+ - url: https://api.shipeasy.ai
306
+ description: Shipeasy edge worker (production)
307
+ - url: http://localhost:8787
308
+ description: Local `wrangler dev`
309
+ security:
310
+ - clientSdkKey: []
311
+ requestBody:
312
+ required: true
313
+ content:
314
+ application/json:
315
+ schema:
316
+ $ref: ../components/schemas/ops.yaml#/CreatePublicBugRequest
317
+ examples:
318
+ minimal:
319
+ summary: Just a title
320
+ description: Every other field is optional — the queue triages from `open` at default priority.
321
+ value:
322
+ title: Checkout button misaligned on mobile
323
+ full:
324
+ summary: A full report
325
+ value:
326
+ title: Checkout button misaligned on mobile
327
+ stepsToReproduce: Open the cart on iOS Safari and tap the price row.
328
+ actualResult: The primary CTA overlaps the price.
329
+ expectedResult: The CTA sits below the price with clear spacing.
330
+ priority: high
331
+ reporterEmail: alice@acme.dev
332
+ pageUrl: https://acme.dev/cart
333
+ responses:
334
+ "200":
335
+ description: An open ticket already tracks this report; nothing was filed
336
+ content:
337
+ application/json:
338
+ schema:
339
+ $ref: ../components/schemas/ops.yaml#/CreatePublicTicketResponse
340
+ example:
341
+ number: 7
342
+ deduped: true
343
+ "201":
344
+ description: File a bug
345
+ content:
346
+ application/json:
347
+ schema:
348
+ $ref: ../components/schemas/ops.yaml#/CreatePublicTicketResponse
349
+ example:
350
+ id: fb_01j7w8a1b2c3d4e5f6g7h8i9j0
351
+ number: 7
352
+ "400":
353
+ $ref: ../components/responses.yaml#/BadRequest
354
+ "401":
355
+ $ref: ../components/responses.yaml#/Unauthorized
356
+ "403":
357
+ $ref: ../components/responses.yaml#/Forbidden
358
+ "413":
359
+ $ref: ../components/responses.yaml#/PayloadTooLarge
360
+ "429":
361
+ $ref: ../components/responses.yaml#/TooManyRequests
362
+ "503":
363
+ $ref: ../components/responses.yaml#/ServiceUnavailable
364
+ x-error-codes:
365
+ - BAD_REQUEST
366
+ - UNAUTHORIZED
367
+ - FORBIDDEN
368
+ - VALIDATION
369
+ x-cli:
370
+ hidden: true
371
+ # Spec- and SDK-only. The generated CLI/MCP client resolves ONE base URL
372
+ # and ONE credential for the whole contract, so it cannot honour this
373
+ # operation's own `servers` (the edge worker) or its `clientSdkKey`
374
+ # security — it would POST to the admin host with an admin bearer and 404.
375
+ # Both surfaces are authenticated admin tools anyway: `shipeasy ops bug`
376
+ # and the `ops_bug` MCP tool file through `createOpsItem`, which lands an
377
+ # `open` item with the full field set rather than a `pending_approval` one.
378
+ /ops/feature-request:
379
+ post:
380
+ operationId: createPublicFeatureRequest
381
+ summary: File a feature request
382
+ description: |-
383
+ Files one feature request onto a project's queue, awaiting human approval. The feature-request counterpart to `POST /ops/bug`, with the same three gates: a `client` key carrying `tickets:public_create`, a project that has opted in, and a `pending_approval` state forced server-side.
384
+
385
+ The project is the key's own project; there is no `X-Project-Id` to pass. Repeat submissions of the same title dedupe against the open ticket already tracking it, which returns `200` with `deduped: true` instead of filing again. A `dedupKey` works exactly as on `POST /ops/bug`: a repeat carrying the same key refreshes the open ticket and comments that it was re-triggered.
386
+
387
+ This endpoint is served by the Shipeasy **edge worker** (`api.shipeasy.ai`), not the admin API — see `servers` below.
388
+
389
+ **Use case:** An in-product "request a feature" form posts what the user asked for — `{ "title": "Dark mode", "useCase": "Reduce eye strain at night" }`.
390
+ tags:
391
+ - Ops
392
+ servers:
393
+ - url: https://api.shipeasy.ai
394
+ description: Shipeasy edge worker (production)
395
+ - url: http://localhost:8787
396
+ description: Local `wrangler dev`
397
+ security:
398
+ - clientSdkKey: []
399
+ requestBody:
400
+ required: true
401
+ content:
402
+ application/json:
403
+ schema:
404
+ $ref: ../components/schemas/ops.yaml#/CreatePublicFeatureRequestRequest
405
+ examples:
406
+ minimal:
407
+ summary: Just a title
408
+ value:
409
+ title: Dark mode for the dashboard
410
+ full:
411
+ summary: A full request
412
+ value:
413
+ title: Dark mode for the dashboard
414
+ description: Add a theme toggle that persists per user.
415
+ useCase: Reduce eye strain for users working at night.
416
+ priority: nice_to_have
417
+ reporterEmail: alice@acme.dev
418
+ responses:
419
+ "200":
420
+ description: An open ticket already tracks this report; nothing was filed
421
+ content:
422
+ application/json:
423
+ schema:
424
+ $ref: ../components/schemas/ops.yaml#/CreatePublicTicketResponse
425
+ example:
426
+ number: 7
427
+ deduped: true
428
+ "201":
429
+ description: File a feature request
430
+ content:
431
+ application/json:
432
+ schema:
433
+ $ref: ../components/schemas/ops.yaml#/CreatePublicTicketResponse
434
+ example:
435
+ id: fb_01j7w8a1b2c3d4e5f6g7h8i9j0
436
+ number: 8
437
+ "400":
438
+ $ref: ../components/responses.yaml#/BadRequest
439
+ "401":
440
+ $ref: ../components/responses.yaml#/Unauthorized
441
+ "403":
442
+ $ref: ../components/responses.yaml#/Forbidden
443
+ "413":
444
+ $ref: ../components/responses.yaml#/PayloadTooLarge
445
+ "429":
446
+ $ref: ../components/responses.yaml#/TooManyRequests
447
+ "503":
448
+ $ref: ../components/responses.yaml#/ServiceUnavailable
449
+ x-error-codes:
450
+ - BAD_REQUEST
451
+ - UNAUTHORIZED
452
+ - FORBIDDEN
453
+ - VALIDATION
454
+ x-cli:
455
+ hidden: true
456
+ # Spec- and SDK-only. The generated CLI/MCP client resolves ONE base URL
457
+ # and ONE credential for the whole contract, so it cannot honour this
458
+ # operation's own `servers` (the edge worker) or its `clientSdkKey`
459
+ # security — it would POST to the admin host with an admin bearer and 404.
460
+ # Both surfaces are authenticated admin tools anyway: `shipeasy ops bug`
461
+ # and the `ops_bug` MCP tool file through `createOpsItem`, which lands an
462
+ # `open` item with the full field set rather than a `pending_approval` one.
183
463
  /api/admin/ops/{handle}:
184
464
  get:
185
465
  operationId: getOpsItem
@@ -428,6 +708,11 @@
428
708
  list` and use one of those (or connect the agent under Settings →
429
709
  Triggers).
430
710
 
711
+ **Launching.** When the named agent has a connected trigger and you pass
712
+ no `sessionId`, the ack also fires that trigger — a new provider run
713
+ starts working the item. Pass `launch: false` to only claim the item and
714
+ record the run (you are doing the work yourself), without starting one.
715
+
431
716
  **Completion.** The run closes automatically on the loop's final actions —
432
717
  linking the fixing PR (`link-pr`), an ops-notify escalation, or a
433
718
  completion status change (`ready_for_qa`/`resolved`) — and the dashboard
@@ -549,7 +834,9 @@
549
834
  (`kind: investigated`), a blocking question for the team (`kind:
550
835
  question`), or how to verify the fix (`kind: ready_for_qa` with
551
836
  `qaNotes`). Append-only and create-only, so it is safe for restricted ops
552
- keys.
837
+ keys. For a decision between options you can name, ask a structured
838
+ question instead (`ops questions ask`) — it is answerable in one tap and
839
+ the answer comes back to your run.
553
840
 
554
841
  **Use case:** After working an item, leave a findings write-up (summary,
555
842
  markdown findings, sources inspected, confidence) so the team — and the
@@ -692,6 +979,275 @@
692
979
  positional:
693
980
  - handle
694
981
  - investigationId
982
+ /api/admin/ops/{handle}/questions:
983
+ get:
984
+ operationId: listOpsQuestions
985
+ summary: List an item's questions
986
+ description: |-
987
+ The structured questions asked on a queue item, with their answers —
988
+ newest first (max 100). Pull this to read the answers to questions you
989
+ asked: an answered question reads `status: completed` and carries
990
+ `answer.selected` (the option labels picked) and `answer.other` (the
991
+ free-text "Other" reply, or `null`).
992
+
993
+ **Use case:** You asked with `delivery: poll` from a live session — poll
994
+ this until your questions read `completed`, then continue with the answers.
995
+ tags:
996
+ - Questions
997
+ parameters:
998
+ - $ref: ../components/parameters.yaml#/ProjectId
999
+ - name: handle
1000
+ in: path
1001
+ required: true
1002
+ description: Per-project item number (e.g. `7`) or the full ops item id.
1003
+ schema:
1004
+ $ref: ../components/schemas/common.yaml#/ResourceId
1005
+ - name: status
1006
+ in: query
1007
+ required: false
1008
+ description: Filter by question status, or `all` (the default).
1009
+ schema:
1010
+ default: all
1011
+ anyOf:
1012
+ - $ref: ../components/schemas/ops.yaml#/OpsQuestionStatus
1013
+ - type: string
1014
+ const: all
1015
+ responses:
1016
+ "200":
1017
+ description: List an item's questions
1018
+ content:
1019
+ application/json:
1020
+ schema:
1021
+ $ref: ../components/schemas/ops.yaml#/ListOpsQuestionsResponse
1022
+ "400":
1023
+ $ref: ../components/responses.yaml#/BadRequest
1024
+ "401":
1025
+ $ref: ../components/responses.yaml#/Unauthorized
1026
+ "403":
1027
+ $ref: ../components/responses.yaml#/Forbidden
1028
+ "404":
1029
+ $ref: ../components/responses.yaml#/NotFound
1030
+ "422":
1031
+ $ref: ../components/responses.yaml#/UnprocessableEntity
1032
+ x-error-codes:
1033
+ - BAD_REQUEST
1034
+ - UNAUTHORIZED
1035
+ - FORBIDDEN
1036
+ - NOT_FOUND
1037
+ x-cli:
1038
+ name: list
1039
+ positional:
1040
+ - handle
1041
+ post:
1042
+ operationId: askOpsQuestions
1043
+ summary: Ask the team questions about an item
1044
+ description: |-
1045
+ Ask 1–4 structured questions about a queue item — the way to get a
1046
+ decision from a person when the implementation needs one. Each question
1047
+ is the `AskUserQuestion` shape: a short `header` chip, the full
1048
+ `question`, 2–4 `options` (`label` + `description`), and `multiSelect`
1049
+ (checkboxes instead of radios). The person can always answer "Other" in
1050
+ free text, so never add an "Other" option yourself. The questions render
1051
+ at the very top of the item's detail panel and mark the row in the queue.
1052
+
1053
+ **Delivery.** `resume` re-fires your trigger-connector run on the item
1054
+ once every question it asked is answered, continuing the same run record
1055
+ with the answers in the prompt — stop after asking. `poll` fires nothing;
1056
+ poll `GET .../questions` instead. Omitted, it is `resume` when `runId`
1057
+ names a run launched through a trigger connector, else `poll`.
1058
+
1059
+ **Use case:** Blocked on a product decision (which behaviour to keep,
1060
+ which of two fixes to ship) — ask instead of guessing, then wait for the
1061
+ answer.
1062
+ tags:
1063
+ - Questions
1064
+ parameters:
1065
+ - $ref: ../components/parameters.yaml#/ProjectId
1066
+ - name: handle
1067
+ in: path
1068
+ required: true
1069
+ description: Per-project item number (e.g. `7`) or the full ops item id.
1070
+ schema:
1071
+ $ref: ../components/schemas/common.yaml#/ResourceId
1072
+ requestBody:
1073
+ required: true
1074
+ content:
1075
+ application/json:
1076
+ schema:
1077
+ $ref: ../components/schemas/ops.yaml#/AskOpsQuestionsRequest
1078
+ examples:
1079
+ single:
1080
+ summary: Ask one radio question
1081
+ value:
1082
+ runId: 0d3f4c8e-7a51-4c2e-9b1f-1e2d3c4b5a69
1083
+ questions:
1084
+ - header: Legacy param
1085
+ question: Should the fix keep honouring the legacy `?v=1` query param?
1086
+ multiSelect: false
1087
+ options:
1088
+ - label: Keep it
1089
+ description: Old links keep working; the redirect stays
1090
+ - label: Drop it
1091
+ description: Simpler code; old links land on the default view
1092
+ responses:
1093
+ "200":
1094
+ description: The created questions, in the order asked
1095
+ content:
1096
+ application/json:
1097
+ schema:
1098
+ $ref: ../components/schemas/ops.yaml#/AskOpsQuestionsResponse
1099
+ "400":
1100
+ $ref: ../components/responses.yaml#/BadRequest
1101
+ "401":
1102
+ $ref: ../components/responses.yaml#/Unauthorized
1103
+ "403":
1104
+ $ref: ../components/responses.yaml#/Forbidden
1105
+ "404":
1106
+ $ref: ../components/responses.yaml#/NotFound
1107
+ "422":
1108
+ $ref: ../components/responses.yaml#/UnprocessableEntity
1109
+ x-error-codes:
1110
+ - BAD_REQUEST
1111
+ - UNAUTHORIZED
1112
+ - FORBIDDEN
1113
+ - NOT_FOUND
1114
+ - VALIDATION
1115
+ x-cli:
1116
+ name: ask
1117
+ positional:
1118
+ - handle
1119
+ /api/admin/ops/{handle}/questions/{questionId}/answer:
1120
+ post:
1121
+ operationId: answerOpsQuestion
1122
+ summary: Answer a question
1123
+ description: |-
1124
+ Answer one open question on a queue item — pick option labels in
1125
+ `selected` (exactly one unless the question is `multiSelect`) and/or
1126
+ write a free-text `other`. The question moves to `completed`. When it was
1127
+ the last open question of a `resume` run, that agent is re-fired on the
1128
+ item with every answer. A person's action: never allow-listed for
1129
+ restricted ops keys.
1130
+
1131
+ **Use case:** Unblock an agent from the CLI instead of the dashboard.
1132
+ tags:
1133
+ - Questions
1134
+ parameters:
1135
+ - $ref: ../components/parameters.yaml#/ProjectId
1136
+ - name: handle
1137
+ in: path
1138
+ required: true
1139
+ description: Per-project item number (e.g. `7`) or the full ops item id.
1140
+ schema:
1141
+ $ref: ../components/schemas/common.yaml#/ResourceId
1142
+ - name: questionId
1143
+ in: path
1144
+ required: true
1145
+ description: The question id (from `ask` or `list`).
1146
+ schema:
1147
+ $ref: ../components/schemas/common.yaml#/ResourceId
1148
+ requestBody:
1149
+ required: true
1150
+ content:
1151
+ application/json:
1152
+ schema:
1153
+ $ref: ../components/schemas/ops.yaml#/AnswerOpsQuestionRequest
1154
+ examples:
1155
+ option:
1156
+ summary: Pick an option
1157
+ value:
1158
+ selected:
1159
+ - Keep it
1160
+ other:
1161
+ summary: Answer in free text
1162
+ value:
1163
+ other: Keep it for a release, then drop it
1164
+ responses:
1165
+ "200":
1166
+ description: The answered question
1167
+ content:
1168
+ application/json:
1169
+ schema:
1170
+ $ref: ../components/schemas/ops.yaml#/OpsQuestion
1171
+ "400":
1172
+ $ref: ../components/responses.yaml#/BadRequest
1173
+ "401":
1174
+ $ref: ../components/responses.yaml#/Unauthorized
1175
+ "403":
1176
+ $ref: ../components/responses.yaml#/Forbidden
1177
+ "404":
1178
+ $ref: ../components/responses.yaml#/NotFound
1179
+ "409":
1180
+ $ref: ../components/responses.yaml#/Conflict
1181
+ "422":
1182
+ $ref: ../components/responses.yaml#/UnprocessableEntity
1183
+ x-error-codes:
1184
+ - BAD_REQUEST
1185
+ - UNAUTHORIZED
1186
+ - FORBIDDEN
1187
+ - NOT_FOUND
1188
+ - INVALID_TRANSITION
1189
+ - VALIDATION
1190
+ x-cli:
1191
+ name: answer
1192
+ positional:
1193
+ - handle
1194
+ - questionId
1195
+ /api/admin/ops/{handle}/questions/{questionId}/cancel:
1196
+ post:
1197
+ operationId: cancelOpsQuestion
1198
+ summary: Withdraw a question
1199
+ description: |-
1200
+ Withdraw one open question you asked — it no longer needs an answer
1201
+ (you found it out yourself, or the item moved on). The question moves to
1202
+ `cancelled` and leaves the detail panel. Withdrawing the last open
1203
+ question of a run delivers nothing.
1204
+
1205
+ **Use case:** You asked, then answered it yourself from the code — take
1206
+ it off the person's plate.
1207
+ tags:
1208
+ - Questions
1209
+ parameters:
1210
+ - $ref: ../components/parameters.yaml#/ProjectId
1211
+ - name: handle
1212
+ in: path
1213
+ required: true
1214
+ description: Per-project item number (e.g. `7`) or the full ops item id.
1215
+ schema:
1216
+ $ref: ../components/schemas/common.yaml#/ResourceId
1217
+ - name: questionId
1218
+ in: path
1219
+ required: true
1220
+ description: The question id (from `ask` or `list`).
1221
+ schema:
1222
+ $ref: ../components/schemas/common.yaml#/ResourceId
1223
+ responses:
1224
+ "200":
1225
+ description: The withdrawn question
1226
+ content:
1227
+ application/json:
1228
+ schema:
1229
+ $ref: ../components/schemas/ops.yaml#/OpsQuestion
1230
+ "400":
1231
+ $ref: ../components/responses.yaml#/BadRequest
1232
+ "401":
1233
+ $ref: ../components/responses.yaml#/Unauthorized
1234
+ "403":
1235
+ $ref: ../components/responses.yaml#/Forbidden
1236
+ "404":
1237
+ $ref: ../components/responses.yaml#/NotFound
1238
+ "409":
1239
+ $ref: ../components/responses.yaml#/Conflict
1240
+ x-error-codes:
1241
+ - BAD_REQUEST
1242
+ - UNAUTHORIZED
1243
+ - FORBIDDEN
1244
+ - NOT_FOUND
1245
+ - INVALID_TRANSITION
1246
+ x-cli:
1247
+ name: cancel
1248
+ positional:
1249
+ - handle
1250
+ - questionId
695
1251
  /api/admin/agent-profiles:
696
1252
  get:
697
1253
  operationId: listOpsAgents