@workerkit/core 0.3.2 → 0.3.4
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 +97 -0
- 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/manage.d.ts.map +1 -1
- package/dist/descriptors/manage.js +56 -28
- package/dist/descriptors/manage.js.map +1 -1
- package/package.json +1 -1
|
@@ -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, 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, 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 — 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[] 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",
|
|
@@ -72,7 +75,7 @@ const accountUsage = {
|
|
|
72
75
|
const getWorker = {
|
|
73
76
|
name: "worker_get",
|
|
74
77
|
title: "Get Worker",
|
|
75
|
-
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, 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).",
|
|
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).",
|
|
76
79
|
auth: "manager",
|
|
77
80
|
method: "get",
|
|
78
81
|
schema: {
|
|
@@ -86,17 +89,31 @@ const getWorker = {
|
|
|
86
89
|
const runWorker = {
|
|
87
90
|
name: "worker_run",
|
|
88
91
|
title: "Run Worker Now",
|
|
89
|
-
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." +
|
|
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 / answers / maxItems instead — and sending those to a language worker is 409 not_decision_worker." +
|
|
93
|
+
DECISION_BLOCK_NOTE +
|
|
90
94
|
RUNTIME_NOTE,
|
|
91
95
|
auth: "manager",
|
|
92
96
|
method: "post",
|
|
93
97
|
schema: {
|
|
94
98
|
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
95
|
-
prompt: z.string().max(8000).optional().describe("
|
|
96
|
-
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 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."),
|
|
103
|
+
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."),
|
|
104
|
+
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."),
|
|
105
|
+
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."),
|
|
97
106
|
},
|
|
98
107
|
path: (params) => `${API}/${params.tokenId}/run`,
|
|
99
|
-
bodyBuilder: (params) => ({
|
|
108
|
+
bodyBuilder: (params) => ({
|
|
109
|
+
prompt: params.prompt,
|
|
110
|
+
modelSlug: params.modelSlug,
|
|
111
|
+
preview: params.preview,
|
|
112
|
+
sourceArgs: params.sourceArgs,
|
|
113
|
+
answers: params.answers,
|
|
114
|
+
maxItems: params.maxItems,
|
|
115
|
+
waitSeconds: params.waitSeconds,
|
|
116
|
+
}),
|
|
100
117
|
annotations: TRIGGER,
|
|
101
118
|
};
|
|
102
119
|
const runWorkersBulk = {
|
|
@@ -194,6 +211,7 @@ const getRun = {
|
|
|
194
211
|
title: "Get Run",
|
|
195
212
|
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." +
|
|
196
213
|
SCORE_NOTE +
|
|
214
|
+
DECISION_BLOCK_NOTE +
|
|
197
215
|
RUNTIME_NOTE,
|
|
198
216
|
auth: "manager",
|
|
199
217
|
method: "get",
|
|
@@ -479,7 +497,7 @@ const deleteSchedule = {
|
|
|
479
497
|
// Where the PLATFORM sends a worker's report when a run finishes. The two facts an
|
|
480
498
|
// agent gets wrong by default: availability is judged at ACCOUNT level (never from
|
|
481
499
|
// the worker's own apps), and a receipt is not a guarantee.
|
|
482
|
-
const DELIVERY_NOTE = "
|
|
500
|
+
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.";
|
|
483
501
|
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).";
|
|
484
502
|
const DELIVERY_TARGET_SHAPE = {
|
|
485
503
|
to: z.array(z.string()).optional().describe("email: 1–10 recipient addresses. messaging: exactly one phone number. Unused by every other channel."),
|
|
@@ -621,25 +639,30 @@ const deleteDelivery = {
|
|
|
621
639
|
const getInstruction = {
|
|
622
640
|
name: "instruction_get",
|
|
623
641
|
title: "Get Instruction",
|
|
624
|
-
description: "
|
|
642
|
+
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.",
|
|
625
643
|
auth: "manager",
|
|
626
644
|
method: "get",
|
|
627
645
|
schema: {
|
|
628
646
|
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
647
|
+
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."),
|
|
629
648
|
},
|
|
630
649
|
path: (params) => `${API}/${params.tokenId}/instruction`,
|
|
631
|
-
paramFilter: () =>
|
|
650
|
+
paramFilter: (params) => {
|
|
651
|
+
const { tokenId: _tokenId, ...query } = params;
|
|
652
|
+
return query;
|
|
653
|
+
},
|
|
632
654
|
annotations: READ_ONLY,
|
|
633
655
|
};
|
|
634
656
|
const setInstruction = {
|
|
635
657
|
name: "instruction_set",
|
|
636
658
|
title: "Set Instruction",
|
|
637
|
-
description: "
|
|
659
|
+
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.",
|
|
638
660
|
auth: "manager",
|
|
639
661
|
method: "put",
|
|
640
662
|
schema: {
|
|
641
663
|
tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
|
|
642
|
-
content: z.string().min(1).max(100000).describe("
|
|
664
|
+
content: z.string().min(1).max(100000).optional().describe("Language workers: the instruction text (1–100,000 chars) — replaces the whole content."),
|
|
665
|
+
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."),
|
|
643
666
|
jobSentence: z.string().max(200).optional().describe("The worker's one-liner (≤200). Omit = unchanged; '' = clear."),
|
|
644
667
|
whenToUse: z.string().max(500).optional().describe("Trigger text: when this worker should be used (≤500). Omit = unchanged; '' = clear."),
|
|
645
668
|
description: z.string().max(2000).optional().describe("Longer display-only description (≤2000). Omit = unchanged; '' = clear."),
|
|
@@ -649,6 +672,7 @@ const setInstruction = {
|
|
|
649
672
|
path: (params) => `${API}/${params.tokenId}/instruction`,
|
|
650
673
|
bodyBuilder: (params) => ({
|
|
651
674
|
content: params.content,
|
|
675
|
+
answers: params.answers,
|
|
652
676
|
jobSentence: params.jobSentence,
|
|
653
677
|
whenToUse: params.whenToUse,
|
|
654
678
|
description: params.description,
|
|
@@ -745,7 +769,7 @@ const KIT_SLUG = z.string().min(1).max(120).regex(/^[a-z0-9][a-z0-9-]*$/, "not a
|
|
|
745
769
|
const kitInstallPreview = {
|
|
746
770
|
name: "kit_install_preview",
|
|
747
771
|
title: "Preview Kit Install",
|
|
748
|
-
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.",
|
|
772
|
+
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.",
|
|
749
773
|
auth: "manager",
|
|
750
774
|
method: "get",
|
|
751
775
|
schema: {
|
|
@@ -762,7 +786,7 @@ const kitInstallPreview = {
|
|
|
762
786
|
const kitInstall = {
|
|
763
787
|
name: "kit_install",
|
|
764
788
|
title: "Install Kit",
|
|
765
|
-
description: "Install a directory kit as a NEW worker on this account. NOT idempotent: every
|
|
789
|
+
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.",
|
|
766
790
|
auth: "manager",
|
|
767
791
|
method: "post",
|
|
768
792
|
schema: {
|
|
@@ -777,6 +801,7 @@ const kitInstall = {
|
|
|
777
801
|
})).optional().describe("One entry per categorySlots slot. Omitted slots resolve to the kit's first offered member on safe defaults."),
|
|
778
802
|
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."),
|
|
779
803
|
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."),
|
|
804
|
+
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."),
|
|
780
805
|
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."),
|
|
781
806
|
deployment: z.object({
|
|
782
807
|
modelSlug: z.string().max(64).optional().describe("Model to deploy on, from models_list. Omit for the kit's recommended model."),
|
|
@@ -794,6 +819,7 @@ const kitInstall = {
|
|
|
794
819
|
categoryChoices: params.categoryChoices,
|
|
795
820
|
inputs: params.inputs,
|
|
796
821
|
memoryAnswers: params.memoryAnswers,
|
|
822
|
+
decisionAnswers: params.decisionAnswers,
|
|
797
823
|
deploy: params.deploy,
|
|
798
824
|
deployment: params.deployment,
|
|
799
825
|
}),
|
|
@@ -806,7 +832,7 @@ const kitInstall = {
|
|
|
806
832
|
// chat message is a different power and stays on the dashboard and the kit
|
|
807
833
|
// pipeline, where a manifest gets reviewed. All three need the createWorkers
|
|
808
834
|
// scope.
|
|
809
|
-
const CLONE_NOTE = "
|
|
835
|
+
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.";
|
|
810
836
|
const CLONE_TITLE_HINT = "The new worker's title, 1–120 chars. Required: a clone is never silently named after its source.";
|
|
811
837
|
// One shape, three tools: worker_clone and worker_clone_preview take it as their
|
|
812
838
|
// body, worker_clone_bulk takes an array of it. Every include flag defaults to
|
|
@@ -824,7 +850,7 @@ const CLONE_REQUEST_SHAPE = {
|
|
|
824
850
|
const clonePreview = {
|
|
825
851
|
name: "worker_clone_preview",
|
|
826
852
|
title: "Preview Worker Clone",
|
|
827
|
-
description: "What cloning this worker WOULD produce
|
|
853
|
+
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." +
|
|
828
854
|
NEW_SCOPE_NOTE +
|
|
829
855
|
CLONE_NOTE,
|
|
830
856
|
auth: "manager",
|
|
@@ -849,7 +875,7 @@ const clonePreview = {
|
|
|
849
875
|
const cloneWorker = {
|
|
850
876
|
name: "worker_clone",
|
|
851
877
|
title: "Clone Worker",
|
|
852
|
-
description: "Copy this worker into a NEW one (201 with the created worker). NOT IDEMPOTENT: every
|
|
878
|
+
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." +
|
|
853
879
|
NEW_SCOPE_NOTE +
|
|
854
880
|
CLONE_NOTE,
|
|
855
881
|
auth: "manager",
|
|
@@ -873,7 +899,7 @@ const cloneWorker = {
|
|
|
873
899
|
const cloneWorkerBulk = {
|
|
874
900
|
name: "worker_clone_bulk",
|
|
875
901
|
title: "Clone Worker in Bulk",
|
|
876
|
-
description: "Clone this worker several times in ONE call — how a fleet of eighteen becomes a fleet of a hundred.
|
|
902
|
+
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." +
|
|
877
903
|
NEW_SCOPE_NOTE +
|
|
878
904
|
CLONE_NOTE,
|
|
879
905
|
auth: "manager",
|
|
@@ -895,7 +921,7 @@ const BUDGET_WINDOW_NOTE = " Windows differ, deliberately: maxUsdPerDay and both
|
|
|
895
921
|
const getWorkerBudget = {
|
|
896
922
|
name: "budget_get",
|
|
897
923
|
title: "Get Worker Budget",
|
|
898
|
-
description: "One worker's spend and rate ceilings: maxUsdPerRun, maxUsdPerDay, maxRunsPerDay, maxConcurrentRuns, modelSlug, hasDeployment (plus workerId and its deprecated alias tokenId). Four readings
|
|
924
|
+
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." +
|
|
899
925
|
BUDGET_WINDOW_NOTE +
|
|
900
926
|
NEW_SCOPE_NOTE,
|
|
901
927
|
auth: "manager",
|
|
@@ -910,7 +936,7 @@ const getWorkerBudget = {
|
|
|
910
936
|
const setWorkerBudget = {
|
|
911
937
|
name: "budget_set",
|
|
912
938
|
title: "Set Worker Budget",
|
|
913
|
-
description: "Change one worker's ceilings
|
|
939
|
+
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." +
|
|
914
940
|
BUDGET_WINDOW_NOTE +
|
|
915
941
|
NEW_SCOPE_NOTE,
|
|
916
942
|
auth: "manager",
|
|
@@ -972,11 +998,11 @@ const setFleetBudget = {
|
|
|
972
998
|
// and completely inert: no schedule fires and worker_run answers 409
|
|
973
999
|
// not_deployed. Deploying is also where the model and the spend ceilings are
|
|
974
1000
|
// chosen, which is why the writes sit behind their own scope.
|
|
975
|
-
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
|
|
1001
|
+
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.";
|
|
976
1002
|
const listModels = {
|
|
977
1003
|
name: "models_list",
|
|
978
1004
|
title: "List Deployable Models",
|
|
979
|
-
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." +
|
|
1005
|
+
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." +
|
|
980
1006
|
RUNTIME_NOTE,
|
|
981
1007
|
auth: "manager",
|
|
982
1008
|
method: "get",
|
|
@@ -1014,7 +1040,7 @@ const getDeployment = {
|
|
|
1014
1040
|
const deployWorker = {
|
|
1015
1041
|
name: "worker_deploy",
|
|
1016
1042
|
title: "Deploy Worker",
|
|
1017
|
-
description: "Put the worker on the hosted runtime —
|
|
1043
|
+
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." +
|
|
1018
1044
|
DEPLOYMENT_NOTE +
|
|
1019
1045
|
RUNTIME_NOTE +
|
|
1020
1046
|
NEW_SCOPE_NOTE,
|
|
@@ -1041,7 +1067,7 @@ const deployWorker = {
|
|
|
1041
1067
|
const updateDeployment = {
|
|
1042
1068
|
name: "deployment_update",
|
|
1043
1069
|
title: "Update Deployment",
|
|
1044
|
-
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." +
|
|
1070
|
+
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." +
|
|
1045
1071
|
DEPLOYMENT_NOTE +
|
|
1046
1072
|
RUNTIME_NOTE +
|
|
1047
1073
|
NEW_SCOPE_NOTE,
|
|
@@ -1055,6 +1081,7 @@ const updateDeployment = {
|
|
|
1055
1081
|
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."),
|
|
1056
1082
|
transcriptRetention: z.string().max(16).optional().describe("New transcript setting. Omit to keep."),
|
|
1057
1083
|
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."),
|
|
1084
|
+
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."),
|
|
1058
1085
|
},
|
|
1059
1086
|
path: (params) => `${API}/${params.tokenId}/deployment`,
|
|
1060
1087
|
bodyBuilder: (params) => ({
|
|
@@ -1064,6 +1091,7 @@ const updateDeployment = {
|
|
|
1064
1091
|
thinking: params.thinking,
|
|
1065
1092
|
transcriptRetention: params.transcriptRetention,
|
|
1066
1093
|
action: params.action,
|
|
1094
|
+
decisionMode: params.decisionMode,
|
|
1067
1095
|
}),
|
|
1068
1096
|
annotations: UPDATE,
|
|
1069
1097
|
};
|
|
@@ -1126,7 +1154,7 @@ const LISTING_SCHEMA = {
|
|
|
1126
1154
|
declaredModelFloor: z.string().max(80).optional().describe("Display-only minimum model (≤80)."),
|
|
1127
1155
|
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."),
|
|
1128
1156
|
};
|
|
1129
|
-
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.
|
|
1157
|
+
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.");
|
|
1130
1158
|
const getMyPublisher = {
|
|
1131
1159
|
name: "publisher_get_mine",
|
|
1132
1160
|
title: "Get My Publisher",
|
|
@@ -1453,7 +1481,7 @@ const getMcpServer = {
|
|
|
1453
1481
|
const createMcpServer = {
|
|
1454
1482
|
name: "mcp_server_create",
|
|
1455
1483
|
title: "Register MCP Server",
|
|
1456
|
-
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
|
|
1484
|
+
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." +
|
|
1457
1485
|
NEW_SCOPE_NOTE,
|
|
1458
1486
|
auth: "manager",
|
|
1459
1487
|
method: "post",
|