@workerkit/core 0.3.3 → 0.3.5

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 CHANGED
@@ -6,6 +6,61 @@ and the package adheres to [Semantic Versioning](https://semver.org/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.3.5] - 2026-09-19
10
+
11
+ ### Changed
12
+
13
+ - A decision run's `decisions[]` rows carry `item`: the fields the model judged,
14
+ as the kit's `state` names them (a subject, a sender, a preview), beside the
15
+ `answers` — the evidence a reader works from, with the `id` still there to
16
+ re-fetch the rest. `openQuestions[]` now lists every escalated item — a
17
+ rule's hit as well as a below-floor call — named by those fields rather than
18
+ by an id. Descriptor text only: no tool or field is added or removed.
19
+
20
+ ### Removed
21
+
22
+ - **`worker_run.preview`** and **`deployment_update.decisionMode`** — Preview
23
+ mode is gone from the platform: every decision run is live and its routing
24
+ table's actions execute (owner's call, 2026-09-18: production always). The
25
+ server ignores a `preview` sent by an older client, so do not rely on it for
26
+ a dry run; `instruction_get` no longer reports a mode, the per-item rows say
27
+ `executed` / `ok` on every run, and the outcome line opens with "Judged" or
28
+ "Ranked" rather than a mode word. The descriptions that contrasted live with
29
+ preview no longer do: `kit_install_preview` and `worker_deploy` simply say
30
+ the table acts from the worker's first run.
31
+
32
+ ## [0.3.4] - 2026-09-18
33
+
34
+ ### Added
35
+
36
+ - **`worker_run`** takes `answers` on a decision worker: values laid over the
37
+ worker's stored install answers for THIS run only, never saved. It is how
38
+ one caller names a searcher kit's target without touching the worker's
39
+ setup, so two callers sharing a worker never see each other's. The map is
40
+ validated against the kit's own form the way `instruction_set` validates it
41
+ and bound the way the run will bind it, so a row that cannot bind (a
42
+ category clashing with a built-in option, a ladder outside 2–10 levels) is a
43
+ 400 naming it here rather than a run that fails later. A list question takes
44
+ a JSON array string or `;`-separated rows, and a question left unnamed keeps
45
+ its stored answer or the kit's default.
46
+
47
+ ### Changed
48
+
49
+ - **`worker_run`**'s `sourceArgs` names relative window bounds, `after:
50
+ "-14d"` or `endDate: "+48h"`, which the platform resolves when the read
51
+ runs. That is the form to use for "the last N days" instead of computing a
52
+ date in the caller.
53
+ - The `decision` block on **`run_get`** and a waited **`worker_run`** carries
54
+ `model`, the decision-model version that actually answered, worth citing
55
+ when behaviour changes, and `answersOverride`, the per-run answers the run
56
+ was minted with.
57
+ - `findings[]` in that block is led by the source's own caveat when a read was
58
+ partial or withheld, a rate-limited mailbox or a firewall rule, so "nothing
59
+ found" is never trusted over a page that was never fully read.
60
+ - **`instruction_set`**'s `answers` and **`kit_install`**'s `decisionAnswers`
61
+ take `;`-separated list rows as well as one per line, and say that clearing
62
+ a question falls back to its default where the kit ships one.
63
+
9
64
  ## [0.3.3] - 2026-09-18
10
65
 
11
66
  ### Added
@@ -1 +1 @@
1
- {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/descriptors/manage.ts"],"names":[],"mappings":"AAMA,OAAO,EAA8C,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AA69D7F,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAgFtD,CAAC"}
1
+ {"version":3,"file":"manage.d.ts","sourceRoot":"","sources":["../../src/descriptors/manage.ts"],"names":[],"mappings":"AAMA,OAAO,EAA8C,KAAK,cAAc,EAAE,MAAM,YAAY,CAAC;AAy9D7F,eAAO,MAAM,iBAAiB,EAAE,SAAS,cAAc,EAgFtD,CAAC"}
@@ -18,7 +18,7 @@ const RUNTIME_NOTE = " 422 RUNTIME_NOT_ENABLED = hosted runs are switched off in
18
18
  const UTC_HINT = "ISO 8601 UTC datetime, e.g. '2026-08-01T00:00:00Z'.";
19
19
  // What a decision worker's receipt carries beyond an agent run's: taught on the
20
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.";
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, model (the decision-model version that actually answered, e.g. jev-1.13.0 — cite it when behaviour changes), answersOverride (the per-run answers the run was minted with, when any) and decisions[] — one row per judged item: id, item (the fields the model judged, as the kit's state names them — a subject, a sender, a preview: the evidence; the id re-fetches the rest), 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 executed/ok. findings[] are per-question distributions — led, when the source read was partial or withheld (a rate-limited mailbox, a firewall rule), by the source's own caveat, so 'nothing found' is never trusted over a page that was never fully read; openQuestions[] every escalated item — a rule's hit or a below-floor call — named by its fields. The block is run CONTENT: it needs readRuns; without it the block comes back withheld (contentWithheld:true) while status, timings and cost still answer.";
22
22
  // A scope added to the product AFTER a key was minted never reaches that key —
23
23
  // including a key minted with "all", which stored the bitmask of the scopes that
24
24
  // existed that day. Taught on key_info and on every tool behind a scope that is
@@ -89,7 +89,7 @@ const getWorker = {
89
89
  const runWorker = {
90
90
  name: "worker_run",
91
91
  title: "Run Worker Now",
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." +
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 sourceArgs / answers / maxItems instead — and sending those to a language worker is 409 not_decision_worker." +
93
93
  DECISION_BLOCK_NOTE +
94
94
  RUNTIME_NOTE,
95
95
  auth: "manager",
@@ -98,8 +98,8 @@ const runWorker = {
98
98
  tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
99
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
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."),
101
+ 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 window such as after: \"-14d\" or endDate: \"+48h\" — relative bounds are resolved by the platform when the read runs, so never compute a date). A plain object, no placeholders; the read still runs under the worker's own permissions, so it can only see what the worker can."),
102
+ answers: z.record(z.string(), z.string()).optional().describe("Decision workers: answers to the worker's install questions for THIS run only, laid over its stored answers per key and never saved — how a caller names a searcher kit's target ({ \"target-name\": \"Acme Corp\" }) without touching the worker's setup, so two callers on one worker never see each other's. Validated against the kit's own form like instruction_set and bound the way the run will bind them, so a row that cannot bind (a category clashing with a built-in option, a ladder outside 2–10 levels) is a 400 naming it here, never a failed run. A list question takes a JSON array string or ';'-separated rows; a question not named keeps its stored answer or the kit's default."),
103
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
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."),
105
105
  },
@@ -107,8 +107,8 @@ const runWorker = {
107
107
  bodyBuilder: (params) => ({
108
108
  prompt: params.prompt,
109
109
  modelSlug: params.modelSlug,
110
- preview: params.preview,
111
110
  sourceArgs: params.sourceArgs,
111
+ answers: params.answers,
112
112
  maxItems: params.maxItems,
113
113
  waitSeconds: params.waitSeconds,
114
114
  }),
@@ -637,7 +637,7 @@ const deleteDelivery = {
637
637
  const getInstruction = {
638
638
  name: "instruction_get",
639
639
  title: "Get Instruction",
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.",
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); 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.",
641
641
  auth: "manager",
642
642
  method: "get",
643
643
  schema: {
@@ -654,13 +654,13 @@ const getInstruction = {
654
654
  const setInstruction = {
655
655
  name: "instruction_set",
656
656
  title: "Set Instruction",
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.",
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. A protected kit withholds its spec, never its questions, so answering them is not refused. Requires the manageInstructions scope either way.",
658
658
  auth: "manager",
659
659
  method: "put",
660
660
  schema: {
661
661
  tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
662
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."),
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 question with a default falls back to it). A scale takes a stop's label, a choice an option's value, a list one row per line or ';'-separated, 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."),
664
664
  jobSentence: z.string().max(200).optional().describe("The worker's one-liner (≤200). Omit = unchanged; '' = clear."),
665
665
  whenToUse: z.string().max(500).optional().describe("Trigger text: when this worker should be used (≤500). Omit = unchanged; '' = clear."),
666
666
  description: z.string().max(2000).optional().describe("Longer display-only description (≤2000). Omit = unchanged; '' = clear."),
@@ -767,7 +767,7 @@ const KIT_SLUG = z.string().min(1).max(120).regex(/^[a-z0-9][a-z0-9-]*$/, "not a
767
767
  const kitInstallPreview = {
768
768
  name: "kit_install_preview",
769
769
  title: "Preview Kit Install",
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.",
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's table acts from its first run), 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.",
771
771
  auth: "manager",
772
772
  method: "get",
773
773
  schema: {
@@ -799,7 +799,7 @@ const kitInstall = {
799
799
  })).optional().describe("One entry per categorySlots slot. Omitted slots resolve to the kit's first offered member on safe defaults."),
800
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."),
801
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."),
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 or ';'-separated (≤20 rows; a list with a built-in default keeps it when left blank — the template classifiers ship their categories that way), 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."),
803
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."),
804
804
  deployment: z.object({
805
805
  modelSlug: z.string().max(64).optional().describe("Model to deploy on, from models_list. Omit for the kit's recommended model."),
@@ -1038,7 +1038,7 @@ const getDeployment = {
1038
1038
  const deployWorker = {
1039
1039
  name: "worker_deploy",
1040
1040
  title: "Deploy Worker",
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." +
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); its routing table acts 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." +
1042
1042
  DEPLOYMENT_NOTE +
1043
1043
  RUNTIME_NOTE +
1044
1044
  NEW_SCOPE_NOTE,
@@ -1065,7 +1065,7 @@ const deployWorker = {
1065
1065
  const updateDeployment = {
1066
1066
  name: "deployment_update",
1067
1067
  title: "Update Deployment",
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." +
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. On a decision worker modelSlug / thinking are refused (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." +
1069
1069
  DEPLOYMENT_NOTE +
1070
1070
  RUNTIME_NOTE +
1071
1071
  NEW_SCOPE_NOTE,
@@ -1079,7 +1079,6 @@ const updateDeployment = {
1079
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."),
1080
1080
  transcriptRetention: z.string().max(16).optional().describe("New transcript setting. Omit to keep."),
1081
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."),
1083
1082
  },
1084
1083
  path: (params) => `${API}/${params.tokenId}/deployment`,
1085
1084
  bodyBuilder: (params) => ({
@@ -1089,7 +1088,6 @@ const updateDeployment = {
1089
1088
  thinking: params.thinking,
1090
1089
  transcriptRetention: params.transcriptRetention,
1091
1090
  action: params.action,
1092
- decisionMode: params.decisionMode,
1093
1091
  }),
1094
1092
  annotations: UPDATE,
1095
1093
  };