@cat-factory/contracts 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/accounts.d.ts +103 -0
- package/dist/accounts.d.ts.map +1 -0
- package/dist/accounts.js +102 -0
- package/dist/accounts.js.map +1 -0
- package/dist/agent-config.d.ts +77 -0
- package/dist/agent-config.d.ts.map +1 -0
- package/dist/agent-config.js +78 -0
- package/dist/agent-config.js.map +1 -0
- package/dist/api-keys.d.ts +44 -0
- package/dist/api-keys.d.ts.map +1 -0
- package/dist/api-keys.js +49 -0
- package/dist/api-keys.js.map +1 -0
- package/dist/auth.d.ts +24 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +15 -0
- package/dist/auth.js.map +1 -0
- package/dist/board-scan.d.ts +89 -0
- package/dist/board-scan.d.ts.map +1 -0
- package/dist/board-scan.js +122 -0
- package/dist/board-scan.js.map +1 -0
- package/dist/bootstrap.d.ts +168 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +148 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/clarity.d.ts +75 -0
- package/dist/clarity.d.ts.map +1 -0
- package/dist/clarity.js +66 -0
- package/dist/clarity.js.map +1 -0
- package/dist/companion.d.ts +32 -0
- package/dist/companion.d.ts.map +1 -0
- package/dist/companion.js +43 -0
- package/dist/companion.js.map +1 -0
- package/dist/consensus.d.ts +195 -0
- package/dist/consensus.d.ts.map +1 -0
- package/dist/consensus.js +164 -0
- package/dist/consensus.js.map +1 -0
- package/dist/documents.d.ts +197 -0
- package/dist/documents.d.ts.map +1 -0
- package/dist/documents.js +161 -0
- package/dist/documents.js.map +1 -0
- package/dist/entities.d.ts +1691 -0
- package/dist/entities.d.ts.map +1 -0
- package/dist/entities.js +853 -0
- package/dist/entities.js.map +1 -0
- package/dist/environments.d.ts +426 -0
- package/dist/environments.d.ts.map +1 -0
- package/dist/environments.js +190 -0
- package/dist/environments.js.map +1 -0
- package/dist/events.d.ts +98 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/fragment-library.d.ts +123 -0
- package/dist/fragment-library.d.ts.map +1 -0
- package/dist/fragment-library.js +88 -0
- package/dist/fragment-library.js.map +1 -0
- package/dist/github.d.ts +215 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js +204 -0
- package/dist/github.js.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/index.js.map +1 -0
- package/dist/iteration-cap.d.ts +17 -0
- package/dist/iteration-cap.d.ts.map +1 -0
- package/dist/iteration-cap.js +25 -0
- package/dist/iteration-cap.js.map +1 -0
- package/dist/localModels.d.ts +54 -0
- package/dist/localModels.d.ts.map +1 -0
- package/dist/localModels.js +79 -0
- package/dist/localModels.js.map +1 -0
- package/dist/merge.d.ts +106 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +129 -0
- package/dist/merge.js.map +1 -0
- package/dist/model-defaults.d.ts +23 -0
- package/dist/model-defaults.d.ts.map +1 -0
- package/dist/model-defaults.js +34 -0
- package/dist/model-defaults.js.map +1 -0
- package/dist/notifications.d.ts +136 -0
- package/dist/notifications.d.ts.map +1 -0
- package/dist/notifications.js +125 -0
- package/dist/notifications.js.map +1 -0
- package/dist/observability.d.ts +271 -0
- package/dist/observability.d.ts.map +1 -0
- package/dist/observability.js +152 -0
- package/dist/observability.js.map +1 -0
- package/dist/personal-subscriptions.d.ts +66 -0
- package/dist/personal-subscriptions.d.ts.map +1 -0
- package/dist/personal-subscriptions.js +70 -0
- package/dist/personal-subscriptions.js.map +1 -0
- package/dist/primitives.d.ts +57 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +66 -0
- package/dist/primitives.js.map +1 -0
- package/dist/provisioning.d.ts +46 -0
- package/dist/provisioning.d.ts.map +1 -0
- package/dist/provisioning.js +107 -0
- package/dist/provisioning.js.map +1 -0
- package/dist/recurring.d.ts +117 -0
- package/dist/recurring.d.ts.map +1 -0
- package/dist/recurring.js +99 -0
- package/dist/recurring.js.map +1 -0
- package/dist/release.d.ts +60 -0
- package/dist/release.d.ts.map +1 -0
- package/dist/release.js +75 -0
- package/dist/release.js.map +1 -0
- package/dist/requests.d.ts +451 -0
- package/dist/requests.d.ts.map +1 -0
- package/dist/requests.js +231 -0
- package/dist/requests.js.map +1 -0
- package/dist/requirements.d.ts +127 -0
- package/dist/requirements.d.ts.map +1 -0
- package/dist/requirements.js +137 -0
- package/dist/requirements.js.map +1 -0
- package/dist/runners.d.ts +387 -0
- package/dist/runners.d.ts.map +1 -0
- package/dist/runners.js +117 -0
- package/dist/runners.js.map +1 -0
- package/dist/sandbox.d.ts +300 -0
- package/dist/sandbox.d.ts.map +1 -0
- package/dist/sandbox.js +243 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/service-fragment-defaults.d.ts +16 -0
- package/dist/service-fragment-defaults.d.ts.map +1 -0
- package/dist/service-fragment-defaults.js +23 -0
- package/dist/service-fragment-defaults.js.map +1 -0
- package/dist/services.d.ts +81 -0
- package/dist/services.d.ts.map +1 -0
- package/dist/services.js +77 -0
- package/dist/services.js.map +1 -0
- package/dist/slack.d.ts +104 -0
- package/dist/slack.d.ts.map +1 -0
- package/dist/slack.js +98 -0
- package/dist/slack.js.map +1 -0
- package/dist/snapshot.d.ts +522 -0
- package/dist/snapshot.d.ts.map +1 -0
- package/dist/snapshot.js +104 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/spec.d.ts +174 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +173 -0
- package/dist/spec.js.map +1 -0
- package/dist/tasks.d.ts +150 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +146 -0
- package/dist/tasks.js.map +1 -0
- package/dist/testing.d.ts +67 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +64 -0
- package/dist/testing.js.map +1 -0
- package/dist/tracker.d.ts +18 -0
- package/dist/tracker.d.ts.map +1 -0
- package/dist/tracker.js +24 -0
- package/dist/tracker.js.map +1 -0
- package/dist/vendor-credentials.d.ts +37 -0
- package/dist/vendor-credentials.d.ts.map +1 -0
- package/dist/vendor-credentials.js +40 -0
- package/dist/vendor-credentials.js.map +1 -0
- package/dist/workspace-settings.d.ts +36 -0
- package/dist/workspace-settings.d.ts.map +1 -0
- package/dist/workspace-settings.js +41 -0
- package/dist/workspace-settings.js.map +1 -0
- package/package.json +31 -0
package/dist/entities.js
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
import { subscriptionVendorSchema } from './vendor-credentials.js';
|
|
3
|
+
import { agentConfigValuesSchema } from './agent-config.js';
|
|
4
|
+
import { testReportSchema } from './testing.js';
|
|
5
|
+
import { consensusStepConfigSchema, stepGatingSchema, taskEstimateSchema } from './consensus.js';
|
|
6
|
+
import { cloudProviderSchema, instanceSizeSchema } from './provisioning.js';
|
|
7
|
+
import { releaseSignalSchema } from './release.js';
|
|
8
|
+
import { agentKindSchema, agentStateSchema, blockLevelSchema, blockStatusSchema, blockTypeSchema, positionSchema, sizeSchema, taskTypeFieldsSchema, taskTypeSchema, } from './primitives.js';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Entity schemas: the single source of truth for the data shapes that travel
|
|
11
|
+
// over the wire. Domain types in @cat-factory/kernel are derived from these, and
|
|
12
|
+
// the worker validates responses against them, so frontend, core and facade can
|
|
13
|
+
// never silently drift apart.
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
/**
|
|
16
|
+
* A lightweight link from a block to the pull request an implementation agent
|
|
17
|
+
* opened for it. Distinct from the richer {@link GitHubPullRequest} projection
|
|
18
|
+
* (synced from GitHub): this is just enough to display the PR on the board and
|
|
19
|
+
* navigate to it. Recorded on a task when its container ("implementer") agent
|
|
20
|
+
* pushes a branch and opens a PR.
|
|
21
|
+
*/
|
|
22
|
+
export const pullRequestRefSchema = v.object({
|
|
23
|
+
/** The PR's web URL, opened when the user clicks through from the board. */
|
|
24
|
+
url: v.string(),
|
|
25
|
+
/** The PR number within the repo, shown as `#<number>` when known. */
|
|
26
|
+
number: v.optional(v.number()),
|
|
27
|
+
/** The head branch the agent pushed its work to, when known. */
|
|
28
|
+
branch: v.optional(v.string()),
|
|
29
|
+
});
|
|
30
|
+
export const blockSchema = v.object({
|
|
31
|
+
id: v.string(),
|
|
32
|
+
title: v.string(),
|
|
33
|
+
type: blockTypeSchema,
|
|
34
|
+
description: v.string(),
|
|
35
|
+
position: positionSchema,
|
|
36
|
+
/**
|
|
37
|
+
* An explicit, user-set pixel size for the block (service frames are resizable
|
|
38
|
+
* by dragging their borders). Absent means the board auto-sizes the frame from
|
|
39
|
+
* its contents; present is the dragged size (the client never shrinks it below
|
|
40
|
+
* the content's natural extent). Only frames carry this today.
|
|
41
|
+
*/
|
|
42
|
+
size: v.optional(sizeSchema),
|
|
43
|
+
status: blockStatusSchema,
|
|
44
|
+
progress: v.number(),
|
|
45
|
+
dependsOn: v.array(v.string()),
|
|
46
|
+
executionId: v.nullable(v.string()),
|
|
47
|
+
level: blockLevelSchema,
|
|
48
|
+
parentId: v.nullable(v.string()),
|
|
49
|
+
confidence: v.optional(v.number()),
|
|
50
|
+
/**
|
|
51
|
+
* The `task-estimator` agent's triage of this task (complexity / risk / impact,
|
|
52
|
+
* each 0..1, + rationale). Written by a `task-estimator` pipeline step once it
|
|
53
|
+
* runs; surfaced in the UI and used to gate consensus steps. Absent until a
|
|
54
|
+
* task-estimator step has run. Only meaningful on `task`-level blocks.
|
|
55
|
+
*/
|
|
56
|
+
estimate: v.optional(v.nullable(taskEstimateSchema)),
|
|
57
|
+
moduleName: v.optional(v.string()),
|
|
58
|
+
/**
|
|
59
|
+
* The kind of work this task represents (feature / bug / document / spike / recurring),
|
|
60
|
+
* chosen by the human at creation. Drives the card's icon/badge, the per-type creation
|
|
61
|
+
* fields, and the per-service running-task limit's optional per-type bucketing. Only
|
|
62
|
+
* meaningful on `task`-level blocks; absent ⇒ treated as `feature`.
|
|
63
|
+
*/
|
|
64
|
+
taskType: v.optional(taskTypeSchema),
|
|
65
|
+
/**
|
|
66
|
+
* Small per-type fields collected on the create-task form (see {@link taskTypeFieldsSchema}),
|
|
67
|
+
* e.g. a bug's severity / repro steps, a spike's time-box. Only meaningful on `task`-level
|
|
68
|
+
* blocks; absent ⇒ none collected.
|
|
69
|
+
*/
|
|
70
|
+
taskTypeFields: v.optional(v.nullable(taskTypeFieldsSchema)),
|
|
71
|
+
/**
|
|
72
|
+
* Ids of curated best-practice prompt fragments selected for this block. Their
|
|
73
|
+
* bodies are composed into the agent system prompt at run time. The catalog
|
|
74
|
+
* itself lives in @cat-factory/prompt-fragments and is served separately.
|
|
75
|
+
*/
|
|
76
|
+
fragmentIds: v.optional(v.array(v.string())),
|
|
77
|
+
/**
|
|
78
|
+
* Service-level (frame-only): ids of the best-practice / guideline prompt fragments
|
|
79
|
+
* selected as this service's programming standards (drawn from the universal pool in
|
|
80
|
+
* @cat-factory/prompt-fragments). At run time the execution engine folds their bodies
|
|
81
|
+
* into the system prompt of every agent under this service that carries the
|
|
82
|
+
* `code-aware` trait. Seeded from the workspace default on new services; absent ⇒ no
|
|
83
|
+
* service-level fragments (only the block's own `fragmentIds` apply).
|
|
84
|
+
*/
|
|
85
|
+
serviceFragmentIds: v.optional(v.array(v.string())),
|
|
86
|
+
/**
|
|
87
|
+
* Id of the LLM model selected for this block from the shared model catalog
|
|
88
|
+
* (see MODEL_CATALOG in @cat-factory/kernel). When set it overrides the agent
|
|
89
|
+
* routing's default model at run time; absent means "use the routing default".
|
|
90
|
+
*/
|
|
91
|
+
modelId: v.optional(v.string()),
|
|
92
|
+
/**
|
|
93
|
+
* Task-level configuration values contributed by the agents in the task's
|
|
94
|
+
* pipeline (see {@link agentConfigValuesSchema}) — a sparse id→value map. Each
|
|
95
|
+
* value is editable until its contributing agent's step starts, then freezes.
|
|
96
|
+
* Used e.g. for the Tester's `tester.environment` (local vs ephemeral) choice.
|
|
97
|
+
* Only meaningful on `task`-level blocks.
|
|
98
|
+
*/
|
|
99
|
+
agentConfig: v.optional(agentConfigValuesSchema),
|
|
100
|
+
/**
|
|
101
|
+
* Service-level (frame-only): path to the service's docker-compose file used to
|
|
102
|
+
* stand up the Tester's local infra dependencies, relative to the repo root
|
|
103
|
+
* (e.g. `docker-compose.yml`). Autodiscovered when the service is added but may
|
|
104
|
+
* be set later. Mutually exclusive with {@link noInfraDependencies}; a Tester
|
|
105
|
+
* pipeline cannot start until one of the two is set.
|
|
106
|
+
*/
|
|
107
|
+
testComposePath: v.optional(v.string()),
|
|
108
|
+
/**
|
|
109
|
+
* Service-level (frame-only): the service has no infra dependencies to stand up,
|
|
110
|
+
* so the Tester spins nothing up. When true {@link testComposePath} is ignored.
|
|
111
|
+
*/
|
|
112
|
+
noInfraDependencies: v.optional(v.boolean()),
|
|
113
|
+
/**
|
|
114
|
+
* Service-level (frame-only): the cloud provider this service's container jobs
|
|
115
|
+
* run on. Absent means the owning account's `defaultCloudProvider`.
|
|
116
|
+
*/
|
|
117
|
+
cloudProvider: v.optional(cloudProviderSchema),
|
|
118
|
+
/**
|
|
119
|
+
* Service-level (frame-only): the abstract instance size for this service's
|
|
120
|
+
* container jobs, resolved to a provider-specific id at dispatch. Absent means
|
|
121
|
+
* the built-in default size.
|
|
122
|
+
*/
|
|
123
|
+
instanceSize: v.optional(instanceSizeSchema),
|
|
124
|
+
/**
|
|
125
|
+
* The pull request the block's implementation ("implementer") agent opened for
|
|
126
|
+
* its work. Set on a task once its container agent pushes a branch and opens a
|
|
127
|
+
* PR; surfaced on the board so the PR can be opened from the selected task.
|
|
128
|
+
*/
|
|
129
|
+
pullRequest: v.optional(pullRequestRefSchema),
|
|
130
|
+
/**
|
|
131
|
+
* Id of the merge threshold preset selected for this task (see
|
|
132
|
+
* {@link mergeThresholdPresetSchema}). Drives the `merger` step's auto-merge
|
|
133
|
+
* decision and the CI-fixer attempt budget. Absent means "use the workspace's
|
|
134
|
+
* default preset".
|
|
135
|
+
*/
|
|
136
|
+
mergePresetId: v.optional(v.string()),
|
|
137
|
+
/**
|
|
138
|
+
* Id of the pipeline chosen for this task at creation (see {@link pipelineSchema}).
|
|
139
|
+
* The task's "Start"/"Run" controls default to it; absent means the user picks a
|
|
140
|
+
* pipeline at run time (the board falls back to the first defined pipeline).
|
|
141
|
+
*/
|
|
142
|
+
pipelineId: v.optional(v.string()),
|
|
143
|
+
/**
|
|
144
|
+
* Internal user id (`usr_*`) of the person who created this block, captured from
|
|
145
|
+
* the authenticated session at creation (tasks today). Drives "notify the task
|
|
146
|
+
* creator" routing for notifications. Absent/null on blocks created before this
|
|
147
|
+
* was recorded, or with auth disabled (local/dev), where there is no user.
|
|
148
|
+
*/
|
|
149
|
+
createdBy: v.optional(v.nullable(v.string())),
|
|
150
|
+
/**
|
|
151
|
+
* Internal user id (`usr_*`) of the account member (a `product` role-holder) made
|
|
152
|
+
* responsible for this task. They are notified when requirement review flags findings.
|
|
153
|
+
* Absent/null when no responsible product person is assigned.
|
|
154
|
+
*/
|
|
155
|
+
responsibleProductUserId: v.optional(v.nullable(v.string())),
|
|
156
|
+
});
|
|
157
|
+
/**
|
|
158
|
+
* A curated best-practice "prompt fragment" (e.g. Node performance, React state
|
|
159
|
+
* management). The catalog is authored in @cat-factory/prompt-fragments and
|
|
160
|
+
* surfaced to the frontend read-only so a user can pick which apply to a block.
|
|
161
|
+
*/
|
|
162
|
+
export const promptFragmentSchema = v.object({
|
|
163
|
+
/** Stable id, e.g. `node.performance`. Selection persists this. */
|
|
164
|
+
id: v.string(),
|
|
165
|
+
/** Semver of the body content, for display and future version pinning. */
|
|
166
|
+
version: v.string(),
|
|
167
|
+
/** Human title shown in the picker, e.g. `Node.js performance`. */
|
|
168
|
+
title: v.string(),
|
|
169
|
+
/** Grouping label for the picker, e.g. `Node`, `React`. */
|
|
170
|
+
category: v.string(),
|
|
171
|
+
/** One-line description shown in the picker. */
|
|
172
|
+
summary: v.string(),
|
|
173
|
+
/** The guidance injected into the agent system prompt. */
|
|
174
|
+
body: v.string(),
|
|
175
|
+
/** Optional hints for filtering which blocks/agents a fragment suits. */
|
|
176
|
+
appliesTo: v.optional(v.object({
|
|
177
|
+
blockTypes: v.optional(v.array(blockTypeSchema)),
|
|
178
|
+
agentKinds: v.optional(v.array(agentKindSchema)),
|
|
179
|
+
})),
|
|
180
|
+
/**
|
|
181
|
+
* Free-form tags used by the relevance selector to decide whether a fragment
|
|
182
|
+
* is pertinent to a given run (e.g. `backend`, `frontend`, `db`). Optional and
|
|
183
|
+
* absent on the built-in catalog tier; managed fragments may set them.
|
|
184
|
+
*/
|
|
185
|
+
tags: v.optional(v.array(v.string())),
|
|
186
|
+
/**
|
|
187
|
+
* Provenance for a fragment sourced from a repo: which {@link FragmentSource}
|
|
188
|
+
* it came from, the file path within that source, and the blob sha last synced
|
|
189
|
+
* (so a "changed?" check is a cheap comparison). Absent for hand-authored
|
|
190
|
+
* fragments and the built-in catalog.
|
|
191
|
+
*/
|
|
192
|
+
source: v.optional(v.object({
|
|
193
|
+
sourceId: v.string(),
|
|
194
|
+
path: v.string(),
|
|
195
|
+
sha: v.string(),
|
|
196
|
+
})),
|
|
197
|
+
});
|
|
198
|
+
/** The full catalog as served by `GET /prompt-fragments`. */
|
|
199
|
+
export const promptFragmentCatalogSchema = v.array(promptFragmentSchema);
|
|
200
|
+
/** Informational list price for a model, surfaced in the picker. */
|
|
201
|
+
export const modelCostSchema = v.object({
|
|
202
|
+
/** List price per 1M input tokens. */
|
|
203
|
+
inputPerMillion: v.number(),
|
|
204
|
+
/** List price per 1M output tokens. */
|
|
205
|
+
outputPerMillion: v.number(),
|
|
206
|
+
/** ISO 4217 currency the prices are expressed in (e.g. `EUR`). */
|
|
207
|
+
currency: v.string(),
|
|
208
|
+
});
|
|
209
|
+
/**
|
|
210
|
+
* A selectable LLM model, resolved to the flavour actually in use for this
|
|
211
|
+
* deployment (`GET /models`). `flavor` is `direct` when the model's own provider
|
|
212
|
+
* API key is configured, `cloudflare` for the Workers AI fallback, or
|
|
213
|
+
* `subscription` for a Claude Code / Codex model run via a stored subscription
|
|
214
|
+
* token. `provider`/`model` are the effective {@link ModelRef} parts the agent
|
|
215
|
+
* will run with; the picker stores only `id`.
|
|
216
|
+
*/
|
|
217
|
+
export const modelOptionSchema = v.object({
|
|
218
|
+
/** Stable id persisted on a block (`Block.modelId`). */
|
|
219
|
+
id: v.string(),
|
|
220
|
+
/** Model-family label, e.g. `Qwen3`. */
|
|
221
|
+
label: v.string(),
|
|
222
|
+
/** One-line description shown in the picker. */
|
|
223
|
+
description: v.string(),
|
|
224
|
+
/** Which flavour is active for this deployment. */
|
|
225
|
+
flavor: v.picklist(['cloudflare', 'direct', 'subscription']),
|
|
226
|
+
/**
|
|
227
|
+
* Whether this model is actually selectable given what the workspace has
|
|
228
|
+
* configured: a direct key for its provider, a subscription token for its vendor,
|
|
229
|
+
* or the opt-in Cloudflare lib enabled. The picker disables an unavailable model.
|
|
230
|
+
*/
|
|
231
|
+
available: v.optional(v.boolean()),
|
|
232
|
+
/** Short provider label for the active flavour, e.g. `Cloudflare`, `DashScope`. */
|
|
233
|
+
providerLabel: v.string(),
|
|
234
|
+
/** Effective provider id the agent runs with. */
|
|
235
|
+
provider: v.string(),
|
|
236
|
+
/** Effective model id within the provider. */
|
|
237
|
+
model: v.string(),
|
|
238
|
+
/**
|
|
239
|
+
* For a `subscription` model, the vendor whose pooled token authenticates it;
|
|
240
|
+
* the frontend enables the option only when the workspace has a token for it.
|
|
241
|
+
*/
|
|
242
|
+
vendor: v.optional(subscriptionVendorSchema),
|
|
243
|
+
/** Informational list price for the model, when known. */
|
|
244
|
+
cost: v.optional(modelCostSchema),
|
|
245
|
+
/** The model's context window at the effective provider, when known. */
|
|
246
|
+
contextTokens: v.optional(v.number()),
|
|
247
|
+
/**
|
|
248
|
+
* True when the effective flavour runs on a flat-rate subscription. Its `cost`
|
|
249
|
+
* is illustrative of quota burn rate only — quota-based usage does NOT draw on
|
|
250
|
+
* the monetary spend budget.
|
|
251
|
+
*/
|
|
252
|
+
quotaBased: v.optional(v.boolean()),
|
|
253
|
+
/**
|
|
254
|
+
* An alternative subscription flavour for a model that ALSO has a Cloudflare /
|
|
255
|
+
* direct base (e.g. GLM-5.2, Kimi). The frontend renders ONLY this flavour when
|
|
256
|
+
* the workspace has a token for `vendor` (hiding the base), and the executor
|
|
257
|
+
* always prefers it at dispatch. Absent for subscription-only models (whose base
|
|
258
|
+
* IS the subscription) and for models with no subscription path.
|
|
259
|
+
*/
|
|
260
|
+
subscription: v.optional(v.object({
|
|
261
|
+
vendor: subscriptionVendorSchema,
|
|
262
|
+
providerLabel: v.string(),
|
|
263
|
+
provider: v.string(),
|
|
264
|
+
model: v.string(),
|
|
265
|
+
cost: v.optional(modelCostSchema),
|
|
266
|
+
contextTokens: v.optional(v.number()),
|
|
267
|
+
})),
|
|
268
|
+
});
|
|
269
|
+
/** The full catalog as served by `GET /models`. */
|
|
270
|
+
export const modelCatalogSchema = v.array(modelOptionSchema);
|
|
271
|
+
export const pipelineSchema = v.object({
|
|
272
|
+
id: v.string(),
|
|
273
|
+
name: v.string(),
|
|
274
|
+
agentKinds: v.array(agentKindSchema),
|
|
275
|
+
/**
|
|
276
|
+
* Per-step human approval gates, parallel to {@link agentKinds}: when
|
|
277
|
+
* `gates[i]` is true the run pauses after step `i` completes so a human can
|
|
278
|
+
* review (and edit) its proposal before the next step runs. Absent / shorter
|
|
279
|
+
* than `agentKinds` means "no gate" for the missing indices, so legacy
|
|
280
|
+
* pipelines run straight through unchanged.
|
|
281
|
+
*/
|
|
282
|
+
gates: v.optional(v.array(v.boolean())),
|
|
283
|
+
/**
|
|
284
|
+
* Per-step companion quality thresholds, parallel to {@link agentKinds}: when step
|
|
285
|
+
* `i` is a companion kind, `thresholds[i]` is the minimum rating (0..1) its review
|
|
286
|
+
* must reach for the run to proceed; below it the preceding producer is re-run, and
|
|
287
|
+
* once the rework budget is spent the step parks for a human (the iteration-cap gate).
|
|
288
|
+
* `null`/absent on a companion step means "use the companion's default threshold";
|
|
289
|
+
* ignored on non-companion steps.
|
|
290
|
+
*/
|
|
291
|
+
thresholds: v.optional(v.array(v.nullable(v.pipe(v.number(), v.minValue(0), v.maxValue(1))))),
|
|
292
|
+
/**
|
|
293
|
+
* Per-step enable flags, parallel to {@link agentKinds}: when `enabled[i]` is
|
|
294
|
+
* explicitly `false` the step is kept in the pipeline (so it can be toggled back
|
|
295
|
+
* on) but SKIPPED at run start — the execution instance is built only from the
|
|
296
|
+
* enabled steps. Absent / shorter than `agentKinds`, or `true`, means the step
|
|
297
|
+
* runs, so legacy pipelines run every step unchanged.
|
|
298
|
+
*/
|
|
299
|
+
enabled: v.optional(v.array(v.boolean())),
|
|
300
|
+
/**
|
|
301
|
+
* Per-step consensus configuration, parallel to {@link agentKinds}: when
|
|
302
|
+
* `consensus[i]` is set and its `enabled` is true AND step `i`'s kind carries a
|
|
303
|
+
* consensus capability trait, the step runs through the multi-model consensus
|
|
304
|
+
* mechanism (specialist panel / debate / ranked voting) instead of a single LLM
|
|
305
|
+
* call — optionally gated on the task estimate (sub-threshold ⇒ standard agent).
|
|
306
|
+
* `null`/absent means "standard single-actor agent" (the default). Copied onto
|
|
307
|
+
* the run's step at start, like {@link gates}. See {@link consensusStepConfigSchema}.
|
|
308
|
+
*/
|
|
309
|
+
consensus: v.optional(v.array(v.nullable(consensusStepConfigSchema))),
|
|
310
|
+
/**
|
|
311
|
+
* Per-step gating, parallel to {@link agentKinds}: when `gating[i]` is set and its
|
|
312
|
+
* `enabled` is true, step `i` runs only if the task estimate meets the threshold
|
|
313
|
+
* (OR across the supplied axes); otherwise it is transparently SKIPPED at runtime.
|
|
314
|
+
* `null`/absent means "always run" (the default). Copied onto the run's step at
|
|
315
|
+
* start, like {@link gates}. A pipeline with any enabled gating requires a
|
|
316
|
+
* `task-estimator` step earlier in the chain. See {@link stepGatingSchema}.
|
|
317
|
+
*/
|
|
318
|
+
gating: v.optional(v.array(v.nullable(stepGatingSchema))),
|
|
319
|
+
/**
|
|
320
|
+
* Free-form organizational labels for the saved-pipeline library (filter/search).
|
|
321
|
+
* Absent ⇒ no labels. Applies to built-in and custom pipelines alike.
|
|
322
|
+
*/
|
|
323
|
+
labels: v.optional(v.array(v.string())),
|
|
324
|
+
/**
|
|
325
|
+
* When true the pipeline is archived: kept but hidden from the default library view
|
|
326
|
+
* (a "show archived" toggle reveals it). Organizational only — an archived built-in
|
|
327
|
+
* is still read-only for structure. Absent / false ⇒ active.
|
|
328
|
+
*/
|
|
329
|
+
archived: v.optional(v.boolean()),
|
|
330
|
+
/**
|
|
331
|
+
* True for the curated built-in catalog pipelines (`seedPipelines()`). Built-ins
|
|
332
|
+
* are read-only templates: they can be cloned (into an editable copy) but not
|
|
333
|
+
* edited in place. Absent / false on user-created and cloned pipelines.
|
|
334
|
+
*/
|
|
335
|
+
builtin: v.optional(v.boolean()),
|
|
336
|
+
});
|
|
337
|
+
export const decisionSchema = v.object({
|
|
338
|
+
id: v.string(),
|
|
339
|
+
question: v.string(),
|
|
340
|
+
options: v.array(v.string()),
|
|
341
|
+
chosen: v.nullable(v.string()),
|
|
342
|
+
});
|
|
343
|
+
/** One entry of a running step's todo list — its label and current status. */
|
|
344
|
+
export const stepSubtaskItemSchema = v.object({
|
|
345
|
+
/** The task's human-readable subject, as the agent wrote it. */
|
|
346
|
+
label: v.string(),
|
|
347
|
+
status: v.picklist(['pending', 'in_progress', 'completed']),
|
|
348
|
+
});
|
|
349
|
+
/**
|
|
350
|
+
* Live subtask counts for a running step, reported by the container agent from
|
|
351
|
+
* the coding tool's own todo list (e.g. "3/8 done, 1 in progress"). Present only
|
|
352
|
+
* while an async job is in flight and the agent maintains a todo list; the board
|
|
353
|
+
* renders it as a finer-grained progress indicator than `progress` alone.
|
|
354
|
+
*
|
|
355
|
+
* `items` carries the individual todo entries (label + status) so a zoomed-in
|
|
356
|
+
* card can render the actual task list, not just the count. It is optional — an
|
|
357
|
+
* older agent/poll that only reported counts, or the simpler `todos[].done`
|
|
358
|
+
* fallback shape, still validates without it.
|
|
359
|
+
*/
|
|
360
|
+
export const stepSubtasksSchema = v.object({
|
|
361
|
+
completed: v.number(),
|
|
362
|
+
inProgress: v.number(),
|
|
363
|
+
total: v.number(),
|
|
364
|
+
items: v.optional(v.array(stepSubtaskItemSchema)),
|
|
365
|
+
});
|
|
366
|
+
/**
|
|
367
|
+
* One GitHub-review-style comment left on a specific block or item of an agent's
|
|
368
|
+
* proposal — either by a human reviewing an approval gate, or by a quality
|
|
369
|
+
* companion (e.g. the Spec Reviewer) grading a structured output. `quotedSource`
|
|
370
|
+
* is the verbatim raw markdown of the block the comment targets (sliced from the
|
|
371
|
+
* proposal by its source line range), so a "request changes" re-run can quote the
|
|
372
|
+
* agent's own text back to it rather than a re-rendered approximation. It is
|
|
373
|
+
* OPTIONAL because a comment may instead anchor to a structured item via
|
|
374
|
+
* {@link anchorId} (e.g. a spec requirement / acceptance-criterion id), where the
|
|
375
|
+
* reviewed output is rendered as discrete items rather than free prose and there is
|
|
376
|
+
* no quoted source range — the shape a companion returns.
|
|
377
|
+
*/
|
|
378
|
+
export const stepReviewCommentSchema = v.object({
|
|
379
|
+
/**
|
|
380
|
+
* Verbatim raw-markdown source of the commented prose block. Optional: a comment
|
|
381
|
+
* may instead anchor to a structured item via {@link anchorId}, where there is no
|
|
382
|
+
* prose source to quote.
|
|
383
|
+
*/
|
|
384
|
+
quotedSource: v.optional(v.string()),
|
|
385
|
+
/**
|
|
386
|
+
* 0-based source line range [start, end) of the commented prose block, for
|
|
387
|
+
* best-effort re-anchoring. Optional: a comment may instead anchor to a structured
|
|
388
|
+
* item via {@link anchorId} (e.g. a spec requirement/acceptance-criterion id), where
|
|
389
|
+
* there is no prose line range.
|
|
390
|
+
*/
|
|
391
|
+
srcStart: v.optional(v.number()),
|
|
392
|
+
srcEnd: v.optional(v.number()),
|
|
393
|
+
/**
|
|
394
|
+
* Stable id of the structured item the comment targets (e.g. a spec
|
|
395
|
+
* requirement/criterion id), when the reviewed output is rendered as structured
|
|
396
|
+
* items rather than free prose. Absent for prose-range comments.
|
|
397
|
+
*/
|
|
398
|
+
anchorId: v.optional(v.string()),
|
|
399
|
+
/** The reviewer's note on this block / item. */
|
|
400
|
+
body: v.string(),
|
|
401
|
+
});
|
|
402
|
+
/**
|
|
403
|
+
* The standardized, stored verdict a quality companion produced for an output it
|
|
404
|
+
* graded — shared by every companion site (the pipeline companion step and the
|
|
405
|
+
* requirements-rework gate). The raw model response is {@link companionAssessmentSchema}
|
|
406
|
+
* (rating + summary + comments); this is the persisted, self-describing record of how
|
|
407
|
+
* that assessment was applied: the `rating`, the `threshold` it was judged against,
|
|
408
|
+
* whether it `passed`, and the `feedback` surfaced to the human / fed into a rework.
|
|
409
|
+
*/
|
|
410
|
+
export const companionVerdictSchema = v.object({
|
|
411
|
+
/** Overall quality of the graded output (0..1, higher = better). */
|
|
412
|
+
rating: v.pipe(v.number(), v.minValue(0), v.maxValue(1)),
|
|
413
|
+
/** The quality bar the rating had to reach to pass. */
|
|
414
|
+
threshold: v.pipe(v.number(), v.minValue(0), v.maxValue(1)),
|
|
415
|
+
/** Whether the rating met the threshold. */
|
|
416
|
+
passed: v.boolean(),
|
|
417
|
+
/** The companion's challenge / justification (its assessment summary). */
|
|
418
|
+
feedback: v.string(),
|
|
419
|
+
});
|
|
420
|
+
/**
|
|
421
|
+
* A human approval gate raised after a step whose pipeline marked it
|
|
422
|
+
* `requiresApproval`. Unlike a {@link Decision} (which an agent raises and which
|
|
423
|
+
* re-runs the same step on resolution), an approval gate fires once the step has
|
|
424
|
+
* already produced its `proposal`; approving advances the run (carrying the —
|
|
425
|
+
* possibly edited — proposal forward as context), requesting changes re-runs the
|
|
426
|
+
* same step with the human's `feedback` (+ per-block `comments`), and rejecting
|
|
427
|
+
* stops the run entirely (a terminal `rejected` failure the board can retry).
|
|
428
|
+
*/
|
|
429
|
+
export const stepApprovalSchema = v.object({
|
|
430
|
+
/** Unique id of this gate; the durable run parks on it like a decision. */
|
|
431
|
+
id: v.string(),
|
|
432
|
+
/** `pending` while awaiting the human; terminal `approved`/`rejected`; `changes_requested` re-runs the step. */
|
|
433
|
+
status: v.picklist(['pending', 'approved', 'changes_requested', 'rejected']),
|
|
434
|
+
/** The agent's output the human is reviewing (editable before approval). */
|
|
435
|
+
proposal: v.string(),
|
|
436
|
+
/** When changes were requested, the human's freeform guidance fed into the re-run. */
|
|
437
|
+
feedback: v.optional(v.string()),
|
|
438
|
+
/** When changes were requested, per-block review comments fed into the re-run. */
|
|
439
|
+
comments: v.optional(v.array(stepReviewCommentSchema)),
|
|
440
|
+
});
|
|
441
|
+
/**
|
|
442
|
+
* The agent flows that produce an "agent run" (a container-backed job whose
|
|
443
|
+
* lifecycle, progress and failure the board surfaces uniformly):
|
|
444
|
+
* - `bootstrap` — a "bootstrap repo" run that scaffolds/adapts a new repo.
|
|
445
|
+
* - `execution` — a task pipeline run that implements a board task.
|
|
446
|
+
*/
|
|
447
|
+
export const agentRunKindSchema = v.picklist(['bootstrap', 'execution']);
|
|
448
|
+
/**
|
|
449
|
+
* How an agent run faulted, so the board can classify the failure (and hint
|
|
450
|
+
* whether a retry is likely to help). The union spans both flows; a given flow
|
|
451
|
+
* only ever produces a subset:
|
|
452
|
+
* - `preflight` — rejected before dispatch (repo missing/not empty, not connected). [bootstrap]
|
|
453
|
+
* - `dispatch` — the container accept-request itself failed (HTTP / network). [bootstrap]
|
|
454
|
+
* - `evicted` — the container vanished mid-run (eviction/crash). Retrying spins a fresh one.
|
|
455
|
+
* - `timeout` — a container watchdog fired (inactivity or max-duration).
|
|
456
|
+
* - `agent` — the agent / git push reported a failure.
|
|
457
|
+
* - `job_failed` — an async container job came back failed. [execution]
|
|
458
|
+
* - `rejected` — a human rejected a gated proposal, stopping the run. [execution]
|
|
459
|
+
* - `cancelled` — the user (or an orphan sweep) explicitly stopped the run.
|
|
460
|
+
* - `unknown` — anything not otherwise classified.
|
|
461
|
+
*/
|
|
462
|
+
export const agentFailureKindSchema = v.picklist([
|
|
463
|
+
'preflight',
|
|
464
|
+
'dispatch',
|
|
465
|
+
'evicted',
|
|
466
|
+
'timeout',
|
|
467
|
+
'agent',
|
|
468
|
+
'job_failed',
|
|
469
|
+
'rejected',
|
|
470
|
+
// A companion agent could not return a parseable quality assessment (truncated /
|
|
471
|
+
// malformed) even after a repair retry, so the run was failed for human attention.
|
|
472
|
+
// (Exhausting the automatic rework budget no longer fails the run — it parks on the
|
|
473
|
+
// companion iteration-cap gate for a human; see `companion.exceeded`.)
|
|
474
|
+
'companion_rejected',
|
|
475
|
+
'cancelled',
|
|
476
|
+
'unknown',
|
|
477
|
+
]);
|
|
478
|
+
/**
|
|
479
|
+
* Structured diagnostics captured when an agent run fails, stored on the run and
|
|
480
|
+
* surfaced on the board so a crash isn't just a one-line message. The container's
|
|
481
|
+
* stdout/stderr can't always be pulled into this record (an evicted container is
|
|
482
|
+
* gone), so for `evicted`/`timeout` failures the `hint` points at where to look.
|
|
483
|
+
*/
|
|
484
|
+
export const agentFailureSchema = v.object({
|
|
485
|
+
kind: agentFailureKindSchema,
|
|
486
|
+
/** Human-readable summary (mirrors the run's `error` for back-compat). */
|
|
487
|
+
message: v.string(),
|
|
488
|
+
/** Extended detail when available (the harness's reason, an HTTP body, …). */
|
|
489
|
+
detail: v.nullable(v.string()),
|
|
490
|
+
/** Where to look next (e.g. "check the container logs for this job id"). */
|
|
491
|
+
hint: v.nullable(v.string()),
|
|
492
|
+
/** Epoch ms the failure was recorded. */
|
|
493
|
+
occurredAt: v.number(),
|
|
494
|
+
/** Last subtask counts seen before the failure, for context (null if none). */
|
|
495
|
+
lastSubtasks: v.nullable(stepSubtasksSchema),
|
|
496
|
+
});
|
|
497
|
+
/**
|
|
498
|
+
* State a polling **gate** step carries (today `ci` and `conflicts`). A gate is
|
|
499
|
+
* special (like a `deployer` step): it is NOT itself an LLM/container agent. It
|
|
500
|
+
* runs a programmatic precheck against a provider (CI check runs / PR mergeability)
|
|
501
|
+
* for the PR head commit and only escalates to a helper container agent (`ci-fixer`
|
|
502
|
+
* / `conflict-resolver`) on a negative verdict, looping until the precheck passes or
|
|
503
|
+
* the attempt budget is spent. Which gate a step is comes from its `agentKind`, so it
|
|
504
|
+
* is not duplicated here. See the engine's `GateDefinition` registry.
|
|
505
|
+
* - `phase: 'checking'` — running the precheck / waiting for the provider.
|
|
506
|
+
* - `phase: 'working'` — a helper agent is in flight (tracked via the step's
|
|
507
|
+
* `jobId`); on completion the gate returns to `checking`.
|
|
508
|
+
*/
|
|
509
|
+
/** One failing check the CI gate's precheck saw, flattened for display. */
|
|
510
|
+
export const gateFailingCheckSchema = v.object({
|
|
511
|
+
name: v.string(),
|
|
512
|
+
/** GitHub conclusion (e.g. `failure`, `timed_out`), or null when not reported. */
|
|
513
|
+
conclusion: v.nullable(v.string()),
|
|
514
|
+
});
|
|
515
|
+
export const gateStepStateSchema = v.object({
|
|
516
|
+
phase: v.picklist(['checking', 'working']),
|
|
517
|
+
/** How many helper-agent attempts have been dispatched so far. */
|
|
518
|
+
attempts: v.number(),
|
|
519
|
+
/** Ceiling on attempts, resolved from the task's merge preset at step start. */
|
|
520
|
+
maxAttempts: v.number(),
|
|
521
|
+
/** The PR head commit being gated, once resolved. */
|
|
522
|
+
headSha: v.optional(v.nullable(v.string())),
|
|
523
|
+
/**
|
|
524
|
+
* The most recent precheck verdict, so the UI can show why the gate is looping
|
|
525
|
+
* (failing → a helper is fixing) vs idle-passing. Set on every probe.
|
|
526
|
+
*/
|
|
527
|
+
lastVerdict: v.optional(v.nullable(v.picklist(['pass', 'pending', 'fail']))),
|
|
528
|
+
/**
|
|
529
|
+
* Human-readable summary of the latest failing precheck (the failing CI checks /
|
|
530
|
+
* the conflict reason) — the conclusion detail that used to be fed only to the
|
|
531
|
+
* helper agent and then discarded. Carried across the helper dispatch so the
|
|
532
|
+
* window keeps showing what is being fixed. Null when the last probe passed.
|
|
533
|
+
*/
|
|
534
|
+
lastFailureSummary: v.optional(v.nullable(v.string())),
|
|
535
|
+
/**
|
|
536
|
+
* Structured failing checks behind {@link lastFailureSummary} for the CI gate, so
|
|
537
|
+
* the UI can list each red check by name + conclusion. Absent for the conflicts
|
|
538
|
+
* gate (GitHub reports no file-level detail) and when the last probe passed.
|
|
539
|
+
*/
|
|
540
|
+
failingChecks: v.optional(v.nullable(v.array(gateFailingCheckSchema))),
|
|
541
|
+
/**
|
|
542
|
+
* Epoch ms of the release marker for a time-windowed gate (post-release-health) — the
|
|
543
|
+
* moment it began watching the deployed release. The gate keeps polling `pending`
|
|
544
|
+
* until this + the preset's watch window has elapsed (then a clean run passes) or a
|
|
545
|
+
* monitor/SLO regresses (then it escalates to the on-call agent). Absent for the
|
|
546
|
+
* CI/conflicts gates.
|
|
547
|
+
*/
|
|
548
|
+
watchSince: v.optional(v.nullable(v.number())),
|
|
549
|
+
/**
|
|
550
|
+
* The watch-window length (minutes) for a time-windowed gate (post-release-health),
|
|
551
|
+
* resolved from the task's merge preset ONCE on first entry (alongside `maxAttempts`)
|
|
552
|
+
* so the probe doesn't re-load the block + re-resolve the preset on every poll. Absent
|
|
553
|
+
* for the CI/conflicts gates.
|
|
554
|
+
*/
|
|
555
|
+
watchWindowMinutes: v.optional(v.nullable(v.number())),
|
|
556
|
+
/**
|
|
557
|
+
* The regressed signals captured when the post-release-health gate escalated to the
|
|
558
|
+
* on-call agent, so the agent's completion handler can build the `release_regression`
|
|
559
|
+
* notification + incident enrichment from the SAME evidence the agent investigated
|
|
560
|
+
* — rather than re-reading Datadog (a third round-trip that could also disagree with
|
|
561
|
+
* what the agent saw if the window moved). Absent for the CI/conflicts gates.
|
|
562
|
+
*/
|
|
563
|
+
regressedSignals: v.optional(v.nullable(v.array(releaseSignalSchema))),
|
|
564
|
+
});
|
|
565
|
+
/**
|
|
566
|
+
* State a `tester` step carries while it runs the Tester → Fixer loop. Unlike `ci`,
|
|
567
|
+
* the gate's own work IS a container job (the Tester); on a withheld greenlight the
|
|
568
|
+
* engine loops a `fixer` container agent and re-tests.
|
|
569
|
+
* - `phase: 'testing'` — a Tester job is in flight (tracked via the step's `jobId`).
|
|
570
|
+
* - `phase: 'fixing'` — a Fixer job is in flight; on completion the step returns to
|
|
571
|
+
* `testing` and a fresh Tester job is dispatched.
|
|
572
|
+
*/
|
|
573
|
+
export const testerStepStateSchema = v.object({
|
|
574
|
+
phase: v.picklist(['testing', 'fixing']),
|
|
575
|
+
/** How many `fixer` attempts have been dispatched so far. */
|
|
576
|
+
attempts: v.number(),
|
|
577
|
+
/** Ceiling on fixer attempts, resolved from the task's merge preset at step start. */
|
|
578
|
+
maxAttempts: v.number(),
|
|
579
|
+
/** The most recent Tester report (what was tested, outcomes, concerns, greenlight). */
|
|
580
|
+
lastReport: v.optional(v.nullable(testReportSchema)),
|
|
581
|
+
});
|
|
582
|
+
/**
|
|
583
|
+
* Per-step LLM observability rollup: a compact aggregate over every model call the
|
|
584
|
+
* step's container made, recorded by the LLM proxy and summed by the engine for the
|
|
585
|
+
* board. It surfaces, at a glance, token usage, how close the step ran to its
|
|
586
|
+
* output-token limit (truncation), the latency split between transport/proxy
|
|
587
|
+
* overhead and actual model execution, and any errors/warnings. The full per-call
|
|
588
|
+
* detail (prompts + responses) is fetched on demand for the drill-down panel.
|
|
589
|
+
* Absent when the observability sink is not wired.
|
|
590
|
+
*/
|
|
591
|
+
export const stepMetricsSchema = v.object({
|
|
592
|
+
/** Number of model calls recorded for this step. */
|
|
593
|
+
calls: v.number(),
|
|
594
|
+
/** Sum of prompt (input) tokens across the step's calls. */
|
|
595
|
+
promptTokens: v.number(),
|
|
596
|
+
/** Sum of completion (output) tokens across the step's calls. */
|
|
597
|
+
completionTokens: v.number(),
|
|
598
|
+
/** Largest single completion the model produced (closest approach to the limit). */
|
|
599
|
+
peakCompletionTokens: v.number(),
|
|
600
|
+
/** The output ceiling in effect (max requested `max_tokens`), or null when unknown. */
|
|
601
|
+
maxOutputTokens: v.nullable(v.number()),
|
|
602
|
+
/** Calls cut short by the output limit (`finish_reason === 'length'`). */
|
|
603
|
+
truncatedCalls: v.number(),
|
|
604
|
+
/** Sum of model execution time (ms) — the "actual prompt/tool execution" slice. */
|
|
605
|
+
upstreamMs: v.number(),
|
|
606
|
+
/** Sum of transport/proxy overhead (ms) — the interim-layer cost. */
|
|
607
|
+
overheadMs: v.number(),
|
|
608
|
+
/** Calls that failed (non-2xx / refused / in-process error). */
|
|
609
|
+
errors: v.number(),
|
|
610
|
+
/** Successful calls that warned (truncated or content-filtered). */
|
|
611
|
+
warnings: v.number(),
|
|
612
|
+
});
|
|
613
|
+
export const pipelineStepSchema = v.object({
|
|
614
|
+
/**
|
|
615
|
+
* Id of the execution run (the {@link executionInstanceSchema} `id`) this step
|
|
616
|
+
* belongs to — surfaced on every step so a lone step in a log line or a detail view
|
|
617
|
+
* can name its run, for easier debugging. A projection that always equals the parent
|
|
618
|
+
* instance's `id`: stamped from the enclosing instance when the run is read or
|
|
619
|
+
* emitted, not persisted independently. Absent only on steps not yet round-tripped.
|
|
620
|
+
*/
|
|
621
|
+
runId: v.optional(v.string()),
|
|
622
|
+
agentKind: agentKindSchema,
|
|
623
|
+
state: agentStateSchema,
|
|
624
|
+
progress: v.number(),
|
|
625
|
+
/** LLM observability rollup for this step; see {@link stepMetricsSchema}. */
|
|
626
|
+
metrics: v.optional(v.nullable(stepMetricsSchema)),
|
|
627
|
+
/**
|
|
628
|
+
* Live gate state while a polling gate step (`ci` / `conflicts`) runs its
|
|
629
|
+
* precheck-or-escalate loop; see {@link gateStepStateSchema}. The gate kind is
|
|
630
|
+
* `agentKind`.
|
|
631
|
+
*/
|
|
632
|
+
gate: v.optional(v.nullable(gateStepStateSchema)),
|
|
633
|
+
/** Live Tester→Fixer loop state while a `tester` step runs/fixes; see {@link testerStepStateSchema}. */
|
|
634
|
+
test: v.optional(v.nullable(testerStepStateSchema)),
|
|
635
|
+
/** Live subtask counts while an async (container) step runs; see {@link stepSubtasksSchema}. */
|
|
636
|
+
subtasks: v.optional(stepSubtasksSchema),
|
|
637
|
+
/**
|
|
638
|
+
* True while a container-backed step is being dispatched and its per-run
|
|
639
|
+
* container is cold-booting — i.e. before the container is up and the agent has
|
|
640
|
+
* begun executing. Set the moment the job is dispatched (the dispatch blocks
|
|
641
|
+
* until the container accepts the job, so it covers the whole boot window) and
|
|
642
|
+
* cleared on the first successful poll, when the container is provably up. Lets
|
|
643
|
+
* the board show an explicit "Spinning up container…" phase instead of a blank
|
|
644
|
+
* "working" state. Only ever set on async (container) steps.
|
|
645
|
+
*/
|
|
646
|
+
startingContainer: v.optional(v.boolean()),
|
|
647
|
+
decision: v.nullable(decisionSchema),
|
|
648
|
+
/**
|
|
649
|
+
* Whether a human approval gate fires after this step completes. Copied from
|
|
650
|
+
* the pipeline's `gates` at run start; absent means no gate.
|
|
651
|
+
*/
|
|
652
|
+
requiresApproval: v.optional(v.boolean()),
|
|
653
|
+
/**
|
|
654
|
+
* The live approval gate for this step (see {@link stepApprovalSchema}). Set
|
|
655
|
+
* once the step's proposal is ready and `requiresApproval` is true; null/absent
|
|
656
|
+
* otherwise.
|
|
657
|
+
*/
|
|
658
|
+
approval: v.optional(v.nullable(stepApprovalSchema)),
|
|
659
|
+
/**
|
|
660
|
+
* Live state of a companion step that reviews a preceding producer step. Set when
|
|
661
|
+
* this step's `agentKind` is a companion kind. `threshold` is the quality bar the
|
|
662
|
+
* companion's latest rating (the last `verdicts` entry) must reach; `attempts`
|
|
663
|
+
* counts only the AUTOMATIC reworks performed, and once it reaches `maxAttempts` the
|
|
664
|
+
* step parks on the iteration-cap gate (`exceeded`) for a human rather than failing.
|
|
665
|
+
* A human "request changes" on the companion's gate also re-runs the producer but does
|
|
666
|
+
* NOT consume `attempts` (only the automatic loop is budgeted). Absent for non-companion steps.
|
|
667
|
+
*/
|
|
668
|
+
companion: v.optional(v.nullable(v.object({
|
|
669
|
+
/** The quality bar (0..1) the latest verdict's rating must reach; seeded from the pipeline. */
|
|
670
|
+
threshold: v.number(),
|
|
671
|
+
/** The automatic rework budget: once `attempts` reaches this the gate parks for a human (`exceeded`). */
|
|
672
|
+
maxAttempts: v.number(),
|
|
673
|
+
/**
|
|
674
|
+
* How many AUTOMATIC reworks the companion has driven so far (the producer is
|
|
675
|
+
* looped back once per failed verdict). Human "request changes" cycles are not
|
|
676
|
+
* counted. Defaults to 0; once it reaches `maxAttempts` the step parks on the
|
|
677
|
+
* iteration-cap gate (`exceeded`) — an "extra round" raises `maxAttempts` by one.
|
|
678
|
+
*/
|
|
679
|
+
attempts: v.optional(v.number(), 0),
|
|
680
|
+
/**
|
|
681
|
+
* One standardized {@link companionVerdictSchema} per grading cycle, in order —
|
|
682
|
+
* the full sequence of correction iterations (the producer is re-run after each
|
|
683
|
+
* rejected verdict), including any human-driven ones. Empty before the first
|
|
684
|
+
* grade; the last entry is the latest.
|
|
685
|
+
*/
|
|
686
|
+
verdicts: v.array(companionVerdictSchema),
|
|
687
|
+
/**
|
|
688
|
+
* Set true when the automatic rework budget (`maxAttempts`) was spent with the
|
|
689
|
+
* rating still below the bar: instead of failing the run, the step parks on its
|
|
690
|
+
* approval gate for a human to resolve via the shared iteration-cap surface
|
|
691
|
+
* (one more round / proceed anyway / stop & reset). Cleared once the human grants
|
|
692
|
+
* an extra round (the loop resumes). Absent until/unless the cap is hit.
|
|
693
|
+
*/
|
|
694
|
+
exceeded: v.optional(v.boolean()),
|
|
695
|
+
}))),
|
|
696
|
+
/**
|
|
697
|
+
* Transient rework feedback carried on a PRODUCER step while it is being re-run by
|
|
698
|
+
* a downstream companion (the analogue of an approval's `changes_requested`
|
|
699
|
+
* feedback for the automatic path). Folded into the agent's revision context on the
|
|
700
|
+
* re-run, then cleared. Absent when no companion rework is in flight.
|
|
701
|
+
*/
|
|
702
|
+
rework: v.optional(v.nullable(v.object({
|
|
703
|
+
/** The producer's previous proposal the companion challenged. */
|
|
704
|
+
previousProposal: v.string(),
|
|
705
|
+
/** The companion's prose feedback driving the rework. */
|
|
706
|
+
feedback: v.string(),
|
|
707
|
+
/** Optional per-item / per-block challenges to address. */
|
|
708
|
+
comments: v.optional(v.array(stepReviewCommentSchema)),
|
|
709
|
+
}))),
|
|
710
|
+
/**
|
|
711
|
+
* Transient incorporation intent carried on a parked `requirements-review` gate step.
|
|
712
|
+
* Set when the human answers the findings and asks to incorporate: the run is signalled
|
|
713
|
+
* to wake and the durable driver, on re-entering the gate, folds the answers into a
|
|
714
|
+
* document and re-reviews it (the LLM work that used to block the HTTP request). Cleared
|
|
715
|
+
* once that async cycle completes. `feedback` is the human's optional "do it differently"
|
|
716
|
+
* direction (a redo). Absent when no incorporation is pending.
|
|
717
|
+
*/
|
|
718
|
+
pendingIncorporation: v.optional(v.nullable(v.object({ feedback: v.optional(v.string()) }))),
|
|
719
|
+
/**
|
|
720
|
+
* Consensus configuration for this step, copied from the pipeline's `consensus`
|
|
721
|
+
* array at run start. Present (with `enabled: true`) when this step should run
|
|
722
|
+
* through the multi-model consensus mechanism; read by the consensus executor
|
|
723
|
+
* (and to decide gating against the block estimate). Absent ⇒ standard agent.
|
|
724
|
+
* See {@link consensusStepConfigSchema}.
|
|
725
|
+
*/
|
|
726
|
+
consensus: v.optional(v.nullable(consensusStepConfigSchema)),
|
|
727
|
+
/**
|
|
728
|
+
* Estimate-based gating for this step, copied from the pipeline's `gating` array at
|
|
729
|
+
* run start. When present (with `enabled: true`) the step is skipped at runtime unless
|
|
730
|
+
* the block's task estimate meets the threshold. Absent ⇒ always run. See
|
|
731
|
+
* {@link stepGatingSchema}.
|
|
732
|
+
*/
|
|
733
|
+
gating: v.optional(v.nullable(stepGatingSchema)),
|
|
734
|
+
/**
|
|
735
|
+
* True when this step was skipped at runtime because its `gating` was not satisfied
|
|
736
|
+
* (the task estimate fell below the threshold). The step's `state` is `done` with no
|
|
737
|
+
* output; the UI renders it as "skipped (gated)". Absent ⇒ the step ran normally.
|
|
738
|
+
*/
|
|
739
|
+
skipped: v.optional(v.boolean()),
|
|
740
|
+
/** Text the agent produced for this step (when LLM execution is enabled). */
|
|
741
|
+
output: v.optional(v.string()),
|
|
742
|
+
/** Identifier of the model that produced `output`, for transparency. */
|
|
743
|
+
model: v.optional(v.string()),
|
|
744
|
+
/**
|
|
745
|
+
* Ids of the prompt-fragment library entries that were folded into this step's
|
|
746
|
+
* system prompt — the manual selection on the block unioned with the relevance
|
|
747
|
+
* selector's pick. Recorded for observability and replay-stability; absent when
|
|
748
|
+
* the fragment-library module is not configured.
|
|
749
|
+
*/
|
|
750
|
+
selectedFragmentIds: v.optional(v.array(v.string())),
|
|
751
|
+
/**
|
|
752
|
+
* Identifier of an in-flight asynchronous agent job (a container run polled by
|
|
753
|
+
* the durable driver). Set while the step is dispatched-but-not-yet-finished so
|
|
754
|
+
* a Workflows replay re-attaches to the running job instead of starting a new
|
|
755
|
+
* one; cleared once the job's result is recorded.
|
|
756
|
+
*/
|
|
757
|
+
jobId: v.optional(v.string()),
|
|
758
|
+
/**
|
|
759
|
+
* Epoch ms the step first began executing (transitioned to `working`). Set once
|
|
760
|
+
* and never overwritten on subsequent state changes, so a re-run/replay keeps the
|
|
761
|
+
* original start. Absent until the step starts.
|
|
762
|
+
*/
|
|
763
|
+
startedAt: v.optional(v.nullable(v.number())),
|
|
764
|
+
/**
|
|
765
|
+
* Epoch ms the step finished (transitioned to `done`). With {@link startedAt}
|
|
766
|
+
* this yields the step's execution duration. Absent until the step completes.
|
|
767
|
+
*/
|
|
768
|
+
finishedAt: v.optional(v.nullable(v.number())),
|
|
769
|
+
/**
|
|
770
|
+
* Epoch ms the step parked on a human (an approval gate, a raised decision, or an
|
|
771
|
+
* iteration-cap gate), freezing its duration clock: while parked, elapsed time stops
|
|
772
|
+
* accruing — the symmetric counterpart of {@link finishedAt}'s terminal freeze, so a
|
|
773
|
+
* step waiting on input is not billed for the human's deliberation. Set once on park,
|
|
774
|
+
* cleared (null) when the step resumes working or finishes. Absent until first parked.
|
|
775
|
+
*/
|
|
776
|
+
pausedAt: v.optional(v.nullable(v.number())),
|
|
777
|
+
/**
|
|
778
|
+
* How many times this step's container was evicted/crashed and recovered by
|
|
779
|
+
* automatically re-dispatching a fresh container (bounded by
|
|
780
|
+
* `MAX_EVICTION_RECOVERIES`). Once spent, a further eviction fails the run as
|
|
781
|
+
* `evicted` rather than looping. Absent/0 until the first eviction.
|
|
782
|
+
*/
|
|
783
|
+
evictionRecoveries: v.optional(v.number()),
|
|
784
|
+
/**
|
|
785
|
+
* How many times this step's container was evicted by *transient infrastructure
|
|
786
|
+
* churn* — an event the runtime facade flags as not-a-crash (e.g. a deploy
|
|
787
|
+
* draining the sandbox) — and recovered by re-dispatching a fresh container.
|
|
788
|
+
* Counted separately from {@link evictionRecoveries} and bounded by a larger
|
|
789
|
+
* `MAX_TRANSIENT_EVICTION_RECOVERIES`, since such churn can recur several times in
|
|
790
|
+
* a short window, unlike a crash. Absent/0 until the first transient eviction.
|
|
791
|
+
*/
|
|
792
|
+
transientEvictionRecoveries: v.optional(v.number()),
|
|
793
|
+
});
|
|
794
|
+
export const executionStatusSchema = v.picklist(['running', 'blocked', 'done', 'paused', 'failed']);
|
|
795
|
+
export const executionInstanceSchema = v.object({
|
|
796
|
+
id: v.string(),
|
|
797
|
+
blockId: v.string(),
|
|
798
|
+
pipelineId: v.string(),
|
|
799
|
+
pipelineName: v.string(),
|
|
800
|
+
steps: v.array(pipelineStepSchema),
|
|
801
|
+
currentStep: v.number(),
|
|
802
|
+
status: executionStatusSchema,
|
|
803
|
+
/**
|
|
804
|
+
* Structured failure diagnostics when `status` is `failed`; absent/null
|
|
805
|
+
* otherwise. Lets a failed task surface the same failure banner + retry as a
|
|
806
|
+
* failed bootstrap (shared {@link agentFailureSchema}).
|
|
807
|
+
*/
|
|
808
|
+
failure: v.optional(v.nullable(agentFailureSchema)),
|
|
809
|
+
/**
|
|
810
|
+
* Internal user id (`usr_*`) of whoever started this run (or retried it). Recorded
|
|
811
|
+
* so the individual-usage restricted mode can use the initiator's OWN personal
|
|
812
|
+
* subscription (e.g. Claude) for the run's steps — a personal credential is never
|
|
813
|
+
* shared, so only its owner's runs may use it. Absent for runs started without a
|
|
814
|
+
* signed-in user (auth-disabled/local dev) and for legacy runs.
|
|
815
|
+
*/
|
|
816
|
+
initiatedBy: v.optional(v.nullable(v.string())),
|
|
817
|
+
});
|
|
818
|
+
export const workspaceSchema = v.object({
|
|
819
|
+
id: v.string(),
|
|
820
|
+
name: v.string(),
|
|
821
|
+
/** Optional free-text description (null when unset). */
|
|
822
|
+
description: v.nullable(v.string()),
|
|
823
|
+
createdAt: v.number(),
|
|
824
|
+
/** The account this board belongs to, or null for a legacy/unscoped board. */
|
|
825
|
+
accountId: v.nullable(v.string()),
|
|
826
|
+
});
|
|
827
|
+
/**
|
|
828
|
+
* The spend safeguard's view of the current billing period. Token usage is
|
|
829
|
+
* tracked per LLM call and priced into a single currency; once `costSpent`
|
|
830
|
+
* reaches `costLimit` the engine pauses runs and the frontend shows a warning.
|
|
831
|
+
* Global across all workspaces (an operator's budget is org-wide), attached to
|
|
832
|
+
* every snapshot by the worker so the client can render the warning anywhere.
|
|
833
|
+
*/
|
|
834
|
+
export const spendStatusSchema = v.object({
|
|
835
|
+
/** Start of the current billing period (epoch ms; calendar month, UTC). */
|
|
836
|
+
periodStart: v.number(),
|
|
837
|
+
/** Input (prompt) tokens consumed this period. */
|
|
838
|
+
inputTokens: v.number(),
|
|
839
|
+
/** Output (completion) tokens produced this period. */
|
|
840
|
+
outputTokens: v.number(),
|
|
841
|
+
/** Estimated cost of this period's usage, in `currency`. */
|
|
842
|
+
costSpent: v.number(),
|
|
843
|
+
/** Configured budget for one period, in `currency`. */
|
|
844
|
+
costLimit: v.number(),
|
|
845
|
+
/** ISO 4217 currency the costs are expressed in (e.g. `EUR`). */
|
|
846
|
+
currency: v.string(),
|
|
847
|
+
/** True once `costSpent >= costLimit`: runs are paused until the period rolls over. */
|
|
848
|
+
exceeded: v.boolean(),
|
|
849
|
+
});
|
|
850
|
+
// The workspace snapshot schema lives in ./snapshot — it references
|
|
851
|
+
// `bootstrapJobSchema` from ./bootstrap, which itself imports from this file, so
|
|
852
|
+
// keeping it here would be a circular import.
|
|
853
|
+
//# sourceMappingURL=entities.js.map
|