@workerkit/core 0.3.18 → 0.3.20

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.
@@ -1,9 +1,10 @@
1
1
  // Generated from the backend agent contract. Edit the backend source and re-import; do not edit here.
2
- // The 100 authenticated fleet-management tools, as pure descriptors. The
2
+ // The authenticated fleet-management tools, as pure descriptors. The
3
3
  // descriptions ARE the product surface: they are served verbatim to MCP
4
4
  // clients (and any future CLI help), so every contract nuance an agent must
5
5
  // not get wrong is taught here.
6
6
  import { actionDescriptors } from "./actions.js";
7
+ import { apiAppDescriptors } from "./api-apps.js";
7
8
  import { appDiscoveryDescriptors } from "./app-discovery.js";
8
9
  import { compactDecisionReceipt } from "./decision-receipt.js";
9
10
  import { createDecisionWorker } from "./decision-authoring.js";
@@ -11,7 +12,7 @@ import { onboardingDescriptors } from "./onboarding.js";
11
12
  import { supportDescriptors } from "./support.js";
12
13
  import { qualityDescriptors } from "./quality.js";
13
14
  import { z } from "../zod.js";
14
- import { guidance, scopeReviewAction, runModelHint } from "../guidance.js";
15
+ import { guidance, scopeReviewAction, runModelHint, publicKitPublishingPause } from "../guidance.js";
15
16
  import { defineTool, CREATE, DELETE, READ_ONLY, TRIGGER, UPDATE } from "./types.js";
16
17
  // The graded-outcome vocabulary, taught wherever a score can be read or written.
17
18
  // The null-vs-zero line is the one an agent gets wrong by default: absent means
@@ -33,7 +34,7 @@ const HYBRID_NOTE = " Hybrid classifies a bounded window, then runs one language
33
34
  // Result interpretation is authored once and delivered with classified receipts.
34
35
  // REST and offline help retain it even when callers choose raw JSON.
35
36
  const CLASSIFIED_RESULT_DESCRIPTION = (DECISION_BLOCK_NOTE + HYBRID_NOTE).trim();
36
- const QUALITY_RESULT_DESCRIPTION = "qualityEvaluation separates worker-specific criteria from systemReview diagnostics. Unknown means unverified, not pass. A needs_attention system check includes a recommendation for owner review, not an applied fix. Neither layer replaces execution status or proves external effects.";
37
+ const QUALITY_RESULT_DESCRIPTION = "qualityEvaluation separates worker criteria, code-computed executionChecks and model systemReview diagnostics. Unknown means unverified, not pass. Runtime/app failure counts may overlap; do not add them. Recovered attempts remain recorded without failing business acceptance. needs_attention includes a recommendation for owner review, not an applied fix. None proves external effects.";
37
38
  // Raw HTTP and CLI --json bodies remain unchanged; MCP/plain CLI render footers.
38
39
  function classifiedReceiptGuidance(data) {
39
40
  if (!data || typeof data !== "object" || Array.isArray(data))
@@ -102,10 +103,56 @@ const accountUsage = defineTool({
102
103
  path: `${API}/account/usage`,
103
104
  annotations: READ_ONLY,
104
105
  });
106
+ const accountSpend = defineTool({
107
+ name: "account_spend",
108
+ title: "Account Spend",
109
+ description: "Read run spend this UTC day and month across reachable workers. {{scope}} Returns spentTodayUsd/spentThisMonthUsd, reservedInFlightUsd, runsToday/runsThisMonth/runsInFlight, byWorker (workers with runs or spend this month, highest first), byDay (this month per UTC day) and byModel (null modelSlug = classifier runs). Figures sum receipt modelCostUsd + byokFeeUsd across billing modes, including BYOK provider list cost and FreeRun usage; they are not wallet charges. walletChargedThisMonthUsd is what the wallet paid, appUsageThisMonthUsd its metered-app share; both null unless the key reaches the whole account. Ceilings: fleet_budget_get, budget_get." + RUNTIME_NOTE,
110
+ auth: "manager",
111
+ method: "get",
112
+ schema: {},
113
+ path: `${API}/account/spend`,
114
+ annotations: READ_ONLY,
115
+ });
116
+ // ─── Activity ───────────────────────────────────────────────────────────────
117
+ // The dashboard's activity feed: runs, the app calls workers make through the firewall, and
118
+ // configuration changes, merged newest first. Rows are one flat shape; money stays on receipts.
119
+ const ACTIVITY_WORKER_HINT = "Only this worker's activity, by workerId from workers_list. An unknown or unreachable id returns nothing.";
120
+ const activityFeed = defineTool({
121
+ name: "activity_feed",
122
+ title: "Activity Feed",
123
+ description: "Read reachable workers' runs, app calls and configuration changes in one newest-first feed. {{scope}} Items: kind run|call|change, at, outcome, title, detail, workerId/tokenId/workerTitle, app, runId, actor/actorName. outcome: ok; attention = run succeeded with friction or awaits input, or call allowed with data protected; failed; blocked = call denied by the firewall; skipped (runs, including canceled); running; info (changes). App calls start at callsSinceUtc, the plan's log window. Account-level changes need whole-account reach. Page with nextCursor while hasMore; on 400 invalid_cursor restart without it. No costs: account_spend.",
124
+ auth: "manager",
125
+ method: "get",
126
+ schema: {
127
+ kind: z.enum(["run", "call", "change"]).optional().describe("Only this kind: run, call (app calls workers made through the firewall) or change (configuration changes). Omit for all."),
128
+ outcome: z.enum(["ok", "attention", "failed", "blocked", "skipped", "running", "info"]).optional().describe("Only rows with this outcome. blocked applies to calls, skipped and running to runs, info to changes."),
129
+ workerId: z.string().uuid().optional().describe(ACTIVITY_WORKER_HINT),
130
+ since: z.string().optional().describe(`Only activity at or after this time. ${UTC_HINT}`),
131
+ until: z.string().optional().describe(`Only activity before this time. ${UTC_HINT}`),
132
+ cursor: z.string().optional().describe("Opaque cursor from the previous response's nextCursor; fetches the next (older) page. Omit to read the newest activity."),
133
+ limit: z.number().int().min(1).max(100).default(20).describe("Rows per page (1–100)."),
134
+ },
135
+ path: `${API}/activity`,
136
+ annotations: READ_ONLY,
137
+ });
138
+ const activitySummary = defineTool({
139
+ name: "activity_summary",
140
+ title: "Activity Summary",
141
+ description: "Count reachable workers' activity over a window in one call. {{scope}} Returns window (start, end, plan tier), runs (total, running, succeeded, attention, failed, skipped), calls (total, allowed, protected, blocked, failed), changes, and the busiest apps (byApp) and tools (byTool) with the same call counts. protected = allowed with data masked or redacted; blocked = denied by the firewall. The window starts at since, or at the plan's log window when since is omitted or older, and ends at until or now. Rows: activity_feed.",
142
+ auth: "manager",
143
+ method: "get",
144
+ schema: {
145
+ workerId: z.string().uuid().optional().describe(ACTIVITY_WORKER_HINT),
146
+ since: z.string().optional().describe(`Window start; omit for the plan's full log window. ${UTC_HINT}`),
147
+ until: z.string().optional().describe(`Window end; omit for now. ${UTC_HINT}`),
148
+ },
149
+ path: `${API}/activity/summary`,
150
+ annotations: READ_ONLY,
151
+ });
105
152
  const getWorker = defineTool({
106
153
  name: "worker_get",
107
154
  title: "Get Worker",
108
- description: "Read worker detail, including all workers_list fields, timeZoneId, maxRunsPerDay, kit templateId/slug/name, hasInstruction, isProtected and jobSentence. {{scope}} readiness is ready|blocked with issues (worker_inactive, no_identity, app_not_connected with candidateProviders, no_instruction, action_connection_unavailable/action_tools_unavailable, worker_slots_pending when a required worker slot is unbound); canRun means hosted readiness, canInvoke means Action readiness. modelType: language runs prose on a model; decision runs a routing table (instruction_get/worker_run; decisionPendingSetup names unanswered setup); hybrid classifies then passes selected items to one language agent; none is Action (no model, deployment or run; worker_run/worker_deploy return 400 action_worker_requires_invoke; use worker_tools/worker_tool_call). apps[] lists enabled apps, providers and connected|needs_connection|unknown; fix needs_connection with app_connect on this worker's operator or dashboard. deployment has status/modelSlug; null means no hosted execution regardless of readiness (worker_deploy fixes it, except Action). Activity covers 30-day outcomes/success rate/cost; null when hosted runtime is disabled.",
155
+ description: "Read worker detail, including all workers_list fields, timeZoneId, maxRunsPerDay, kit templateId/slug/name, hasInstruction, isProtected and jobSentence. {{scope}} readiness is ready|blocked with issues (worker_inactive, no_identity, app_not_connected with candidateProviders, no_instruction, action_connection_unavailable/action_tools_unavailable, worker_slots_pending when a required worker slot is unbound: worker_bindings_set fills it); canRun means hosted readiness, canInvoke means Action readiness. modelType: language runs prose on a model; decision runs a routing table (instruction_get/worker_run; decisionPendingSetup names unanswered setup); hybrid classifies then passes selected items to one language agent; none is Action (no model, deployment or run; worker_run/worker_deploy return 400 action_worker_requires_invoke; use worker_tools/worker_tool_call). apps[] lists enabled apps, providers and connected|needs_connection|unknown; fix needs_connection with app_connect on this worker's operator or dashboard. deployment has status/modelSlug; null means no hosted execution regardless of readiness (worker_deploy fixes it, except Action). Activity covers 30-day outcomes/success rate/cost; null when hosted runtime is disabled.",
109
156
  auth: "manager",
110
157
  method: "get",
111
158
  schema: {
@@ -249,7 +296,7 @@ const getRun = defineTool({
249
296
  resultDescription: `${CLASSIFIED_RESULT_DESCRIPTION}\n\n${QUALITY_RESULT_DESCRIPTION}`,
250
297
  name: "run_get",
251
298
  title: "Get Run",
252
- description: "Read a run receipt. includeContent:false returns metadata and bounded contentReferences without copying reports; resolve report/structured with run_get(includeContent:true), transcript with run_transcript, using the same runId and current readRuns/worker authority. References grant no access. Transcript expiry is enforced at read time; reports follow receipt retention and explicit clearing. digestStructured.result is an optional v1 JSON business result: data, evidence, unresolved, completeness and self-reported acceptance. validationError means its shape failed, independently of execution status; Succeeded can still have partial coverage or a rejected business result. " + guidance.spendCap + " Fields: initiator (immediate user/fleetKey/schedule/webhook/worker; historicalUnknown when unrecorded), origin (original run and initiator), relationship (questionContinuation or workerCall), workerCalls (bounded model/Action references, modelType, operation and statuses; runId for model children or actionInvocationId/actionTool for existing Action receipts; canReadReceipt reflects existing receipt authority), status, trigger, timings, token/cost metering, billingMode Platform|Byok, byokFeeUsd, finalDigest (worker report), runPrompt/runContext, in-flight liveness live|stalled/lastActivity, errorCode/errorDetail and issues. {{scope}} Fleet-key identity is a safe id/name/prefix snapshot at start, not a credential or proof of API/CLI/OAuth transport; requestedByUserId remains the accountable user. Continuations preserve origin and record the answering key as initiator. Worker calls identify the immediate caller run and original initiator; child runs have separate charges. A worker binding shares its bounded invocation result, not full receipt/transcript access. Configure grants in Apps and workers, or explicitly bind declared kit slots during an authorized kit_install; manager run permission alone does not grant binding authority. On BYOK, modelCostUsd is provider list price, not wallet charge. Parse footprint, turnsTimeline, issues and priceSnapshot: they are raw JSON strings. Grades: selfScore, selfScoreReason, ownerScore (set with run_score)." + SCORE_NOTE + " Decision/hybrid receipts include classification, source coverage and committed wallet cost. Hybrid runs are manual/on-demand only; never automatically repeat an uncertain write." + RUNTIME_NOTE,
299
+ description: "Read a run receipt. includeContent:false returns metadata and bounded contentReferences without copying reports; resolve report/structured with run_get(includeContent:true), transcript with run_transcript, using the same runId and current readRuns/worker authority. References grant no access. Transcript expiry is enforced at read time; reports follow receipt retention and explicit clearing. digestStructured.result is an optional v1 JSON business result: data, evidence, unresolved, completeness and self-reported acceptance. validationError means its shape failed, independently of execution status; Succeeded can still have partial coverage or a rejected business result. " + guidance.spendCap + " Fields: initiator (immediate user/fleetKey/schedule/webhook/worker; historicalUnknown when unrecorded), origin (original run and initiator), relationship (questionContinuation or workerCall), workerCalls (bounded model/Action references, modelType, operation and statuses; runId for model children or actionInvocationId/actionTool for existing Action receipts; canReadReceipt reflects existing receipt authority), status, trigger, timings, token/cost metering, billingMode Platform|Byok, byokFeeUsd, finalDigest (worker report), runPrompt/runContext, in-flight liveness live|stalled/lastActivity, errorCode/errorDetail and issues. {{scope}} Fleet-key identity is a safe id/name/prefix snapshot at start, not a credential or proof of API/CLI/OAuth transport; requestedByUserId remains the accountable user. Continuations preserve origin and record the answering key as initiator. Worker calls identify the immediate caller run and original initiator; child runs have separate charges. A worker binding shares its bounded invocation result, not full receipt/transcript access. Grants are set with worker_bindings_set (manageAppsAndWorkers) or kit_install.workerBindings (installKits); run permission alone grants none. On BYOK, modelCostUsd is provider list price, not wallet charge. Parse footprint, turnsTimeline, issues and priceSnapshot: they are raw JSON strings. Grades: selfScore, selfScoreReason, ownerScore (set with run_score)." + SCORE_NOTE + " Decision/hybrid receipts include classification, source coverage and committed wallet cost. Hybrid runs are manual/on-demand only; never automatically repeat an uncertain write." + RUNTIME_NOTE,
253
300
  auth: "manager",
254
301
  method: "get",
255
302
  schema: {
@@ -856,7 +903,7 @@ const GITHUB_SELECTION = z.object({
856
903
  const kitInstallPreview = defineTool({
857
904
  name: "kit_install_preview",
858
905
  title: "Preview Kit Install",
859
- description: "Preview the install form and account readiness; creates nothing. Call before kit_install and collect required answers. {{scope}} requiredInputs: EVERY exact key needs a human-supplied value. memorySetup: only required:true answers are mandatory. modelType language|decision|hybrid|none; decisionNarration explains classification, which acts from the first run without a chosen language model. Answer decisionSetup by key in decisionAnswers: required questions without defaults are mandatory, except appPick can wait for instruction_set. Action: actionSpec lists tools, no model/deployment; actionConnections lists Slack bots to pin via slackConnectionId. workerSlots lists the workers this kit calls (alias, modelType, operations, required): fill them with installed workers in kit_install.workerBindings, or later in Apps and workers. Choose ONE member/categorySlot in categoryChoices, preferably connected; connectionProvider requires matching operatorResources.resourceId, preferably hasActiveConnection. apps/appsNeedingConnection predict initial connections; install may proceed blocked, with human setup at connectAppsUrl. githubAccounts.id is connectionId; githubSetup provides requiredScope, discovery tools and explicitSelectionRequired: discover repos/branches, then supply githubSelection selected IDs or accounts:[] to defer. operator identifies target; limits shows currentWorkers/maxWorkers (cap causes 402); kitPageUrl is the human install page. Advisory only: installed readiness is authoritative. kit_get supplies full permissions/instruction. evaluationSpec contains public criteria copied at install; all three quality/learning gates stay off. Review worker_quality_get after install.",
906
+ description: "Preview the install form and account readiness; creates nothing. Call before kit_install and collect required answers. {{scope}} requiredInputs: EVERY exact key needs a human-supplied value. memorySetup: only required:true answers are mandatory. modelType language|decision|hybrid|none; decisionNarration explains classification, which acts from the first run without a chosen language model. Answer decisionSetup by key in decisionAnswers: required questions without defaults are mandatory, except appPick can wait for instruction_set. Action: actionSpec lists tools, no model/deployment; actionConnections lists Slack bots to pin via slackConnectionId. workerSlots lists the workers this kit calls (alias, modelType, operations, required): fill them with installed workers in kit_install.workerBindings, or later with worker_bindings_set. Choose ONE member/categorySlot in categoryChoices, preferably connected; connectionProvider requires matching operatorResources.resourceId, preferably hasActiveConnection. apps/appsNeedingConnection predict initial connections; install may proceed blocked, with human setup at connectAppsUrl. githubAccounts.id is connectionId; githubSetup provides requiredScope, discovery tools and explicitSelectionRequired: discover repos/branches, then supply githubSelection selected IDs or accounts:[] to defer. operator identifies target; limits shows currentWorkers/maxWorkers (cap causes 402); kitPageUrl is the human install page. Advisory only: installed readiness is authoritative. kit_get supplies full permissions/instruction. evaluationSpec contains public criteria copied at install; all three quality/learning gates stay off. Review worker_quality_get after install.",
860
907
  auth: "manager",
861
908
  method: "get",
862
909
  schema: {
@@ -873,7 +920,7 @@ const kitInstallPreview = defineTool({
873
920
  const kitInstall = defineTool({
874
921
  name: "kit_install",
875
922
  title: "Install Kit",
876
- description: "Install a kit as a NEW worker. {{scope}} NOT idempotent: every success creates another; never retry success. After timeout, inspect workers_list (readWorkers) first. Preview first: supply every requiredInputs key in inputs (missing/unknown key: 400), required memoryAnswers, decisionAnswers and one categoryChoices entry/slot; connectionProvider also needs resourceId from operatorResources. Secrets shown ONCE: install.rawKey (worker pe_ key) and install.triggers[].signingSecret; give them to the human before discarding. Return workerUrl to the human. readiness ready|blocked includes actionable issues (app_not_connected/candidateProviders); null means the install SUCCEEDED but readiness check failed: worker_get, never reinstall. connectAppsUrl is human setup. 402 limit_exceeded is terminal: upgrade or, with human agreement, worker_delete (permanent; deleteWorkers). Rate: 10 installs/hour/account. Install never runs; without deployment no schedule fires and worker_run returns not_deployed. deploy:true optionally takes deployment(modelSlug from models_list, ceilings) ; missing deployment scope is 403 before creation. Omit deploy to install only, then seek authorized scope review before worker_deploy. Post-install deploy failure still returns 201/deploymentError: worker exists; fix and worker_deploy, never reinstall. Action(modelType none): omit deploy; worker_tools then worker_tool_call. Worker-slot kits (preview lists workerSlots): workerBindings maps each alias to an installed worker, which returns bounded results to the new worker. Install succeeds without bindings, but an unbound required slot reports readiness issue worker_slots_pending and runs are Skipped until it is bound in Apps and workers. Kit criteria copy with all three quality/learning gates off; review worker_quality_get afterwards. Install has no learning-consent options.",
923
+ description: "Install a kit as a NEW worker. {{scope}} NOT idempotent: every success creates another; never retry success. After timeout, inspect workers_list (readWorkers) first. Preview first: supply every requiredInputs key in inputs (missing/unknown key: 400), required memoryAnswers, decisionAnswers and one categoryChoices entry/slot; connectionProvider also needs resourceId from operatorResources. Install returns no worker API secret. Secret shown ONCE: install.triggers[].signingSecret; give it to the human before discarding. Return workerUrl to the human. readiness ready|blocked includes actionable issues (app_not_connected/candidateProviders); null means the install SUCCEEDED but readiness check failed: worker_get, never reinstall. connectAppsUrl is human setup. 402 limit_exceeded is terminal: upgrade or, with human agreement, worker_delete (permanent; deleteWorkers). Rate: 10 installs/hour/account. Install never runs; without deployment no schedule fires and worker_run returns not_deployed. deploy:true optionally takes deployment(modelSlug from models_list, ceilings) ; missing deployment scope is 403 before creation. Omit deploy to install only, then seek authorized scope review before worker_deploy. Post-install deploy failure still returns 201/deploymentError: worker exists; fix and worker_deploy, never reinstall. Action(modelType none): omit deploy; worker_tools then worker_tool_call. Worker-slot kits (preview lists workerSlots): workerBindings maps each alias to an installed worker, which returns bounded results to the new worker. Install succeeds without bindings, but an unbound required slot reports readiness issue worker_slots_pending and runs are Skipped until worker_bindings_set fills it. Kit criteria copy with all three quality/learning gates off; review worker_quality_get afterwards. Install has no learning-consent options.",
877
924
  auth: "manager",
878
925
  method: "post",
879
926
  schema: {
@@ -884,7 +931,7 @@ const kitInstall = defineTool({
884
931
  timeZoneId: z.string().trim().min(1).max(64).optional().describe("IANA timezone for local dates/reports, set before kit schedules. Ask the worker creator if unknown; never infer from calendars/server clock. Omitted means UTC. Response install.effectiveTimeZoneId confirms the applied zone."),
885
932
  slackConnectionId: z.number().int().positive().optional().describe("Action kits: pin a Slack bot from actionConnections in the install preview. A sole usable connection can be selected automatically; choose explicitly when several exist. No model/deployment."),
886
933
  githubSelection: GITHUB_SELECTION.optional().describe("GitHub targets from app_github_accounts/repos/branches (discoverAppResources); preview githubAccounts[].id is connectionId. For specific jobs use selected with 1-500 stable repositoryIds. all includes future granted repos: choose only intentionally. accounts:[] defers setup. Required on GitHub kits for keys with discoverAppResources; legacy keys keep single-account fallback. Discovery/branch inputs grant no access. These owner selections never enter a published kit manifest."),
887
- workerBindings: z.record(z.string().regex(/^[a-z][a-z0-9_]{0,19}$/), z.string().uuid()).optional().describe("Map of worker-slot alias (kit_install_preview.workerSlots) to the UUID of the installed worker that fills it. The target must be in the same account and within the caller's current owner/Admin authority and fleet-key selection; its modelType, operations and pinned contracts must match the slot. A binding shares the target's bounded results, never its private receipts. Omit to bind later in Apps and workers; an unbound required slot blocks runs. Unknown alias or refused binding: 400."),
934
+ workerBindings: z.record(z.string().regex(/^[a-z][a-z0-9_]{0,19}$/), z.string().uuid()).optional().describe("Map of worker-slot alias (kit_install_preview.workerSlots) to the UUID of the installed worker that fills it. The target must be in the same account and within the caller's current owner/Admin authority and fleet-key selection; its modelType, operations and pinned contracts must match the slot. A binding shares the target's bounded results, never its private receipts. Omit to bind later with worker_bindings_set; an unbound required slot blocks runs. Unknown alias or refused binding: 400."),
888
935
  categoryChoices: z.array(z.object({
889
936
  categoryCode: z.string().describe("The slot's categoryCode from the preview."),
890
937
  memberCode: z.string().describe("Member code (e.g. 'gmail'). Any member of the slot's category is accepted; one outside the author's shortlist uses an existing kit block for that app, otherwise safe defaults. Unknown category member:400."),
@@ -1184,11 +1231,14 @@ const undeployWorker = defineTool({
1184
1231
  successMessage: "Worker undeployed. It still exists and keeps its instruction, memory and schedules; deploy it again with worker_deploy to make it run.",
1185
1232
  annotations: DELETE,
1186
1233
  });
1187
- // ─── Permissions (read-only) ────────────────────────────────────────────────
1234
+ // ─── Permissions and worker grants ──────────────────────────────────────────
1235
+ // The two halves of the dashboard's Apps and workers drawer. Reads ride readWorkers;
1236
+ // the writes ride manageAppsAndWorkers (install-time workerBindings stay on installKits).
1237
+ // Rules, identities and MCP tool bindings stay on the dashboard.
1188
1238
  const getWorkerPermissions = defineTool({
1189
1239
  name: "worker_permissions_get",
1190
1240
  title: "Get Worker Permissions",
1191
- description: "Read exact worker access in kit-authoring vocabulary: app operations/fields/providers/stances, category slots, contact/timeframe/child limits and ruleCounts. {{scope}} ruleCounts reveals presence only, never rule values. No tool here edits grants: ask the human to change Apps and workers in the dashboard, then reread to verify; alternatively create different permissions via private kit_publish then kit_install. worker_clone preserves source rules; kit_publish(sourceWorkerId) does not, so kit installs can be MORE permissive than a source with rules.",
1241
+ description: "Read exact worker access in kit-authoring vocabulary: app operations/fields/providers/stances, category slots, contact/timeframe/child limits and ruleCounts. {{scope}} ruleCounts reveals presence only, never rule values. Edit and write back with worker_permissions_set (account Admin), or create different permissions via private kit_publish then kit_install. worker_clone preserves source rules; kit_publish(sourceWorkerId) does not, so kit installs can be MORE permissive than a source with rules.",
1192
1242
  auth: "manager",
1193
1243
  method: "get",
1194
1244
  schema: {
@@ -1198,6 +1248,67 @@ const getWorkerPermissions = defineTool({
1198
1248
  paramFilter: () => ({}),
1199
1249
  annotations: READ_ONLY,
1200
1250
  });
1251
+ const setWorkerPermissions = defineTool({
1252
+ name: "worker_permissions_set",
1253
+ title: "Set Worker Permissions",
1254
+ description: "Replace what the worker may touch, in the shape worker_permissions_get returns: apps is the whole app set, so an app left out is switched OFF. {{scope}} Account Admin only. An omitted axis keeps the worker's last saved value, so send every axis you mean. The stance changes only when sent: a deny-by-default app switched on here reads nothing until the owner writes rules or you send allowAll:true, and an existing allowAll is never flipped by omission. Rules, identities and MCP tool bindings stay on the dashboard. A scalar left out is unchanged; -1 clears a timeframe. 400 names the refusal: an Action worker, an unknown app or key, a Member-owned worker given an unsupported app, or a classifier losing a tool. Returns the effective permissions; confirm with the human before widening access." + NEW_SCOPE_NOTE,
1255
+ auth: "manager",
1256
+ method: "put",
1257
+ schema: {
1258
+ tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
1259
+ apps: z.array(z.record(z.unknown())).describe("One entry per app, the kit content.apps[] shape: code (kit_vocabulary) plus optional operations, visibleFields, allowAll, writeEnabled, providers, channels, writableFields, readOperations, masterAccessLevel, objectTypes, scopePolicy, contentTypeScopes, includeSpamFolders, pastDays, capabilities. Unknown app codes or vocabulary keys: 400. search_emails implies view_email and view_thread. github switched on here admits no repository until a human selects them in the dashboard."),
1260
+ contactAllowAll: z.boolean().optional().describe("Contact stance: true reads every sender except what contact rules block, false only what they allow. Omit to keep."),
1261
+ blockedSenderCategories: z.array(z.string()).optional().describe("financial|healthcare|government|insurance|hr_payroll; [] blocks none. Omit to keep."),
1262
+ timeframePastDays: z.number().int().min(-1).max(3650).optional().describe("Read window into the past, in days; -1 clears it. Omit to keep."),
1263
+ timeframeFutureDays: z.number().int().min(-1).max(3650).optional().describe("Read window into the future, in days; -1 clears it. Omit to keep."),
1264
+ maxChildTokens: z.number().int().min(0).optional().describe("Legacy spawned sub-worker budget; 0 = none. Omit to keep."),
1265
+ },
1266
+ path: (params) => `${API}/${params.tokenId}/permissions`,
1267
+ bodyBuilder: (params) => ({
1268
+ apps: params.apps,
1269
+ contactAllowAll: params.contactAllowAll,
1270
+ blockedSenderCategories: params.blockedSenderCategories,
1271
+ timeframePastDays: params.timeframePastDays,
1272
+ timeframeFutureDays: params.timeframeFutureDays,
1273
+ maxChildTokens: params.maxChildTokens,
1274
+ }),
1275
+ annotations: UPDATE,
1276
+ });
1277
+ const getWorkerBindings = defineTool({
1278
+ name: "worker_bindings_get",
1279
+ title: "Get Worker Bindings",
1280
+ description: "Read a worker's grants to call other workers: bindings[] (alias, target workerId and title, operations bits, revision, available), slots[] its kit declares, missingSlots[] (required slots that keep runs Skipped) and targets[] it may bind: same-account deployed Language, Decision or Hybrid workers and Action workers with tools, narrowed by search. {{scope}} Member keys see their own workers. Fill, re-point or clear an alias with worker_bindings_set.",
1281
+ auth: "manager",
1282
+ method: "get",
1283
+ schema: {
1284
+ tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
1285
+ search: z.string().max(100).optional().describe("Case-insensitive title filter for targets; at most 50 are listed."),
1286
+ },
1287
+ path: (params) => `${API}/${params.tokenId}/worker-bindings`,
1288
+ paramFilter: (params) => {
1289
+ const { tokenId: _tokenId, ...query } = params;
1290
+ return query;
1291
+ },
1292
+ annotations: READ_ONLY,
1293
+ });
1294
+ const setWorkerBindings = defineTool({
1295
+ name: "worker_bindings_set",
1296
+ title: "Set Worker Bindings",
1297
+ description: "Fill, re-point or clear a worker's grants by alias, atomically; aliases not named are untouched. {{scope}} A declared kit slot takes the slot's operations and tools when they are omitted; a free alias needs operations, and an Action target needs actionTools. One worker fills one alias per caller: clear the old alias first, or swap both in one call. An unchanged live grant is left alone; a stale one is refreshed. Member keys bind their own workers to their own workers. 400 names the refusal. Returns worker_bindings_get." + NEW_SCOPE_NOTE,
1298
+ auth: "manager",
1299
+ method: "patch",
1300
+ schema: {
1301
+ tokenId: z.number().int().min(1).describe(TOKEN_ID_HINT),
1302
+ workerBindings: z.record(z.string().regex(/^[a-z][a-z0-9_]{0,19}$/), z.object({
1303
+ workerId: z.string().uuid().describe("The worker that fills the alias (workers_list workerId): this account, within this key's reach."),
1304
+ operations: z.number().int().min(1).max(15).optional().describe("Bits: 1 trigger, 2 trigger with prompt, 4 call tool (Action targets, alone), 8 trigger with typed input. Omit on a declared slot to take the slot's."),
1305
+ actionTools: z.array(z.string().min(1).max(128)).min(1).max(8).optional().describe("Action targets: 1-8 of the target's saved tool refs from worker_tools. Omit on a declared slot to take the slot's."),
1306
+ }).strict().nullable()).describe("Alias to choice, or null to clear the alias. 1-10 aliases per call."),
1307
+ },
1308
+ path: (params) => `${API}/${params.tokenId}/worker-bindings`,
1309
+ bodyBuilder: (params) => ({ workerBindings: params.workerBindings }),
1310
+ annotations: UPDATE,
1311
+ });
1201
1312
  // ─── Kits (authoring) ───────────────────────────────────────────────────────
1202
1313
  //
1203
1314
  // The publishKits lane. A PRIVATE kit installed with kit_install is the kit route
@@ -1219,7 +1330,8 @@ const LISTING_SCHEMA = {
1219
1330
  jobSentence: z.string().min(1).max(200).describe("One-line outcome, 1-200 chars. With name, the ONLY free-text-searched field."),
1220
1331
  description: z.string().max(20000).optional().describe("Markdown listing description (≤20000; write it under 500: one line, then 3-5 bullets). Not searched."),
1221
1332
  categorySlugs: z.array(z.string()).min(1).max(8).describe("1-8 browse-category slugs from kit_vocabulary's categories (jobFamilies / roles / industries). An unknown slug fails the publish."),
1222
- visibility: z.enum(["public", "private"]).optional().describe("'private' (recommended first) = installable only by this account, no supply-chain scan; 'public' (default) = listed in the directory after the fail-closed scan."),
1333
+ visibility: z.enum(["public", "private"]).optional().describe(publicKitPublishingPause +
1334
+ "'private' (recommended first) = installable only by this account, no supply-chain scan; 'public' (default) = listed in the directory after the fail-closed scan."),
1223
1335
  isProtected: z.boolean().optional().describe("true withholds instruction, run commands, whenToUse and reference bodies; installers keep a working worker and permissions. Independent of visibility; new publishes default false (unprotected)."),
1224
1336
  publisherName: z.string().min(3).max(80).optional().describe("Required on this account's FIRST publish only (creates the publisher profile; globally unique, 409 if taken). Ignored afterwards — rename with publisher_set."),
1225
1337
  appDescriptions: z.array(z.record(z.unknown())).optional().describe("Per PINNED app: {code, description ≤600, tools:[{key, description ≤300}]} — every key must be an operation the kit grants; describing a slot member is a rejection. Shape: kit_authoring_guide section 'schema'."),
@@ -1277,7 +1389,8 @@ const listMyKits = defineTool({
1277
1389
  const validateKit = defineTool({
1278
1390
  name: "kit_validate",
1279
1391
  title: "Validate Kit",
1280
- description: "Dry-run the exact kit_publish body; creates nothing. {{scope}} canPublish is the verdict; errors/warnings identify every failing section (publishing stops at the first), lintWarnings are advisory. Includes inferred requiredInputs, appLabels, normalized memorySetup and exact manifestPreview. Validates as PUBLIC even for private bodies, except the public-kit cap follows visibility (private unlimited). Authored content only; sourceWorkerId is 400. For kit_replace, pass kitRef so the 100-public-kit cap and republish rules use that listing. Repeat until canPublish, then publish the identical body." + AUTHORING_FLOW_NOTE,
1392
+ description: publicKitPublishingPause +
1393
+ "Dry-run the exact kit_publish body; creates nothing. {{scope}} canPublish is the verdict; errors/warnings identify every failing section (publishing stops at the first), lintWarnings are advisory. Includes inferred requiredInputs, appLabels, normalized memorySetup and exact manifestPreview. Validates as PUBLIC even for private bodies, except the public-kit cap follows visibility (private unlimited). Authored content only; sourceWorkerId is 400. For kit_replace, pass kitRef so the 100-public-kit cap and republish rules use that listing. Repeat until canPublish, then publish the identical body." + AUTHORING_FLOW_NOTE,
1281
1394
  auth: "manager",
1282
1395
  method: "post",
1283
1396
  schema: {
@@ -1295,7 +1408,8 @@ const validateKit = defineTool({
1295
1408
  const publishKit = defineTool({
1296
1409
  name: "kit_publish",
1297
1410
  title: "Publish Kit",
1298
- description: "Publish a NEW kit. {{scope}} Exactly one of authored content or sourceWorkerId (reachable worker; outside selection:404). Source copies instruction/text/permissions/schedules/triggers, but never rule VALUES, only their existence. Protected-kit source text:403, never retry. NOT idempotent: never retry success; on timeout inspect my_kits_list. Private (recommended first) installs only within this account; kit_install its slug to create a worker. Public is default and requires fail-closed scanning;503 kit_scan_unavailable:retry later with SAME body or publish private. 400 names first rule failure; kit_validate shows all. First-publish publisherName conflict:409. Limits:20 publishes/hour/account and100 public kits; unlisted/admin-removed keep slots, private unlimited. After public success, semantic scanning can still flag the listing minutes later: my_kits_list/kit_scan_get." + AUTHORING_FLOW_NOTE,
1411
+ description: publicKitPublishingPause +
1412
+ "Publish a NEW kit. {{scope}} Exactly one of authored content or sourceWorkerId (reachable worker; outside selection:404). Source copies instruction/text/permissions/schedules/triggers, but never rule VALUES, only their existence. Protected-kit source text:403, never retry. NOT idempotent: never retry success; on timeout inspect my_kits_list. Private (recommended first) installs only within this account; kit_install its slug to create a worker. Public is default and requires fail-closed scanning;503 kit_scan_unavailable:retry later with SAME body or publish private. 400 names first rule failure; kit_validate shows all. First-publish publisherName conflict:409. Limits:20 publishes/hour/account and100 public kits; unlisted/admin-removed keep slots, private unlimited. After public success, semantic scanning can still flag the listing minutes later: my_kits_list/kit_scan_get." + AUTHORING_FLOW_NOTE,
1299
1413
  auth: "manager",
1300
1414
  method: "post",
1301
1415
  schema: {
@@ -1346,7 +1460,8 @@ const updateKit = defineTool({
1346
1460
  const replaceKit = defineTool({
1347
1461
  name: "kit_replace",
1348
1462
  title: "Replace Kit",
1349
- description: "Replace complete authored content using kit_publish's body; content required, sourceWorkerId forbidden. {{scope}} Also replaces schedules/triggers/usage windows/trigger modes. visibility promotes private to public (scan and public-kit-cap check) or demotes; existing public-lane kits keep their slot. Omitted/null timeframe bounds, blockedSenderCategories and mcpServers carry over. Kit contact allow-all is fixed, not editable. Already-public slugs stay fixed; born-private gets its clean slug on first public release. Admin-removed/taken-down listing:400. First kit_validate this exact body with kitRef." + NEW_SCOPE_NOTE,
1463
+ description: publicKitPublishingPause +
1464
+ "Replace complete authored content using kit_publish's body; content required, sourceWorkerId forbidden. {{scope}} Also replaces schedules/triggers/usage windows/trigger modes. visibility promotes private to public (scan and public-kit-cap check) or demotes; existing public-lane kits keep their slot. Omitted/null timeframe bounds, blockedSenderCategories and mcpServers carry over. Kit contact allow-all is fixed, not editable. Already-public slugs stay fixed; born-private gets its clean slug on first public release. Admin-removed/taken-down listing:400. First kit_validate this exact body with kitRef." + NEW_SCOPE_NOTE,
1350
1465
  auth: "manager",
1351
1466
  method: "put",
1352
1467
  schema: {
@@ -1384,7 +1499,8 @@ const unpublishKit = defineTool({
1384
1499
  const relistKit = defineTool({
1385
1500
  name: "kit_relist",
1386
1501
  title: "Relist Kit",
1387
- description: "Restore an UNLISTED kit to the public directory. Re-runs the fail-closed supply-chain scan (503 kit_scan_unavailable = retry later; nothing changed). Only for an unlisted kit — a PRIVATE kit goes public through kit_replace with visibility 'public'. {{scope}}" +
1502
+ description: publicKitPublishingPause +
1503
+ "Restore an UNLISTED kit to the public directory. Re-runs the fail-closed supply-chain scan (503 kit_scan_unavailable = retry later; nothing changed). Only for an unlisted kit — a PRIVATE kit goes public through kit_replace with visibility 'public'. {{scope}}" +
1388
1504
  NEW_SCOPE_NOTE,
1389
1505
  auth: "manager",
1390
1506
  method: "post",
@@ -1632,6 +1748,7 @@ const deleteMcpServer = defineTool({
1632
1748
  });
1633
1749
  // ─── Export ─────────────────────────────────────────────────────────────────
1634
1750
  export const manageDescriptors = [
1751
+ ...apiAppDescriptors,
1635
1752
  ...qualityDescriptors,
1636
1753
  ...appDiscoveryDescriptors,
1637
1754
  ...onboardingDescriptors,
@@ -1648,6 +1765,9 @@ export const manageDescriptors = [
1648
1765
  fleetPulse,
1649
1766
  fleetHealth,
1650
1767
  accountUsage,
1768
+ accountSpend,
1769
+ activityFeed,
1770
+ activitySummary,
1651
1771
  listRuns,
1652
1772
  getRun,
1653
1773
  getRunEvents,
@@ -1689,6 +1809,9 @@ export const manageDescriptors = [
1689
1809
  getFleetBudget,
1690
1810
  setFleetBudget,
1691
1811
  getWorkerPermissions,
1812
+ setWorkerPermissions,
1813
+ getWorkerBindings,
1814
+ setWorkerBindings,
1692
1815
  listModels,
1693
1816
  listDeployments,
1694
1817
  getDeployment,