@workerkit/core 0.3.1 → 0.3.3
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/CHANGELOG.md +145 -0
- package/README.md +1 -1
- package/dist/descriptors/directory.d.ts.map +1 -1
- package/dist/descriptors/directory.js +7 -6
- package/dist/descriptors/directory.js.map +1 -1
- package/dist/descriptors/index.d.ts +1 -1
- package/dist/descriptors/index.js +1 -1
- package/dist/descriptors/index.js.map +1 -1
- package/dist/descriptors/manage.d.ts.map +1 -1
- package/dist/descriptors/manage.js +150 -39
- package/dist/descriptors/manage.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// The
|
|
1
|
+
// The 79 authenticated fleet-management tools, as pure descriptors. The
|
|
2
2
|
// descriptions ARE the product surface: they are served verbatim to MCP
|
|
3
3
|
// clients (and any future CLI help), so every contract nuance an agent must
|
|
4
4
|
// not get wrong is taught here.
|
|
@@ -7,21 +7,24 @@ import { CREATE, DELETE, READ_ONLY, TRIGGER, UPDATE } from "./types.js";
|
|
|
7
7
|
// The graded-outcome vocabulary, taught wherever a score can be read or written.
|
|
8
8
|
// The null-vs-zero line is the one an agent gets wrong by default: absent means
|
|
9
9
|
// nobody judged the run, and reporting that as a zero would invent a failure.
|
|
10
|
-
const SCORE_NOTE = "
|
|
10
|
+
const SCORE_NOTE = " Grading: selfScore (0-100) is the run's OWN assessment of how much of its task it accomplished — a self-report, not a measurement; ownerScore (0-100) is the human's grade and OVERRIDES it wherever both exist; selfScoreReason explains selfScore in one line. All three are null when the run was never assessed (stateless workers and very short runs are not graded): null means NOT ASSESSED, never zero — never report it as 0 or average nulls into a success rate.";
|
|
11
11
|
const API = "/api/manage/workers";
|
|
12
12
|
// ─── Shared phrasing ────────────────────────────────────────────────────────
|
|
13
|
-
const TOKEN_ID_HINT = "The worker's numeric
|
|
13
|
+
const TOKEN_ID_HINT = "The worker's numeric tokenId, as returned by workers_list. 404 not_found = no such worker on this account (another account's is indistinguishable by design).";
|
|
14
14
|
const RUN_ID_HINT = "The run's UUID, as returned by worker_run (runId), workers_list (currentRunId / lastRun.runId), or worker_runs.";
|
|
15
15
|
// The whole runs family shares this degradation mode; teach it once per tool so
|
|
16
16
|
// agents never retry-loop a 422.
|
|
17
|
-
const RUNTIME_NOTE = "
|
|
17
|
+
const RUNTIME_NOTE = " 422 RUNTIME_NOT_ENABLED = hosted runs are switched off in this environment — a capability, not a transient error: do not retry; every non-run tool keeps working.";
|
|
18
18
|
const UTC_HINT = "ISO 8601 UTC datetime, e.g. '2026-08-01T00:00:00Z'.";
|
|
19
|
+
// What a decision worker's receipt carries beyond an agent run's: taught on the
|
|
20
|
+
// two tools that hand a receipt back.
|
|
21
|
+
const DECISION_BLOCK_NOTE = " A DECISION worker's receipt carries a `decision` block: outcome (one line: how many judged, routed, below the floor, acted on), confidence (share of judged items ABOVE the confidence floor, 0–100 — not a self-score), calls, and decisions[] — one row per judged item: id, answers (question → the model's label or number), confidence, route (rule:N, else, or below_floor: not sure, escalated to the owner), action (call <tool> / escalate / none) and, in live, executed/ok. findings[] are per-question distributions, openQuestions[] the items a person should look at. The block is run CONTENT: it needs readRuns; without it the block comes back withheld (contentWithheld:true) while status, timings and cost still answer.";
|
|
19
22
|
// A scope added to the product AFTER a key was minted never reaches that key —
|
|
20
23
|
// including a key minted with "all", which stored the bitmask of the scopes that
|
|
21
24
|
// existed that day. Taught on key_info and on every tool behind a scope that is
|
|
22
25
|
// younger than the surface, so a 403 there is read as "re-scope the key", not
|
|
23
26
|
// "retry".
|
|
24
|
-
const NEW_SCOPE_NOTE = "
|
|
27
|
+
const NEW_SCOPE_NOTE = " A key minted before this scope existed does not carry it (even one minted with \"all\"): a 403 here is fixed by an account admin re-scoping or re-minting the key at https://workerkit.ai, never by retrying — check key_info's scopes first.";
|
|
25
28
|
// ─── Key ────────────────────────────────────────────────────────────────────
|
|
26
29
|
const getKeyInfo = {
|
|
27
30
|
name: "key_info",
|
|
@@ -37,17 +40,42 @@ const getKeyInfo = {
|
|
|
37
40
|
const listWorkers = {
|
|
38
41
|
name: "workers_list",
|
|
39
42
|
title: "List Workers",
|
|
40
|
-
description: "List
|
|
43
|
+
description: "List the AI workers on this account with live status: title, avatar, status (active|paused|expired), isEnabled, expiresAt, readiness (status ready|blocked with the actionable issues — the same block worker_get carries), deployment (null = this worker is NOT on the hosted runtime and will never run, whatever readiness says; worker_deploy fixes that), lastRun (the newest SETTLED run: runId, outcome succeeded|attention|failed|awaitingInput|skipped|canceled, skipReason, errorCode — in-flight runs never appear here), schedule rollup (total/enabled/nextRunUtc), and live state (isRunning, inFlightRuns, currentRunId — poll run_get with currentRunId to watch it). FILTERS, all optional and ANDed: status, deployed, readiness, q (title substring); an unknown status/readiness value is a 400 invalid_filter, never an empty page. totalWorkers is the count BEFORE filtering: 0 means nothing is installed on this account yet (shortlist a kit with kits_search on the Directory server, kit_install it with deploy:true), non-zero with an empty workers[] means the filter matched nothing. For 'what is wrong with this fleet' call fleet_health instead of scanning this list. Compute elapsed/next-run times against the response's serverTimeUtc, never your own clock. IMPORTANT: lastRun.resultText is the report the worker itself wrote and requires the manager key to hold the readRuns scope IN ADDITION to readWorkers — with readWorkers only it is null, which does NOT mean the run produced no report; check your key's scopes before concluding anything from a null. Scope model for every tool here: a 403 OPERATION_NOT_ALLOWED names the missing scope — the key must be re-minted with broader scopes by an account admin at https://workerkit.ai (do not retry the call).",
|
|
41
44
|
auth: "manager",
|
|
42
45
|
method: "get",
|
|
43
|
-
schema: {
|
|
46
|
+
schema: {
|
|
47
|
+
status: z.enum(["active", "paused", "expired"]).optional().describe("Only workers in this lifecycle state. paused = stopped with worker_set_enabled; expired = the key's expiry passed. Omit for all."),
|
|
48
|
+
deployed: z.boolean().optional().describe("true = only workers on the hosted runtime (the ones that can run); false = only workers without a deployment. Omit for both."),
|
|
49
|
+
readiness: z.enum(["ready", "blocked"]).optional().describe("ready = configuration lets it run hosted; blocked = something actionable is missing (an app connection, an instruction, an identity). Omit for both."),
|
|
50
|
+
q: z.string().max(100).optional().describe("Case-insensitive substring of the worker's title."),
|
|
51
|
+
},
|
|
44
52
|
path: API,
|
|
45
53
|
annotations: READ_ONLY,
|
|
46
54
|
};
|
|
55
|
+
const fleetHealth = {
|
|
56
|
+
name: "fleet_health",
|
|
57
|
+
title: "Fleet Health",
|
|
58
|
+
description: "The fleet's rot in ONE call — the digest to brief from instead of crawling workers_list + worker_get + runs_feed: counts (workers, active, paused, expired, runsInFlight, and one per section: blocked, notDeployed, overdueSchedules, awaitingInput, lastRunAttention) plus five typed sections, each row naming the worker (workerId, tokenId, title) and what to do. blocked[] = active workers whose readiness is blocked, with issues[] (app_not_connected → app_connect on the worker's operator or the human at the dashboard; no_instruction → instruction_set; no_identity → the dashboard). notDeployed[] = active workers with NO hosted deployment: they fire no schedule and refuse worker_run with not_deployed, however ready they look — enabledSchedules > 0 there is a schedule that will never run until worker_deploy; readiness says whether deploying is enough. overdueSchedules[] = deployed workers whose earliest enabled schedule was due more than 15 minutes ago and was never claimed (nextRunUtc, overdueMinutes, deploymentStatus — Paused explains it by itself: deployment_update with resume). awaitingInput[] = runs that ended by ASKING their owner and are still waiting (questionId, runId, askedAtUtc, expiresAtUtc, chainDepth, and question — a 300-char preview that needs readRuns and is NULL without it: that null is a scope gap, not an empty question; run_question has the full text and options, run_answer takes the runId). lastRunAttention[] = active workers whose newest settled run was failed (errorCode), attention (succeeded with friction — denials, tool errors, reported issues) or skipped (skipReason says why; InsufficientCredits means the wallet, TokenDisabled/DailyRunCap/FleetSpendCap name themselves). Paused and expired workers are COUNTED, never listed: stopping a worker is a decision, not rot. Empty sections are the healthy answer. Measure every age against serverTimeUtc. Requires the readWorkers scope.",
|
|
59
|
+
auth: "manager",
|
|
60
|
+
method: "get",
|
|
61
|
+
schema: {},
|
|
62
|
+
path: `${API}/fleet/health`,
|
|
63
|
+
annotations: READ_ONLY,
|
|
64
|
+
};
|
|
65
|
+
const accountUsage = {
|
|
66
|
+
name: "account_usage",
|
|
67
|
+
title: "Account Usage",
|
|
68
|
+
description: "The account's headroom — read it BEFORE an install, a deploy or a run rather than discovering a 402 afterwards. Returns operator (the one kit_install targets by default; limits are per operator), plan (tier free|pro|team|enterprise, complimentaryUntilUtc when the tier is a time-boxed invite grant), workers {used, max, remaining} (remaining 0 → kit_install, worker_clone and re-enabling a stopped worker answer 402 limit_exceeded; worker_delete frees a slot, the plan is the other way out), hostedWorkers {used, max, remaining} (the worker_deploy cap; suspended deployments do not count), wallet {spendableBalanceUsd, promoRemainingUsd, promoExpiresAtUtc, topUpUrl} (hosted runs meter from spendableBalanceUsd — at zero worker_deploy answers 402 wallet_required and scheduled runs are refused with skipReason InsufficientCredits; AN AGENT CANNOT TOP IT UP, hand the human topUpUrl), requests {daily, hourly, monthly} each {limit, used, remaining, resetsAt} (the plan's data-plane windows — the calls workers make to apps; limit 0 means that window is DISABLED on this plan, not exhausted), and spend {todayUsd, thisMonthUsd, reservedInFlightUsd} (settled run spend plus what running runs hold; null while hosted runs are not enabled for the environment). The account's OWN ceilings live on fleet_budget_get, not here. 404 not_found means the account has no active operator. Requires the readWorkers scope.",
|
|
69
|
+
auth: "manager",
|
|
70
|
+
method: "get",
|
|
71
|
+
schema: {},
|
|
72
|
+
path: `${API}/account/usage`,
|
|
73
|
+
annotations: READ_ONLY,
|
|
74
|
+
};
|
|
47
75
|
const getWorker = {
|
|
48
76
|
name: "worker_get",
|
|
49
77
|
title: "Get Worker",
|
|
50
|
-
description: "One worker in full: everything workers_list shows plus timeZoneId, maxRunsPerDay, kit provenance (templateId/slug/name), hasInstruction,
|
|
78
|
+
description: "One worker in full: everything workers_list shows (including readiness: status ready|blocked with actionable issues — worker_inactive, no_identity, app_not_connected incl. candidateProviders, no_instruction) plus timeZoneId, maxRunsPerDay, kit provenance (templateId/slug/name), hasInstruction, modelType ('language' | 'decision' — a decision worker runs a routing table, not an instruction: instruction_get reads it, worker_run runs it, and decisionPendingSetup names any install question still blank), isProtected, jobSentence, apps[] (every app the worker has enabled with connection connected|needs_connection|unknown and the providers serving it — the per-worker view of apps_list; an app at needs_connection is fixed with app_connect on the worker's operator or on the dashboard), deployment (status + modelSlug, or NULL WHEN THE WORKER IS NOT DEPLOYED — an undeployed worker never runs, whatever readiness says, because readiness judges configuration and deployment is what puts it on the runtime; fix with worker_deploy), and 30-day activity (runs by outcome, success rate, cost; null while hosted runs are not enabled for the environment).",
|
|
51
79
|
auth: "manager",
|
|
52
80
|
method: "get",
|
|
53
81
|
schema: {
|
|
@@ -61,17 +89,52 @@ const getWorker = {
|
|
|
61
89
|
const runWorker = {
|
|
62
90
|
name: "worker_run",
|
|
63
91
|
title: "Run Worker Now",
|
|
64
|
-
description: "Trigger the worker to run now (recorded as an API-triggered run, attributed to the manager key's minter). The response is the minted run's RECEIPT — read status before assuming it ran: policy rejections (kill switch, usage window, daily run/spend cap, concurrency, wallet credits, readiness, or a rejected BYOK provider key) come back as HTTP 200 with status Skipped and a skipReason telling the story; that is a normal receipt, NOT an error. Most skips clear on their own (a cap resets, concurrency frees up, credits top up) — but skipReason 'ByokKeyInvalid' / errorCode 'byok_key_invalid' means the ACCOUNT's own model-provider API key was rejected by the provider, arrives with errorDetail null (no prose), and is owner-fixable ONLY: never retry it, report it to the human so they can re-add or remove the key. Structural refusals are 409 (not_deployed, deployment_paused, deployment_suspended, model_unavailable) and 404 for an unknown worker; run-endpoint errors use the {error, message} envelope where error IS the snake_case code. prompt = what to do THIS run; omit it and the worker runs on its standing instructions. Rate limit: 30 run-triggers per minute per ACCOUNT (all of this account's keys and clients share it; other accounts do not affect you). On a 429 back off for the Retry-After — never tighten a loop in response." +
|
|
92
|
+
description: "Trigger the worker to run now (recorded as an API-triggered run, attributed to the manager key's minter). The response is the minted run's RECEIPT — read status before assuming it ran: policy rejections (kill switch, usage window, daily run/spend cap, concurrency, wallet credits, readiness, or a rejected BYOK provider key) come back as HTTP 200 with status Skipped and a skipReason telling the story; that is a normal receipt, NOT an error. Most skips clear on their own (a cap resets, concurrency frees up, credits top up) — but skipReason 'ByokKeyInvalid' / errorCode 'byok_key_invalid' means the ACCOUNT's own model-provider API key was rejected by the provider, arrives with errorDetail null (no prose), and is owner-fixable ONLY: never retry it, report it to the human so they can re-add or remove the key. Structural refusals are 409 (not_deployed, deployment_paused, deployment_suspended, model_unavailable) and 404 for an unknown worker; run-endpoint errors use the {error, message} envelope where error IS the snake_case code. prompt = what to do THIS run; omit it and the worker runs on its standing instructions. Rate limit: 30 run-triggers per minute per ACCOUNT (all of this account's keys and clients share it; other accounts do not affect you). On a 429 back off for the Retry-After — never tighten a loop in response. To fan one prompt across many workers, run_bulk takes up to 20 in one call on a window of its own. Runs in flight per worker are bounded by the account's plan (Free 5, Pro 20, Team 50); at that limit this answers a Skipped receipt with skipReason ConcurrencyLimit. The worker's model type (worker_get → modelType) decides which fields apply: a LANGUAGE worker takes prompt and modelSlug; a DECISION worker runs its own routing table and takes neither (409 not_language_worker), taking preview / sourceArgs / maxItems instead — and sending those to a language worker is 409 not_decision_worker." +
|
|
93
|
+
DECISION_BLOCK_NOTE +
|
|
65
94
|
RUNTIME_NOTE,
|
|
66
95
|
auth: "manager",
|
|
67
96
|
method: "post",
|
|
68
97
|
schema: {
|
|
69
98
|
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
70
|
-
prompt: z.string().max(8000).optional().describe("
|
|
71
|
-
modelSlug: z.string().max(64).optional().describe("
|
|
99
|
+
prompt: z.string().max(8000).optional().describe("Language workers: what to do on THIS run (≤8000 chars). Omit to run the worker's standing instructions unchanged."),
|
|
100
|
+
modelSlug: z.string().max(64).optional().describe("Language workers: one-off model override for this run (≤64 chars). Omit to use the worker's configured model."),
|
|
101
|
+
preview: z.boolean().optional().describe("Decision workers: run THIS run in preview whatever the deployment's mode — every row says what the worker WOULD do and nothing is done. Live is never forced the other way, and it costs the same decision-model calls as a real run."),
|
|
102
|
+
sourceArgs: z.record(z.string(), z.unknown()).optional().describe("Decision workers: narrow what is decided about — values merged over the spec's source arguments for THIS run only (a ticket id, a query, a status). A plain object, no placeholders; the read still runs under the worker's own permissions, so it can only see what the worker can."),
|
|
103
|
+
maxItems: z.number().int().min(1).max(100000).optional().describe("Decision workers: judge at most this many items this run (never above the spec's own cap). ~20 reads as a table; omit for the spec's limit."),
|
|
104
|
+
waitSeconds: z.number().int().min(0).max(55).optional().describe("Hold the call until the run settles and answer the settled receipt (0–55) — worth it on a decision worker, whose runs are seconds. Omit and the answer is the just-minted receipt, followed with run_get. Past the deadline the receipt comes back with a non-terminal status: poll run_get."),
|
|
72
105
|
},
|
|
73
106
|
path: (params) => `${API}/${params.tokenId}/run`,
|
|
74
|
-
bodyBuilder: (params) => ({
|
|
107
|
+
bodyBuilder: (params) => ({
|
|
108
|
+
prompt: params.prompt,
|
|
109
|
+
modelSlug: params.modelSlug,
|
|
110
|
+
preview: params.preview,
|
|
111
|
+
sourceArgs: params.sourceArgs,
|
|
112
|
+
maxItems: params.maxItems,
|
|
113
|
+
waitSeconds: params.waitSeconds,
|
|
114
|
+
}),
|
|
115
|
+
annotations: TRIGGER,
|
|
116
|
+
};
|
|
117
|
+
const runWorkersBulk = {
|
|
118
|
+
name: "run_bulk",
|
|
119
|
+
title: "Run Workers in Bulk",
|
|
120
|
+
description: "Run several workers now in ONE call — a prompt fanned out across a fleet without spending the single trigger's 30/min budget one worker at a time. Up to 20 workers per call, each named by workerId (preferred) or its deprecated tokenId (the GUID wins when both are sent), each with its own optional prompt and modelSlug. IT IS NOT ATOMIC AND IT DOES NOT STOP: every worker passes the mint gauntlet on its own, so an unknown id, a missing deployment or a cap already hit is reported on THAT item (success false, errorCode, error) and the loop goes on to the next — a stale id in a fan-out never discards the nineteen runs that were fine. A SKIPPED RECEIPT IS A SUCCESS HERE: a run was minted and its status/skipReason say why it did not start, so read items[] and never the status code or minted alone. Each successful item carries runId, status, skipReason and errorCode; run_events and run_get take the runId from there. Recorded as API-triggered runs, attributed to the manager key's minter. Own window: 2 calls per minute and 20 per hour per ACCOUNT, on top of the surface windows. Requires the runWorkers scope." +
|
|
121
|
+
RUNTIME_NOTE,
|
|
122
|
+
auth: "manager",
|
|
123
|
+
method: "post",
|
|
124
|
+
schema: {
|
|
125
|
+
workers: z
|
|
126
|
+
.array(z.object({
|
|
127
|
+
workerId: z.string().uuid().optional().describe("The worker's stable public id, as every response that names a worker returns it (preferred). Send this or tokenId."),
|
|
128
|
+
tokenId: z.number().int().min(1).optional().describe("The worker's deprecated numeric token ID. workerId wins when both are sent."),
|
|
129
|
+
prompt: z.string().max(8000).optional().describe("What THIS worker should do on this run (≤8000 chars). Omit to run its standing instructions unchanged."),
|
|
130
|
+
modelSlug: z.string().max(64).optional().describe("One-off model override for this worker's run (≤64 chars). Omit to use its configured model."),
|
|
131
|
+
}))
|
|
132
|
+
.min(1)
|
|
133
|
+
.max(20)
|
|
134
|
+
.describe("One entry per worker to run, 1–20 of them; each needs a workerId or a tokenId."),
|
|
135
|
+
},
|
|
136
|
+
path: `${API}/runs/bulk`,
|
|
137
|
+
bodyBuilder: (params) => ({ workers: params.workers }),
|
|
75
138
|
annotations: TRIGGER,
|
|
76
139
|
};
|
|
77
140
|
// ─── Fleet-wide run reads ───────────────────────────────────────────────────
|
|
@@ -81,7 +144,7 @@ const runWorker = {
|
|
|
81
144
|
const runsFeed = {
|
|
82
145
|
name: "runs_feed",
|
|
83
146
|
title: "Account Run Feed",
|
|
84
|
-
description: "Every worker's runs in ONE account-wide feed, newest first — THE fleet-watching call. When you are minding many workers, call this instead of looping workers_list or worker_runs per worker. Each row is a scan line: runId, workerId, tokenTitle, status, outcome (running|succeeded|attention|failed|skipped|canceled — 'attention' is a SUCCEEDED run that hit friction, i.e. firewall denials, tool errors or reported issues, so report it apart from a clean success), skipReason, errorCode, triggerKind, scheduleTitle, timings, the friction counts (toolCalls, firewallDenials, toolErrors, issueCount, hasBlockerIssue), and one line of what the run did in summary + summarySource ('distilled' = a condensed record of what the run ACTUALLY did; 'report' = the worker's OWN closing account; 'error'/'skipped'/'activity' = the machine's own words — these are not equally reliable, so say which one you are quoting). Carries NO cost and NO token counts by design: read those from run_get on the one run that matters. Cursor-paged, never page-numbered — scroll by passing the previous response's nextCursor back as cursor (hasMore says whether another page exists), and re-fetch with NO cursor to refresh the head; there is deliberately no total count. A cursor we did not issue is a 400 invalid_cursor — drop it and reload the head. An unknown status is a 400 invalid_status, never a silently ignored filter. Requires the readRuns scope." +
|
|
147
|
+
description: "Every worker's runs in ONE account-wide feed, newest first — THE fleet-watching call. When you are minding many workers, call this instead of looping workers_list or worker_runs per worker. Each row is a scan line: runId, workerId, tokenTitle, status, outcome (running|succeeded|attention|failed|awaitingInput|skipped|canceled — 'attention' is a SUCCEEDED run that hit friction, i.e. firewall denials, tool errors or reported issues, so report it apart from a clean success; 'awaitingInput' is a run that ended by asking its owner a question — waiting on a person, not broken: run_question reads it, run_answer answers it, and fleet_health lists every open one), skipReason, errorCode, triggerKind, scheduleTitle, timings, the friction counts (toolCalls, firewallDenials, toolErrors, issueCount, hasBlockerIssue), and one line of what the run did in summary + summarySource ('distilled' = a condensed record of what the run ACTUALLY did; 'report' = the worker's OWN closing account; 'error'/'skipped'/'activity' = the machine's own words — these are not equally reliable, so say which one you are quoting). Carries NO cost and NO token counts by design: read those from run_get on the one run that matters. Cursor-paged, never page-numbered — scroll by passing the previous response's nextCursor back as cursor (hasMore says whether another page exists), and re-fetch with NO cursor to refresh the head; there is deliberately no total count. A cursor we did not issue is a 400 invalid_cursor — drop it and reload the head. An unknown status is a 400 invalid_status, never a silently ignored filter. Requires the readRuns scope." +
|
|
85
148
|
RUNTIME_NOTE,
|
|
86
149
|
auth: "manager",
|
|
87
150
|
method: "get",
|
|
@@ -92,9 +155,9 @@ const runsFeed = {
|
|
|
92
155
|
// two composite values.
|
|
93
156
|
status: z.enum([
|
|
94
157
|
"pending", "dispatched", "running", "succeeded", "failed",
|
|
95
|
-
"timedOut", "budgetExceeded", "skipped", "canceled",
|
|
158
|
+
"timedOut", "budgetExceeded", "awaitingInput", "skipped", "canceled",
|
|
96
159
|
"settled", "all",
|
|
97
|
-
]).optional().describe("Filter to one run status. 'settled' is the composite for every terminal outcome (everything not in flight) — use it for a logbook, and fleet_pulse for what is running. 'all' is an explicit no-op. Omit for all runs."),
|
|
160
|
+
]).optional().describe("Filter to one run status. 'awaitingInput' = runs waiting on their owner's answer (terminal for money, open for the person). 'settled' is the composite for every terminal outcome (everything not in flight, awaitingInput included) — use it for a logbook, and fleet_pulse for what is running. 'all' is an explicit no-op. Omit for all runs."),
|
|
98
161
|
cursor: z.string().optional().describe("Opaque cursor from the previous response's nextCursor — fetches the next (older) page. Omit to read the head of the feed."),
|
|
99
162
|
limit: z.number().int().min(1).max(100).default(20).describe("Rows per page (1–100)."),
|
|
100
163
|
},
|
|
@@ -127,8 +190,8 @@ const listRuns = {
|
|
|
127
190
|
// read as the real subset. These are the WorkerRunStatus names verbatim.
|
|
128
191
|
status: z.enum([
|
|
129
192
|
"pending", "dispatched", "running", "succeeded", "failed",
|
|
130
|
-
"timedOut", "budgetExceeded", "skipped", "canceled",
|
|
131
|
-
]).optional().describe("Filter by run status. Omit for all runs."),
|
|
193
|
+
"timedOut", "budgetExceeded", "awaitingInput", "skipped", "canceled",
|
|
194
|
+
]).optional().describe("Filter by run status ('awaitingInput' = waiting on the owner's answer). Omit for all runs."),
|
|
132
195
|
page: z.number().int().min(1).default(1).describe("1-based page number."),
|
|
133
196
|
pageSize: z.number().int().min(1).max(100).default(20).describe("Results per page (max 100)."),
|
|
134
197
|
fromUtc: z.string().optional().describe(`Only runs started at/after this time. ${UTC_HINT}`),
|
|
@@ -146,6 +209,7 @@ const getRun = {
|
|
|
146
209
|
title: "Get Run",
|
|
147
210
|
description: "One run's full receipt: status, trigger, timings, token/cost metering (including billingMode 'Platform'|'Byok' and byokFeeUsd — on a Byok run modelCostUsd is the provider's list price and was NOT charged to the wallet), finalDigest (the worker's report), runPrompt/runContext, liveness (live|stalled) and lastActivity while in flight, errorCode/errorDetail, issues, and the grades (selfScore, selfScoreReason, ownerScore — set or change ownerScore with run_score). Note: footprint, turnsTimeline, issues, and priceSnapshot arrive as raw JSON strings — parse them before reasoning over their contents. Requires readRuns." +
|
|
148
211
|
SCORE_NOTE +
|
|
212
|
+
DECISION_BLOCK_NOTE +
|
|
149
213
|
RUNTIME_NOTE,
|
|
150
214
|
auth: "manager",
|
|
151
215
|
method: "get",
|
|
@@ -175,6 +239,20 @@ const getRunEvents = {
|
|
|
175
239
|
},
|
|
176
240
|
annotations: READ_ONLY,
|
|
177
241
|
};
|
|
242
|
+
const getRunTranscript = {
|
|
243
|
+
name: "run_transcript",
|
|
244
|
+
title: "Get Run Transcript",
|
|
245
|
+
description: "The run's stored LLM process log — the raw record behind the digest: every model turn and tool call, as the worker made them. EXPECT 404 no_transcript MORE OFTEN THAN 200: transcripts are OFF by default and kept for 7 days, so this answers only when the worker's deployment had transcriptRetention on before the run happened and the window has not elapsed; a worker installed from a protected kit never stores one. The run receipt's transcriptAvailable (run_get) says whether this returns 200 — branch on that rather than calling this to find out. For WHAT a run did, the digest on the receipt is the intended handoff; reach for the transcript when the digest is not enough: a run that misbehaved, a tool call to inspect, a denial to trace. Up to 1 MB. Requires the readRuns scope." +
|
|
246
|
+
RUNTIME_NOTE,
|
|
247
|
+
auth: "manager",
|
|
248
|
+
method: "get",
|
|
249
|
+
schema: {
|
|
250
|
+
runId: z.string().uuid().describe(RUN_ID_HINT),
|
|
251
|
+
},
|
|
252
|
+
path: (params) => `${API}/runs/${params.runId}/transcript`,
|
|
253
|
+
paramFilter: () => ({}),
|
|
254
|
+
annotations: READ_ONLY,
|
|
255
|
+
};
|
|
178
256
|
// ─── Two-way runs (ask / answer) ────────────────────────────────────────────
|
|
179
257
|
// A run that needs something from its owner ENDS by asking (status AwaitingInput)
|
|
180
258
|
// rather than blocking on a human while holding a request slot and a wallet
|
|
@@ -334,12 +412,17 @@ const SCHEDULE_TYPE_HINT = "EveryNMinutes (intervalValue 1–1440), EveryNHours
|
|
|
334
412
|
// is in `feature`, while `error` holds the literal string "feature_gated" — the
|
|
335
413
|
// one place on this surface where `error` is not the code.
|
|
336
414
|
const SCHEDULE_GATE_HINT = " Two plan walls return 402 {error:'feature_gated', feature, upgradeTrigger, message}: feature 'schedule_cadence' means minute-based schedules are not in this plan (use EveryNHours or DailyAtTime), and 'schedule_count' means the worker is at its recurring-schedule cap (a OneShot is never counted). Both are terminal — do NOT retry; either pick an allowed cadence or tell the human to upgrade.";
|
|
415
|
+
// The plan's per-worker concurrency is for runs started on demand. A schedule
|
|
416
|
+
// never overlaps itself, whatever the plan allows: two copies of the same
|
|
417
|
+
// scheduled job side by side duplicate its side effects.
|
|
418
|
+
const SCHEDULE_OVERLAP_NOTE = " A schedule never overlaps itself: a fire that finds any run of the worker in flight lands as a Skipped receipt (skipReason ConcurrencyLimit) whatever the plan's per-worker concurrency allows — space the schedule wider than a run takes. Runs started on demand may run alongside a scheduled one, up to the plan's limit.";
|
|
337
419
|
const createSchedule = {
|
|
338
420
|
name: "schedule_create",
|
|
339
421
|
title: "Create Schedule",
|
|
340
422
|
description: "Create a schedule for the worker. Per-type required fields: " + SCHEDULE_TYPE_HINT +
|
|
341
423
|
" Omit timeZoneId to follow the worker's own time zone (the normal case). Requires the manageSchedules scope." +
|
|
342
|
-
SCHEDULE_GATE_HINT
|
|
424
|
+
SCHEDULE_GATE_HINT +
|
|
425
|
+
SCHEDULE_OVERLAP_NOTE,
|
|
343
426
|
auth: "manager",
|
|
344
427
|
method: "post",
|
|
345
428
|
schema: {
|
|
@@ -368,7 +451,7 @@ const updateSchedule = {
|
|
|
368
451
|
name: "schedule_update",
|
|
369
452
|
title: "Update Schedule",
|
|
370
453
|
description: "Edit a schedule (partial — omitted fields stay unchanged; timeZoneId='' clears the override so the schedule follows the worker's zone again). nextRunUtc is recomputed on every edit. Per-type field rules: " +
|
|
371
|
-
SCHEDULE_TYPE_HINT + " Requires manageSchedules." + SCHEDULE_GATE_HINT,
|
|
454
|
+
SCHEDULE_TYPE_HINT + " Requires manageSchedules." + SCHEDULE_GATE_HINT + SCHEDULE_OVERLAP_NOTE,
|
|
372
455
|
auth: "manager",
|
|
373
456
|
method: "patch",
|
|
374
457
|
schema: {
|
|
@@ -412,7 +495,7 @@ const deleteSchedule = {
|
|
|
412
495
|
// Where the PLATFORM sends a worker's report when a run finishes. The two facts an
|
|
413
496
|
// agent gets wrong by default: availability is judged at ACCOUNT level (never from
|
|
414
497
|
// the worker's own apps), and a receipt is not a guarantee.
|
|
415
|
-
const DELIVERY_NOTE = "
|
|
498
|
+
const DELIVERY_NOTE = " Delivery is PLATFORM-level, detached from the worker's own permissions: the destination row you configure IS the permission (a worker with no email app still delivers to email). The one configuration refusal is 400 CHANNEL_NOT_CONNECTED, judged at ACCOUNT level — never infer availability from worker_get's apps; call delivery_channels. At most 5 destinations per worker. Canceled runs never deliver; Skipped runs (refused at the gate) reach WEBHOOK destinations only, as run.blocked, once per worker per skip reason per UTC day; a run that ended by ASKING its owner (AwaitingInput) delivers everywhere, the question being the message. Sends are AT-MOST-ONCE: never double-sent, but a crash can lose one, so the receipt's deliveries[] records what happened, not a guarantee.";
|
|
416
499
|
const DELIVERY_TARGET_HINT = "The destination. ONE shape serves every channel — set only the fields YOUR channel needs; anything else is dropped. email: to[1–10 addresses] (+ optional connectionId OR sendFrom to choose the mailbox on a multi-mailbox account). slack: channelId (+ optional workspaceId). msTeams: EITHER teamId+channelId (post to a channel) OR chatId (post to a chat) — exactly one form; both or neither is a 400. telegram: chatId, a numeric id or an @username (+ optional botId). notion: boardId — the board/database that gets one item per run with the report as its content. discord: channelId (+ optional applicationId). messaging: provider (e.g. 'TWILIO'), messagingChannel ('sms' | 'whatsapp'), to[EXACTLY ONE phone number] (+ optional from; omit for the connection's default sender). webhook: url — an absolute https URL that resolves to a PUBLIC address (private, loopback and metadata ranges are a 400 at configuration and refused again at send time; redirects are never followed).";
|
|
417
500
|
const DELIVERY_TARGET_SHAPE = {
|
|
418
501
|
to: z.array(z.string()).optional().describe("email: 1–10 recipient addresses. messaging: exactly one phone number. Unused by every other channel."),
|
|
@@ -554,25 +637,30 @@ const deleteDelivery = {
|
|
|
554
637
|
const getInstruction = {
|
|
555
638
|
name: "instruction_get",
|
|
556
639
|
title: "Get Instruction",
|
|
557
|
-
description: "
|
|
640
|
+
description: "What the worker runs, by its model type (worker_get → modelType). A LANGUAGE worker: its standing instruction — content, jobSentence, whenToUse, description, memoryProfile, selfFactsEnabled, isProtected, currentVersion, timestamps; for a worker installed from a protected kit the metadata is returned and the text is REDACTED (it belongs to the kit's publisher), which is not an error. A DECISION worker: its routing table as sentences (narration: what it reads, what it asks per item, what it does with each answer, what happens when it is not sure), its install questions (setup[]) with the CURRENT answers, the ones still pending (pendingSetup[] — a pending question blocks deploy and run; fill them with instruction_set's answers) and decisionMode (live: the table acts; preview: runs report and act on nothing); the raw spec is withheld for a protected kit's worker. A bare \"Operation completed successfully.\" response means there is neither yet (HTTP 204) — use instruction_set. The version history (instruction_versions, instruction_restore) is language-only: a decision worker's table is versioned with its kit.",
|
|
558
641
|
auth: "manager",
|
|
559
642
|
method: "get",
|
|
560
643
|
schema: {
|
|
561
644
|
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
645
|
+
optionsFor: z.string().max(60).optional().describe("Decision workers: one appPick install question's key (from setup[]) — answers with its LIVE options instead of the table, listed from the worker's OWN connected app through the runtime under the worker's own permissions (a folder, a label, a queue, a board). Never a 4xx for an app that could not answer: items[] comes back empty with a warning saying why (not connected, dead credential, tool refused), and you paste an id from the app into instruction_set instead. 404 when the key names no appPick question; 409 on a language-model worker."),
|
|
562
646
|
},
|
|
563
647
|
path: (params) => `${API}/${params.tokenId}/instruction`,
|
|
564
|
-
paramFilter: () =>
|
|
648
|
+
paramFilter: (params) => {
|
|
649
|
+
const { tokenId: _tokenId, ...query } = params;
|
|
650
|
+
return query;
|
|
651
|
+
},
|
|
565
652
|
annotations: READ_ONLY,
|
|
566
653
|
};
|
|
567
654
|
const setInstruction = {
|
|
568
655
|
name: "instruction_set",
|
|
569
656
|
title: "Set Instruction",
|
|
570
|
-
description: "
|
|
657
|
+
description: "Set what the worker runs: content for a LANGUAGE worker's standing instruction, answers for a DECISION worker's install questions. They are mutually exclusive — sending both is a 400. content replaces the whole instruction: a changed body snapshots the prior version, omitted optional fields stay unchanged, an empty string clears, and a protected kit's worker returns 403 OPERATION_NOT_ALLOWED because that text belongs to its publisher (do not retry). answers fills or changes the questions the routing table binds at every run, so a saved change reaches the next one — check it first with worker_run preview:true. A protected kit withholds its spec, never its questions, so answering them is not refused. Requires the manageInstructions scope either way.",
|
|
571
658
|
auth: "manager",
|
|
572
659
|
method: "put",
|
|
573
660
|
schema: {
|
|
574
661
|
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
575
|
-
content: z.string().min(1).max(100000).describe("
|
|
662
|
+
content: z.string().min(1).max(100000).optional().describe("Language workers: the instruction text (1–100,000 chars) — replaces the whole content."),
|
|
663
|
+
answers: z.record(z.string(), z.string().max(4000)).optional().describe("Decision workers: install answers by question key, from instruction_get's setup[]. A PARTIAL map changes only the keys it names; '' clears one. A scale takes a stop's label, a choice an option's value, a list one row per line, an appPick an id (instruction_get's optionsFor lists the live ones). An unknown key or an off-menu value is a 400 naming it; 409 on a language-model worker."),
|
|
576
664
|
jobSentence: z.string().max(200).optional().describe("The worker's one-liner (≤200). Omit = unchanged; '' = clear."),
|
|
577
665
|
whenToUse: z.string().max(500).optional().describe("Trigger text: when this worker should be used (≤500). Omit = unchanged; '' = clear."),
|
|
578
666
|
description: z.string().max(2000).optional().describe("Longer display-only description (≤2000). Omit = unchanged; '' = clear."),
|
|
@@ -582,6 +670,7 @@ const setInstruction = {
|
|
|
582
670
|
path: (params) => `${API}/${params.tokenId}/instruction`,
|
|
583
671
|
bodyBuilder: (params) => ({
|
|
584
672
|
content: params.content,
|
|
673
|
+
answers: params.answers,
|
|
585
674
|
jobSentence: params.jobSentence,
|
|
586
675
|
whenToUse: params.whenToUse,
|
|
587
676
|
description: params.description,
|
|
@@ -639,7 +728,7 @@ const restoreInstructionVersion = {
|
|
|
639
728
|
const setWorkerEnabled = {
|
|
640
729
|
name: "worker_set_enabled",
|
|
641
730
|
title: "Start or Stop Worker",
|
|
642
|
-
description: "Start (enabled=true) or stop (enabled=false) a worker. Stopping pauses the WHOLE worker: its own API key stops working AND its schedules stop firing; re-enabling restores both. Returns {tokenId, isEnabled}. Idempotent. Requires the manageState scope.",
|
|
731
|
+
description: "Start (enabled=true) or stop (enabled=false) a worker. Stopping pauses the WHOLE worker: its own API key stops working AND its schedules stop firing; re-enabling restores both. THIS IS THE REVERSIBLE ONE — reach for it whenever someone says stop, pause, turn off or disable; worker_delete is permanent and nothing undoes it. Two things about stopping an ORCHESTRATOR that the response does not tell you: it CASCADES to that worker's sub-workers (children and grandchildren), and re-enabling the parent brings back ONLY the parent — each sub-worker needs its own worker_set_enabled(tokenId, true), so check workers_list afterwards rather than assuming the fleet came back. And re-enabling consumes a worker slot: at the plan's worker cap it is refused with 402 limit_exceeded, so a worker stopped before a downgrade can be stuck stopped until the owner upgrades or deletes another worker. Returns {tokenId, isEnabled}. Idempotent. Requires the manageState scope.",
|
|
643
732
|
auth: "manager",
|
|
644
733
|
method: "post",
|
|
645
734
|
schema: {
|
|
@@ -650,6 +739,19 @@ const setWorkerEnabled = {
|
|
|
650
739
|
bodyBuilder: (params) => ({ enabled: params.enabled }),
|
|
651
740
|
annotations: UPDATE,
|
|
652
741
|
};
|
|
742
|
+
const deleteWorker = {
|
|
743
|
+
name: "worker_delete",
|
|
744
|
+
title: "Delete Worker",
|
|
745
|
+
description: "Delete a worker PERMANENTLY. Not reversible by any call, on any surface: its own pe_ key stops working immediately (an agent holding it starts failing), its schedules stop firing, its instruction, memory, deployment and delivery destinations go with it, and it disappears from this API, MCP, the CLI and the dashboard alike. TO STOP A WORKER YOU MIGHT WANT BACK, USE worker_set_enabled WITH enabled:false — that pauses the key and the schedules together and is reversible (with two caveats it states: stopping cascades to sub-workers and re-enabling brings back only the worker you name, and re-enabling is refused with 402 at the plan's worker cap); THIS one cannot be undone at all, so confirm with the person before calling it, never infer it from 'get rid of', 'turn off' or 'stop'. THE SURPRISE: deleting an orchestrator DELETES ITS SUB-WORKERS TOO (children and grandchildren — a dead orchestrator must never leave live workers behind), and the response's subWorkersDeleted says how many went with it; report that number, because those workers had their own jobs. What SURVIVES: the apps stay connected for every other worker on the operator, and the run receipts stay readable through the account-wide reads (runs_feed, run_get) — a deleted worker's spending is still part of the account's history. A run in flight settles normally and is neither cancelled nor refunded; runsInFlight reports how many were running when the delete landed. Deleting FREES A WORKER SLOT, which is the fix for kit_install's 402 limit_exceeded when the owner would rather not upgrade. 404 means the worker does not exist or is not on this account — including a second delete of one already gone, so a 404 on a retry means the first call worked. Requires the deleteWorkers scope." +
|
|
746
|
+
NEW_SCOPE_NOTE,
|
|
747
|
+
auth: "manager",
|
|
748
|
+
method: "delete",
|
|
749
|
+
schema: {
|
|
750
|
+
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
751
|
+
},
|
|
752
|
+
path: (params) => `${API}/${params.tokenId}`,
|
|
753
|
+
annotations: DELETE,
|
|
754
|
+
};
|
|
653
755
|
// ─── Kit install ────────────────────────────────────────────────────────────
|
|
654
756
|
// The one place on this surface an agent can CREATE a worker: install a kit from
|
|
655
757
|
// the public directory. Both tools need the installKits scope (keys minted
|
|
@@ -665,7 +767,7 @@ const KIT_SLUG = z.string().min(1).max(120).regex(/^[a-z0-9][a-z0-9-]*$/, "not a
|
|
|
665
767
|
const kitInstallPreview = {
|
|
666
768
|
name: "kit_install_preview",
|
|
667
769
|
title: "Preview Kit Install",
|
|
668
|
-
description: "The kit's install form plus this account's current ability to satisfy it — call this BEFORE kit_install and gather every answer it demands. Creates nothing. Requires the installKits scope (403 names it). Returns: requiredInputs (EVERY key is mandatory at install — collect a value for each from the human, keys are exact), memorySetup (questions whose answers become the worker's first memory; only required:true entries are mandatory), categorySlots (pick ONE member per slot via categoryChoices; each member carries connection — prefer a connected one — and connectionProvider, which when set means also pass categoryChoices[].resourceId picked from operatorResources, ideally one with hasActiveConnection and a matching provider), apps + appsNeedingConnection (the connection state the new worker would START with; installing anyway is allowed — the worker starts blocked and a human finishes at connectAppsUrl), operator (which operator the install targets), limits (currentWorkers/maxWorkers — at the cap the install returns 402), kitPageUrl (the human install page). Preview is advisory: the install response's readiness block is the verdict. For the kit's full permissions manifest and instruction use kit_get on the public Directory server.",
|
|
770
|
+
description: "The kit's install form plus this account's current ability to satisfy it — call this BEFORE kit_install and gather every answer it demands. Creates nothing. Requires the installKits scope (403 names it). Returns: requiredInputs (EVERY key is mandatory at install — collect a value for each from the human, keys are exact), memorySetup (questions whose answers become the worker's first memory; only required:true entries are mandatory), modelType, decisionSetup, decisionNarration (a 'decision' kit runs a routing table instead of an instruction, on no model you pick: decisionNarration is that table as sentences — brief the human from it; decisionSetup are its install questions, answered by key in kit_install's decisionAnswers — a required one with no default is mandatory, except an appPick, answerable later with instruction_set; the worker starts live), categorySlots (pick ONE member per slot via categoryChoices; each member carries connection — prefer a connected one — and connectionProvider, which when set means also pass categoryChoices[].resourceId picked from operatorResources, ideally one with hasActiveConnection and a matching provider), apps + appsNeedingConnection (the connection state the new worker would START with; installing anyway is allowed — the worker starts blocked and a human finishes at connectAppsUrl), operator (which operator the install targets), limits (currentWorkers/maxWorkers — at the cap the install returns 402), kitPageUrl (the human install page). Preview is advisory: the install response's readiness block is the verdict. For the kit's full permissions manifest and instruction use kit_get on the public Directory server.",
|
|
669
771
|
auth: "manager",
|
|
670
772
|
method: "get",
|
|
671
773
|
schema: {
|
|
@@ -682,7 +784,7 @@ const kitInstallPreview = {
|
|
|
682
784
|
const kitInstall = {
|
|
683
785
|
name: "kit_install",
|
|
684
786
|
title: "Install Kit",
|
|
685
|
-
description: "Install a directory kit as a NEW worker on this account. NOT idempotent: every
|
|
787
|
+
description: "Install a directory kit as a NEW worker on this account. NOT idempotent: every success creates another worker — never retry a success; on a timeout check workers_list (readWorkers) first. Requires the installKits scope. Call kit_install_preview first, then supply every requiredInputs key in inputs (a missing or unknown key is a 400 naming it), required memorySetup answers in memoryAnswers, a DECISION kit's decisionSetup answers in decisionAnswers, and one categoryChoices entry per slot (a member with connectionProvider set also takes resourceId from the preview's operatorResources). CRITICAL — secrets shown ONCE: install.rawKey (the worker's pe_ API key) and install.triggers[].signingSecret can NEVER be read again; hand them to the human before discarding the response. Also returned: readiness (ready | blocked with actionable issues such as app_not_connected + candidateProviders; null means the readiness check failed AFTER the install succeeded — read it via worker_get, never re-install), workerUrl (the worker's dashboard page — hand it to the human) and connectAppsUrl (where the human connects missing apps; agents cannot). 402 {error:'limit_exceeded'} is the plan's worker cap — terminal: the human upgrades, or frees a slot with worker_delete (deleteWorkers scope; permanent, so only with their agreement). Rate: 10 installs/hour per account. Installing does NOT make the worker run: without a deployment it fires no schedule and worker_run refuses it with not_deployed. deploy:true (optionally with deployment: modelSlug from models_list and the ceilings) installs and deploys in ONE call and the response carries deployment; a deploy refused after the install still returns 201 with deploymentError naming the fix — the worker exists, so fix it and call worker_deploy, never re-install. deploy:true needs manageDeployments IN ADDITION to installKits: a key without it is refused up front (403 OPERATION_NOT_ALLOWED) and nothing is created — drop deploy, install, and have the owner re-scope the key before worker_deploy.",
|
|
686
788
|
auth: "manager",
|
|
687
789
|
method: "post",
|
|
688
790
|
schema: {
|
|
@@ -697,6 +799,7 @@ const kitInstall = {
|
|
|
697
799
|
})).optional().describe("One entry per categorySlots slot. Omitted slots resolve to the kit's first offered member on safe defaults."),
|
|
698
800
|
inputs: z.record(z.string(), z.string()).optional().describe("Answers to requiredInputs, keyed EXACTLY by each entry's key. Every key is mandatory; values are single-line, 1-200 chars."),
|
|
699
801
|
memoryAnswers: z.record(z.string(), z.string()).optional().describe("Answers to memorySetup questions by key. Entries with required:true are mandatory. Keep this separate from inputs — mixing the two maps is a 400."),
|
|
802
|
+
decisionAnswers: z.record(z.string(), z.string()).optional().describe("DECISION kits only: answers to the preview's decisionSetup questions by key — a choice takes an option's value, a scale a stop's label (or its value), a list one row per line (≤20 rows), an appPick an id (or leave it blank and answer it later with instruction_set); text and rows ≤200 chars. A required question with no default and no answer is a 400 naming it."),
|
|
700
803
|
deploy: z.boolean().optional().describe("true = also put the new worker on the hosted runtime, so it actually runs. Without this the install creates a worker that fires no schedule and refuses worker_run with not_deployed. Needs the manageDeployments scope."),
|
|
701
804
|
deployment: z.object({
|
|
702
805
|
modelSlug: z.string().max(64).optional().describe("Model to deploy on, from models_list. Omit for the kit's recommended model."),
|
|
@@ -714,6 +817,7 @@ const kitInstall = {
|
|
|
714
817
|
categoryChoices: params.categoryChoices,
|
|
715
818
|
inputs: params.inputs,
|
|
716
819
|
memoryAnswers: params.memoryAnswers,
|
|
820
|
+
decisionAnswers: params.decisionAnswers,
|
|
717
821
|
deploy: params.deploy,
|
|
718
822
|
deployment: params.deployment,
|
|
719
823
|
}),
|
|
@@ -726,7 +830,7 @@ const kitInstall = {
|
|
|
726
830
|
// chat message is a different power and stays on the dashboard and the kit
|
|
727
831
|
// pipeline, where a manifest gets reviewed. All three need the createWorkers
|
|
728
832
|
// scope.
|
|
729
|
-
const CLONE_NOTE = "
|
|
833
|
+
const CLONE_NOTE = " A clone copies the source's PERMISSIONS (it can never hold access a human did not already approve on the source) plus, by flag, the instruction, owner-authored memory, schedules, delivery destinations and the hosted deployment. Three things are deliberately not straight copies: schedules arrive DISABLED (enable with schedule_update once satisfied), AGENT-AUTHORED FACTS do not travel (only owner-authored rules and facts do), and a webhook destination arrives DISABLED WITH NO SIGNING SECRET so revoking the clone cannot break its source (delivery_secret_rotate, then delivery_update to enable). The clone inherits the SAME per-run and per-day ceilings, so cloning multiplies the fleet's ceiling — read fleet_budget_get before a bulk clone. Instruction history does not travel (the clone starts at version 1). A source whose instruction came from a PROTECTED kit is refused while includeInstruction is true — install the kit again or clone with includeInstruction:false.";
|
|
730
834
|
const CLONE_TITLE_HINT = "The new worker's title, 1–120 chars. Required: a clone is never silently named after its source.";
|
|
731
835
|
// One shape, three tools: worker_clone and worker_clone_preview take it as their
|
|
732
836
|
// body, worker_clone_bulk takes an array of it. Every include flag defaults to
|
|
@@ -744,7 +848,7 @@ const CLONE_REQUEST_SHAPE = {
|
|
|
744
848
|
const clonePreview = {
|
|
745
849
|
name: "worker_clone_preview",
|
|
746
850
|
title: "Preview Worker Clone",
|
|
747
|
-
description: "What cloning this worker WOULD produce
|
|
851
|
+
description: "What cloning this worker WOULD produce — CREATES NOTHING. Run it before worker_clone, and before worker_clone_bulk especially. Takes the same body as worker_clone (the only reason it is a POST) and answers with sourceWorkerId / sourceTitle, the title the clone would carry, apps[] (every app the clone would hold and whether the operator can serve it today — where you learn a clone would be born unable to run), wouldCopyInstruction, memoryItemCount, scheduleCount, deliveryCount, the deployment it would inherit (modelSlug, maxUsdPerRun, maxUsdPerDay), and two lists that carry the verdict: BLOCKERS — reasons the clone WOULD BE REFUSED (empty = it would succeed) — and NOTES, things that would succeed but are worth knowing (schedules landing disabled, only owner-authored memory travelling, a webhook destination arriving unarmed, the fleet's spend ceiling multiplying). blockers is a verdict, not advice. Requires the createWorkers scope." +
|
|
748
852
|
NEW_SCOPE_NOTE +
|
|
749
853
|
CLONE_NOTE,
|
|
750
854
|
auth: "manager",
|
|
@@ -769,7 +873,7 @@ const clonePreview = {
|
|
|
769
873
|
const cloneWorker = {
|
|
770
874
|
name: "worker_clone",
|
|
771
875
|
title: "Clone Worker",
|
|
772
|
-
description: "Copy this worker into a NEW one (201 with the created worker). NOT IDEMPOTENT: every
|
|
876
|
+
description: "Copy this worker into a NEW one (201 with the created worker). NOT IDEMPOTENT: every success creates another worker — never retry a success; after a timeout check workers_list (readWorkers) first. CRITICAL — the key is shown ONCE: rawKey is the new worker's pe_ API key and can NEVER be read again; hand it to the human before discarding the response. The new worker is identified by workerId (a UUID); every other tool here addresses workers by numeric tokenId, so look the clone up in workers_list. Call worker_clone_preview first — its blockers are the same refusal you would otherwise meet as a 400 whose message names the cause (a protected kit's instruction is the common one); 404 = unknown or other-account source. The clone starts ENABLED with its schedules off: nothing runs until you enable a schedule or call worker_run. Rate: 20 create CALLS per hour per ACCOUNT, shared with worker_clone_bulk (a bulk call of twenty costs one). Requires the createWorkers scope." +
|
|
773
877
|
NEW_SCOPE_NOTE +
|
|
774
878
|
CLONE_NOTE,
|
|
775
879
|
auth: "manager",
|
|
@@ -793,7 +897,7 @@ const cloneWorker = {
|
|
|
793
897
|
const cloneWorkerBulk = {
|
|
794
898
|
name: "worker_clone_bulk",
|
|
795
899
|
title: "Clone Worker in Bulk",
|
|
796
|
-
description: "Clone this worker several times in ONE call — how a fleet of eighteen becomes a fleet of a hundred.
|
|
900
|
+
description: "Clone this worker several times in ONE call — how a fleet of eighteen becomes a fleet of a hundred. NOT ATOMIC, NO ROLLBACK: PARTIAL SUCCESS IS THE NORMAL OUTCOME (a per-item report beats discarding nineteen good workers over the twentieth's bad title), so READ items[] RATHER THAN THE STATUS CODE — a 200 with created:17 of requested:20 is a success and a failure at once; each item carries index, title, success, workerId and either rawKey or error. KEEP EVERY rawKey: each is that worker's pe_ API key, shown once and never again; a discarded partial-success response strands real workers whose keys nobody has. Capped at 20 workers per call (the 21st is a 400) and 20 create calls per hour per ACCOUNT, shared with worker_clone — a typo here creates workers. Run worker_clone_preview once for the shape you are about to repeat: a blocker that refuses one item refuses all. Requires the createWorkers scope." +
|
|
797
901
|
NEW_SCOPE_NOTE +
|
|
798
902
|
CLONE_NOTE,
|
|
799
903
|
auth: "manager",
|
|
@@ -815,7 +919,7 @@ const BUDGET_WINDOW_NOTE = " Windows differ, deliberately: maxUsdPerDay and both
|
|
|
815
919
|
const getWorkerBudget = {
|
|
816
920
|
name: "budget_get",
|
|
817
921
|
title: "Get Worker Budget",
|
|
818
|
-
description: "One worker's spend and rate ceilings: maxUsdPerRun, maxUsdPerDay, maxRunsPerDay, maxConcurrentRuns, modelSlug, hasDeployment (plus workerId and its deprecated alias tokenId). Four readings
|
|
922
|
+
description: "One worker's spend and rate ceilings: maxUsdPerRun, maxUsdPerDay, maxRunsPerDay, maxConcurrentRuns, modelSlug, hasDeployment (plus workerId and its deprecated alias tokenId). Four readings agents get wrong. (1) maxRunsPerDay NULL MEANS THE PLATFORM DEFAULT, NOT UNLIMITED — the gate substitutes the platform's number; never report null as 'no limit'. (2) maxUsdPerRun is RESERVED from the wallet at dispatch, so a run must be able to AFFORD it to start, and the day cap counts those RESERVATIONS, not settled cost: a worker reserving $0.50 under a $2/day cap is skipped on its fifth run even if each cost a cent (a reservation is refunded when a later gate skips the run). (3) maxConcurrentRuns is SET BY THE ACCOUNT'S PLAN, not per worker — Free 5, Pro 20, Team 50, Enterprise uncapped — so budget_set has no parameter for it and raising it is an upgrade; it bounds runs started on demand (worker_run, run_bulk, webhooks), while a schedule never overlaps itself whatever the plan allows. (4) hasDeployment FALSE means hosted runs are not set up, and the dollar figures then read 0 because there is no deployment to read them from — 'no deployment', NOT 'capped at zero'; check hasDeployment before quoting a ceiling. Per-worker caps do not compose; the ceiling above them is fleet_budget_get. Requires the manageBudgets scope — reading a ceiling rides the same scope as changing it." +
|
|
819
923
|
BUDGET_WINDOW_NOTE +
|
|
820
924
|
NEW_SCOPE_NOTE,
|
|
821
925
|
auth: "manager",
|
|
@@ -830,7 +934,7 @@ const getWorkerBudget = {
|
|
|
830
934
|
const setWorkerBudget = {
|
|
831
935
|
name: "budget_set",
|
|
832
936
|
title: "Set Worker Budget",
|
|
833
|
-
description: "Change one worker's ceilings
|
|
937
|
+
description: "Change one worker's ceilings and get the whole budget back. PARTIAL: an OMITTED FIELD MEANS UNCHANGED, so one ceiling can be raised without restating the rest or racing another writer. Three traps. maxUsdPerRun / maxUsdPerDay on a worker with NO HOSTED DEPLOYMENT is a 400 (nothing for a dollar cap to bind to; budget_get's hasDeployment says which those are) — maxRunsPerDay can be set on any worker. maxRunsPerDay cannot be cleared back to the platform default here, because null already means 'leave it alone': send an explicit number. LOWERING maxUsdPerRun below what a run needs does not fail loudly — the worker's next run becomes a Skipped receipt with a skipReason, which you must go and read. maxConcurrentRuns is absent by design: the account's plan sets it (Free 5, Pro 20, Team 50), so raising it is an upgrade. A ZERO per-run cap is REFUSED (400), as is a day cap below the per-run cap — a zero reserve is a run that cannot start, not a worker that spends nothing; to stop a worker use worker_set_enabled. maxRunsPerDay is clamped to the organization policy ceiling when one is enforced. The same call sets the question timeout: awaitInputTimeoutMinutes (how long a question the worker asks stays open; clearAwaitInputTimeout removes the bound) — when it passes the question is closed out, never resumed unanswered, and a late answer still starts the run. Requires the manageBudgets scope." +
|
|
834
938
|
BUDGET_WINDOW_NOTE +
|
|
835
939
|
NEW_SCOPE_NOTE,
|
|
836
940
|
auth: "manager",
|
|
@@ -892,11 +996,11 @@ const setFleetBudget = {
|
|
|
892
996
|
// and completely inert: no schedule fires and worker_run answers 409
|
|
893
997
|
// not_deployed. Deploying is also where the model and the spend ceilings are
|
|
894
998
|
// chosen, which is why the writes sit behind their own scope.
|
|
895
|
-
const DEPLOYMENT_NOTE = " A worker with NO DEPLOYMENT is configuration only: its schedules never fire and worker_run refuses it with 409 not_deployed. Deploying
|
|
999
|
+
const DEPLOYMENT_NOTE = " A worker with NO DEPLOYMENT is configuration only: its schedules never fire and worker_run refuses it with 409 not_deployed. Deploying puts it on the runtime and sets its model and per-run / per-day spend ceilings.";
|
|
896
1000
|
const listModels = {
|
|
897
1001
|
name: "models_list",
|
|
898
1002
|
title: "List Deployable Models",
|
|
899
|
-
description: "The models this ACCOUNT may deploy a worker on, priced per million tokens — the picker for worker_deploy's modelSlug. Each row carries slug (the value deployment calls take), displayName, provider, inputUsdPerMTok / outputUsdPerMTok / cachedInputUsdPerMTok / cacheWriteInputUsdPerMTok, contextWindowK, minTier, recommended, and the reasoning vocabulary. A MODEL ABSENT FROM THIS LIST IS NOT DEPLOYABLE HERE — either the account's tier does not reach it or no live provider serves it — so never pass a slug you read somewhere else; that is a 400 one call later. reasoningStyle says what the deployment's thinking field accepts FOR THAT MODEL: 'budget' takes a per-turn token count AS A STRING ('1024', between thinkingBudgetMin and thinkingBudgetMax), 'effort' takes one of reasoningEffortOptions ('high'), 'none' takes only 'default' or 'off'. Sending the wrong kind is 400 invalid_thinking. byokProviders lists the providers this account holds its own API key for (model_keys_list) — a run on a model from one of those bills the account's key plus a platform fee instead of the wallet. Pass kitSlug to have the kit's own recommendation marked recommended:true; without it nothing is marked. Requires readWorkers." +
|
|
1003
|
+
description: "The models this ACCOUNT may deploy a worker on, priced per million tokens — the picker for worker_deploy's modelSlug. Each row carries slug (the value deployment calls take), displayName, provider, inputUsdPerMTok / outputUsdPerMTok / cachedInputUsdPerMTok / cacheWriteInputUsdPerMTok, contextWindowK, minTier, recommended, and the reasoning vocabulary. A MODEL ABSENT FROM THIS LIST IS NOT DEPLOYABLE HERE — either the account's tier does not reach it or no live provider serves it — so never pass a slug you read somewhere else; that is a 400 one call later. reasoningStyle says what the deployment's thinking field accepts FOR THAT MODEL: 'budget' takes a per-turn token count AS A STRING ('1024', between thinkingBudgetMin and thinkingBudgetMax), 'effort' takes one of reasoningEffortOptions ('high'), 'none' takes only 'default' or 'off'. Sending the wrong kind is 400 invalid_thinking. byokProviders lists the providers this account holds its own API key for (model_keys_list) — a run on a model from one of those bills the account's key plus a platform fee instead of the wallet. Pass kitSlug to have the kit's own recommendation marked recommended:true; without it nothing is marked. Language-model workers only: a DECISION worker (worker_get modelType 'decision') runs the decision model and takes no modelSlug. Requires readWorkers." +
|
|
900
1004
|
RUNTIME_NOTE,
|
|
901
1005
|
auth: "manager",
|
|
902
1006
|
method: "get",
|
|
@@ -934,7 +1038,7 @@ const getDeployment = {
|
|
|
934
1038
|
const deployWorker = {
|
|
935
1039
|
name: "worker_deploy",
|
|
936
1040
|
title: "Deploy Worker",
|
|
937
|
-
description: "Put the worker on the hosted runtime —
|
|
1041
|
+
description: "Put the worker on the hosted runtime — what makes it runnable and schedulable, and the step most often forgotten after kit_install (whose deploy:true does both in one call). EVERYTHING IS OPTIONAL: send nothing and it deploys on the model its kit recommends with the platform's default ceilings; 400 model_required means the kit named none — pick one from models_list. Each refusal names its fix: 422 instruction_required (no instruction — a hosted run has nothing to execute), 422 apps_not_connected (the message names the apps: app_connect them or drop them from the worker — a half-connected worker is refused rather than left to burn spend on a partial answer), 402 wallet_required (hosted runs meter from the prepaid wallet and AN AGENT CANNOT TOP IT UP — tell the human), 402 hosted_worker_limit, 409 already_deployed (use deployment_update), 400 invalid_model / model_tier_gated (unknown, disabled or above the account's tier — choose another from models_list), 400 invalid_thinking (wrong reasoning vocabulary for the model), 400 invalid_caps (maxUsdPerDay below maxUsdPerRun). A DECISION worker needs neither a model nor an instruction — deploy it as is (a modelSlug or thinking value is refused: 400 invalid_model / invalid_thinking); it starts live, its routing table acting from the first run. Its one extra refusal is 400 decision_setup_pending, an install question still blank: instruction_get names it, instruction_get with optionsFor lists an appPick's live values, instruction_set fills it. warnings[] on success never blocks: ip_rules_enforced means the worker's IP allowlist will reject hosted runs, which arrive from platform egress. Requires manageDeployments." +
|
|
938
1042
|
DEPLOYMENT_NOTE +
|
|
939
1043
|
RUNTIME_NOTE +
|
|
940
1044
|
NEW_SCOPE_NOTE,
|
|
@@ -961,7 +1065,7 @@ const deployWorker = {
|
|
|
961
1065
|
const updateDeployment = {
|
|
962
1066
|
name: "deployment_update",
|
|
963
1067
|
title: "Update Deployment",
|
|
964
|
-
description: "Change a live deployment, or pause and resume it. PARTIAL: an OMITTED FIELD MEANS UNCHANGED, so one ceiling can be raised without restating the rest. action:'pause' stops schedules and run triggers while KEEPING the deployment, its model and its ceilings — the reversible way to stop a worker spending, and the one to reach for before worker_undeploy; action:'resume' puts it back, and is refused on a Suspended deployment (only support lifts that). One interplay to know: changing modelSlug WITHOUT sending thinking keeps a still-valid reasoning setting and otherwise clears it to 'default', reporting a thinking_reset warning rather than refusing the model change. 404 not_found = the worker is not deployed; deploy it with worker_deploy first. Requires manageDeployments." +
|
|
1068
|
+
description: "Change a live deployment, or pause and resume it. PARTIAL: an OMITTED FIELD MEANS UNCHANGED, so one ceiling can be raised without restating the rest. action:'pause' stops schedules and run triggers while KEEPING the deployment, its model and its ceilings — the reversible way to stop a worker spending, and the one to reach for before worker_undeploy; action:'resume' puts it back, and is refused on a Suspended deployment (only support lifts that). One interplay to know: changing modelSlug WITHOUT sending thinking keeps a still-valid reasoning setting and otherwise clears it to 'default', reporting a thinking_reset warning rather than refusing the model change. decisionMode (decision workers only) is the Live / Preview switch — the one field that stops a decision worker's routing table acting, and the one that puts it back; 400 invalid_decision_mode on a language-model worker, and on a decision worker modelSlug / thinking are refused instead (400 invalid_model / invalid_thinking: it has no model to change). 404 not_found = the worker is not deployed; deploy it with worker_deploy first. Requires manageDeployments." +
|
|
965
1069
|
DEPLOYMENT_NOTE +
|
|
966
1070
|
RUNTIME_NOTE +
|
|
967
1071
|
NEW_SCOPE_NOTE,
|
|
@@ -975,6 +1079,7 @@ const updateDeployment = {
|
|
|
975
1079
|
thinking: z.string().max(16).optional().describe("New reasoning setting for the CURRENT model: 'default', 'off', a token budget as a string ('1024'), or an effort word ('high'). Omit to keep."),
|
|
976
1080
|
transcriptRetention: z.string().max(16).optional().describe("New transcript setting. Omit to keep."),
|
|
977
1081
|
action: z.enum(["pause", "resume"]).optional().describe("'pause' = stop schedules and run triggers, keeping the deployment; 'resume' = start again (refused while Suspended). Omit to change settings only."),
|
|
1082
|
+
decisionMode: z.enum(["preview", "live"]).optional().describe("Decision workers only. 'live' (the default) = the routing table's actions execute; 'preview' = runs report what they would do and act on nothing. Omit to keep. Check a changed table with worker_run preview:true before going back to live."),
|
|
978
1083
|
},
|
|
979
1084
|
path: (params) => `${API}/${params.tokenId}/deployment`,
|
|
980
1085
|
bodyBuilder: (params) => ({
|
|
@@ -984,6 +1089,7 @@ const updateDeployment = {
|
|
|
984
1089
|
thinking: params.thinking,
|
|
985
1090
|
transcriptRetention: params.transcriptRetention,
|
|
986
1091
|
action: params.action,
|
|
1092
|
+
decisionMode: params.decisionMode,
|
|
987
1093
|
}),
|
|
988
1094
|
annotations: UPDATE,
|
|
989
1095
|
};
|
|
@@ -1046,7 +1152,7 @@ const LISTING_SCHEMA = {
|
|
|
1046
1152
|
declaredModelFloor: z.string().max(80).optional().describe("Display-only minimum model (≤80)."),
|
|
1047
1153
|
modelScores: z.array(z.record(z.unknown())).max(10).optional().describe("≤10 of {model, score 0-100, notes ≤300} — only for models the kit was actually run on."),
|
|
1048
1154
|
};
|
|
1049
|
-
const CONTENT_SCHEMA = z.record(z.unknown()).describe("The kit's content: instructionContent (required), whenToUse, startCommand, endCommand, endCommandDescription, skillResources, appCodes XOR apps, categorySlots, mcpServers, contactAllowAll, blockedSenderCategories, timeframePastDays, timeframeFutureDays, maxChildTokens, memoryProfile, selfFactsEnabled, memorySetup, schedules, triggers, usageWindows.
|
|
1155
|
+
const CONTENT_SCHEMA = z.record(z.unknown()).describe("The kit's content: instructionContent (required on a LANGUAGE kit), whenToUse, startCommand, endCommand, endCommandDescription, skillResources, appCodes XOR apps, categorySlots, mcpServers, contactAllowAll, blockedSenderCategories, timeframePastDays, timeframeFutureDays, maxChildTokens, memoryProfile, selfFactsEnabled, memorySetup, schedules, triggers, usageWindows — or, for a DECISION kit, decisionSpec INSTEAD of instructionContent (a routing table the decision model runs per item; optionally modelType 'decision') and none of commands, whenToUse, skillResources, memorySetup or selfFactsEnabled; the compartment, stance and cadence fields apply to both kinds. Which kind a job is, the exact shape, every cap and every rule: kit_authoring_guide sections 'index' and 'schema'; app codes, tool keys and category slugs: kit_vocabulary. Validated strictly server-side — unknown keys are rejected, never ignored — so kit_validate first.");
|
|
1050
1156
|
const getMyPublisher = {
|
|
1051
1157
|
name: "publisher_get_mine",
|
|
1052
1158
|
title: "Get My Publisher",
|
|
@@ -1373,7 +1479,7 @@ const getMcpServer = {
|
|
|
1373
1479
|
const createMcpServer = {
|
|
1374
1480
|
name: "mcp_server_create",
|
|
1375
1481
|
title: "Register MCP Server",
|
|
1376
|
-
description: "Register an MCP server as this account's own custom MCP app — the way to reach an app the platform does not offer — and list its tools in the same call. upstreamUrl
|
|
1482
|
+
description: "Register an MCP server as this account's own custom MCP app — the way to reach an app the platform does not offer — and list its tools in the same call. upstreamUrl is the MCP endpoint itself (https), from the user or the vendor's docs, never a docs page and never guessed. authType is what the server expects: None (default — discovered in this call, no credential), Bearer {token}, ApiKeyHeader {header, value}, ApiKeyQuery {param, value}, Basic {username, password}, CustomHeaders {headers} or McpOAuth (a human completes the sign-in on the dashboard); credential carries those fields — probed LIVE against the server before anything is stored, stored encrypted, never returned, and the tools are discovered with it. credentialScope: operator (default — each operator connects its own; stored for operatorId or the default operator) or account (one credential serves every operator). Omit credential to register now and connect later with app_connect (provider mcp:<slug>). Returns 201 {server (the mcp_servers_list shape — id is what a kit binds, gatewayId what the next tools take), discovery {added, changed, removed, unchanged}, next}. The server stays UNPUBLISHED until mcp_server_set_tools enables at least one tool. Errors: 400 credential_invalid (the server rejected the credential; nothing kept — fix and retry), 502 provider_unavailable (the URL did not answer as an MCP server; nothing kept — check the endpoint or retry later), 422 invalid_url (not an https URL on a public host), 400 invalid_request (names the valid authType / credentialScope values or the 25-server ceiling). Tell the user the upstream URL becomes public listing data if a kit binding this server publishes publicly. Rate: 20 registers/hour per account. Requires the manageConnections scope." +
|
|
1377
1483
|
NEW_SCOPE_NOTE,
|
|
1378
1484
|
auth: "manager",
|
|
1379
1485
|
method: "post",
|
|
@@ -1464,11 +1570,15 @@ export const manageDescriptors = [
|
|
|
1464
1570
|
listWorkers,
|
|
1465
1571
|
getWorker,
|
|
1466
1572
|
runWorker,
|
|
1573
|
+
runWorkersBulk,
|
|
1467
1574
|
runsFeed,
|
|
1468
1575
|
fleetPulse,
|
|
1576
|
+
fleetHealth,
|
|
1577
|
+
accountUsage,
|
|
1469
1578
|
listRuns,
|
|
1470
1579
|
getRun,
|
|
1471
1580
|
getRunEvents,
|
|
1581
|
+
getRunTranscript,
|
|
1472
1582
|
getRunQuestion,
|
|
1473
1583
|
answerRunQuestion,
|
|
1474
1584
|
cancelRun,
|
|
@@ -1494,6 +1604,7 @@ export const manageDescriptors = [
|
|
|
1494
1604
|
getInstructionVersion,
|
|
1495
1605
|
restoreInstructionVersion,
|
|
1496
1606
|
setWorkerEnabled,
|
|
1607
|
+
deleteWorker,
|
|
1497
1608
|
kitInstallPreview,
|
|
1498
1609
|
kitInstall,
|
|
1499
1610
|
clonePreview,
|