@noimosai/platform-cli 0.1.3 → 0.1.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.
Files changed (93) hide show
  1. package/README.md +12 -56
  2. package/bin/noimos-platform.mjs +3 -4
  3. package/dist/cli/cli.js +477 -87
  4. package/dist/cli/command-help.js +349 -23
  5. package/dist/cli/download.js +25 -25
  6. package/dist/cli/input-error.js +48 -0
  7. package/dist/cli/local-agents.js +199 -0
  8. package/dist/cli/mcp-supervisor.js +136 -0
  9. package/dist/cli/noimos-cli.js +72 -0
  10. package/dist/cli/project-archive.js +27 -0
  11. package/dist/cli/project-files.js +25 -0
  12. package/dist/cli/project-sync.js +259 -0
  13. package/dist/cli/setup-lock.js +1 -0
  14. package/dist/cli/setup.js +347 -0
  15. package/dist/cli/skills.js +142 -0
  16. package/dist/cli/team-harness-targets.js +337 -0
  17. package/dist/cli/team-harness.js +1562 -0
  18. package/dist/cli/team-resources.js +222 -0
  19. package/dist/cli/team-skills.js +457 -0
  20. package/dist/cli/update-bootstrap.js +437 -0
  21. package/dist/cli/update-release.js +212 -0
  22. package/dist/cli/version.js +1 -0
  23. package/dist/cli/wallet.js +85 -0
  24. package/dist/client/agent-run.js +1425 -0
  25. package/dist/client/browser.js +905 -0
  26. package/dist/client/distribution.js +2 -0
  27. package/dist/client/evm-wallet.js +214 -0
  28. package/dist/client/execution-context.js +14 -0
  29. package/dist/client/failure-recovery.js +128 -0
  30. package/dist/client/index.js +4 -225
  31. package/dist/client/local-input.js +33 -12
  32. package/dist/client/local-profile-lock.js +52 -0
  33. package/dist/client/local-profile.js +181 -0
  34. package/dist/client/local-projects.js +131 -0
  35. package/dist/client/local-setup.js +93 -0
  36. package/dist/client/realtime-voice.js +184 -0
  37. package/dist/client/tool-name-hints.js +48 -0
  38. package/dist/client/wallet-access.js +214 -0
  39. package/dist/mcp/agent-server.js +345 -0
  40. package/dist/mcp/context.js +86 -0
  41. package/dist/mcp/host-schema.js +76 -0
  42. package/dist/mcp/icon.js +1 -0
  43. package/dist/mcp/index.js +3 -0
  44. package/dist/mcp/instructions.js +26 -0
  45. package/dist/mcp/local-projects.js +42 -0
  46. package/dist/mcp/local-upload.js +39 -0
  47. package/dist/mcp/noimos-runtime.js +18 -0
  48. package/dist/mcp/noimos-server.js +38 -0
  49. package/dist/mcp/operations.js +40 -0
  50. package/dist/mcp/parallel-result.js +17 -0
  51. package/dist/mcp/project-sync.js +105 -0
  52. package/dist/mcp/results-reply.js +15 -0
  53. package/dist/mcp/runtime.js +81 -0
  54. package/dist/mcp/server.js +410 -0
  55. package/dist/mcp/team-skill.js +63 -0
  56. package/dist/mcp/update-notice.js +75 -0
  57. package/dist/mcp/version.js +1 -0
  58. package/dist/schema/accountRequestMethods.js +1 -0
  59. package/dist/schema/boundedBody.js +30 -0
  60. package/dist/schema/cronValidator.js +150 -0
  61. package/dist/schema/platformApiKey.js +14 -0
  62. package/dist/schema/platformAppAuth.js +11 -0
  63. package/dist/schema/platformBillingConstants.js +4 -0
  64. package/dist/schema/platformCapabilityFamilies.js +293 -0
  65. package/dist/schema/platformConnectionCatalog.js +39 -0
  66. package/dist/schema/platformConnectionPolicy.js +33 -0
  67. package/dist/schema/platformCustomTools.js +148 -0
  68. package/dist/schema/platformEndUserApi.js +62 -0
  69. package/dist/schema/platformFunding.js +10 -0
  70. package/dist/schema/platformHttpConstraints.js +12 -0
  71. package/dist/schema/platformLocalSetup.js +25 -0
  72. package/dist/schema/platformMcpTools.js +7 -0
  73. package/dist/schema/platformMetaTools.js +63 -0
  74. package/dist/schema/platformMonitorSchedule.js +123 -0
  75. package/dist/schema/platformMonitors.js +159 -0
  76. package/dist/schema/platformOAuth.js +13 -0
  77. package/dist/schema/platformProjectPermissions.js +33 -0
  78. package/dist/schema/platformPublicExecution.js +202 -21
  79. package/dist/schema/platformRoutes.js +122 -0
  80. package/dist/schema/platformSkills.js +11 -0
  81. package/dist/schema/platformTeamSecretName.js +3 -0
  82. package/dist/schema/platformTeamSkillSlug.js +1 -0
  83. package/dist/schema/platformTeamSkills.js +302 -0
  84. package/dist/schema/platformToolAccess.js +62 -0
  85. package/dist/schema/platformTriggers.js +153 -0
  86. package/dist/schema/platformWorkflows.js +100 -0
  87. package/dist/schema/scheduleTimeZone.js +15 -0
  88. package/dist/schema/toolDiscovery.js +71 -0
  89. package/dist/schema/toolInputRepair.js +122 -0
  90. package/dist/schema/toolResultContext.js +51 -0
  91. package/dist/schema/untrustedContent.js +35 -0
  92. package/npm-shrinkwrap.json +73 -0
  93. package/package.json +6 -3
@@ -0,0 +1,905 @@
1
+ import { z } from "zod";
2
+ import { ToolNameHints } from "./tool-name-hints.js";
3
+ import { platformFailureGuidance, unexecutedCallBilling } from "./failure-recovery.js";
4
+ export { platformFailureGuidance, platformExecutionGuidance } from "./failure-recovery.js";
5
+ import { realtimeVoiceConfigSchema, realtimeVoiceGrantSchema, realtimeVoiceModelsSchema, connectRealtimeVoice } from "./realtime-voice.js";
6
+ export { connectRealtimeVoice, RealtimeVoiceSession, RealtimeVoiceError } from "./realtime-voice.js";
7
+ import { PLATFORM_CONNECTION_CATALOG_LIMITS, platformConnectionCatalogSchema, platformConnectionCatalogQuerySchema } from "../schema/platformConnectionCatalog.js";
8
+ import { PLATFORM_END_USER_ROUTES, platformEndUserContracts, platformEndUserUsageContract } from "../schema/platformEndUserApi.js";
9
+ export { PLATFORM_CONNECTION_CATALOG_LIMITS, platformConnectionProviderSchema, platformConnectionCatalogQuerySchema } from "../schema/platformConnectionCatalog.js";
10
+ import { TOOL_RESULT_CONTEXT_MAX_BYTES, toolResultSelectionSchema } from "../schema/toolResultContext.js";
11
+ import { executionReceiptContext } from "./execution-context.js";
12
+ export { executionReceiptContext, executionPageContext } from "./execution-context.js";
13
+ export { UNTRUSTED_CONTENT_RULE, wrapUntrusted } from "../schema/untrustedContent.js";
14
+ export { fitToolResultPage, TOOL_RESULT_CONTEXT_MAX_BYTES, TOOL_RESULT_PAGE_MAX_ITEMS, toolResultSelectionSchema } from "../schema/toolResultContext.js";
15
+ export { withEvidentValues } from "../schema/toolInputRepair.js";
16
+ export { platformJsonObjectSchema, PLATFORM_TOOL_REQUEST_MAX_BYTES } from "../schema/platformPublicExecution.js";
17
+ import { platformAppConnectQuerySchema, platformAppConnectSchema, toolDiscoveryInputSchema, toolDiscoveryResultSchema, TOOL_DISCOVERY_REQUEST_TIMEOUT_MS } from "../schema/toolDiscovery.js";
18
+ export { platformAppConnectQuerySchema, toolDiscoveryInputSchema } from "../schema/toolDiscovery.js";
19
+ export { PLATFORM_MCP_DIRECT_TOOLS_MAX, platformMcpDirectToolNames } from "../schema/platformMetaTools.js";
20
+ import { isRelvoProduct, PLATFORM_API_KEY_PATTERN, PLATFORM_CLIENT_HEADERS, PLATFORM_KEY_PREFIXES, PLATFORM_PRODUCT_NAMES, platformKeyProduct } from "../schema/platformApiKey.js";
21
+ export { platformKeyProduct } from "../schema/platformApiKey.js";
22
+ import { PLATFORM_DISTRIBUTION } from "./distribution.js";
23
+ import { readBoundedBody } from "../schema/boundedBody.js";
24
+ import { platformFundingShortfallSchema } from "../schema/platformFunding.js";
25
+ import { PLATFORM_META_ROUTES, PLATFORM_ROUTES, platformToolsPath as route } from "../schema/platformRoutes.js";
26
+ import { platformSkillsBundleSchema, platformSkillSchema } from "../schema/platformSkills.js";
27
+ export { platformSkillsBundleSchema, platformSkillSchema } from "../schema/platformSkills.js";
28
+ import { platformMonitorCreateSchema, platformMonitorSchema, platformMonitorEventsSchema, platformMonitorEventsQuerySchema, platformPopularMonitorSchema, platformMonitorableToolSchema, platformMonitorQuoteSchema, platformMonitorQuoteResultSchema, platformMonitorResumeSchema, platformMonitorDestinationInputSchema, PLATFORM_MONITOR_ID } from "../schema/platformMonitors.js";
29
+ export { platformMonitorCreateSchema, platformMonitorEventsQuerySchema, platformMonitorQuoteSchema, platformMonitorResumeSchema, platformMonitorDestinationInputSchema, PLATFORM_MONITOR_ID } from "../schema/platformMonitors.js";
30
+ import { platformCustomToolCreateSchema, platformCustomToolNameSchema, platformCustomToolRestoreSchema, platformCustomToolSchema, platformCustomToolSummarySchema, platformCustomToolUpdateSchema, platformCustomToolVersionSchema, PLATFORM_CUSTOM_TOOL_PREFIX } from "../schema/platformCustomTools.js";
31
+ export { platformCustomEndpointDefinitionSchema, platformCustomToolCreateSchema, platformCustomToolDefinitionSchema, platformCustomToolNameSchema, platformCustomToolSlugSchema, platformCustomToolRestoreSchema, platformCustomToolUpdateSchema, PLATFORM_CUSTOM_TOOL_PREFIX } from "../schema/platformCustomTools.js";
32
+ import { platformSuggestionsSchema, platformTeamSkillCreateSchema, platformTeamSkillListKindSchema, platformTeamSkillRestoreSchema, platformTeamSkillSchema, platformTeamSkillSummarySchema, platformTeamSkillUpdateSchema, platformTeamSkillVersionSchema, PLATFORM_TEAM_SKILL_SLUG, PLATFORM_TEAM_SKILL_LIMITS } from "../schema/platformTeamSkills.js";
33
+ export { platformTeamSkillCreateSchema, platformTeamSkillDefinitionSchema, platformTeamSkillRestoreSchema, platformTeamSkillUpdateSchema, PLATFORM_TEAM_SKILL_SLUG, PLATFORM_TEAM_SKILL_LIMITS, platformSkillPathSchema, renderSkillMarkdown, skillFileBytes, teamSkillKindNeedsAdmin } from "../schema/platformTeamSkills.js";
34
+ import { platformWorkflowCreateSchema, platformWorkflowRunCreateSchema, platformWorkflowRunIdSchema, platformWorkflowRunListQuerySchema, platformWorkflowRunListSchema, platformWorkflowRunSchema, platformWorkflowSchema, platformWorkflowSummarySchema, platformWorkflowUpdateSchema, platformWorkflowVersionSchema, PLATFORM_WORKFLOW_SLUG } from "../schema/platformWorkflows.js";
35
+ export { platformWorkflowCreateSchema, platformWorkflowDefinitionSchema, platformWorkflowRunCreateSchema, platformWorkflowRunIdSchema, platformWorkflowRunListQuerySchema, platformWorkflowSlugSchema, platformWorkflowStepSchema, platformWorkflowUpdateSchema, PLATFORM_WORKFLOW_SLUG } from "../schema/platformWorkflows.js";
36
+ import { platformTriggerCreateSchema, platformTriggerDestinationInputSchema, platformTriggerEventsQuerySchema, platformTriggerEventsSchema, platformTriggerIdSchema, platformTriggerSchema, platformTriggerTypesQuerySchema, platformTriggerTypesSchema, platformTriggerUpdateSchema } from "../schema/platformTriggers.js";
37
+ export { platformTriggerCreateSchema, platformTriggerDestinationInputSchema, platformTriggerEventsQuerySchema, platformTriggerIdSchema, platformTriggerIntervalSchema, platformTriggerTypesQuerySchema, platformTriggerUpdateSchema } from "../schema/platformTriggers.js";
38
+ import { platformMemberAccessUpdateSchema, platformTeamAccessSchema } from "../schema/platformToolAccess.js";
39
+ import { platformTeamContextSchema, platformTeamQuerySchema } from "../schema/platformToolAccess.js";
40
+ export { platformTeamContextSchema, platformTeamQuerySchema } from "../schema/platformToolAccess.js";
41
+ import { platformProjectStatusSchema, platformProjectStatusQuerySchema } from "../schema/platformProjectPermissions.js";
42
+ export { platformProjectStatusSchema, platformProjectStatusQuerySchema } from "../schema/platformProjectPermissions.js";
43
+ export { platformMemberAccessUpdateSchema } from "../schema/platformToolAccess.js";
44
+ import { platformInputFileSchema, platformInputUploadSchema, PLATFORM_UPLOAD_MAX_BYTES, PLATFORM_TOOL_REQUEST_MAX_BYTES } from "../schema/platformPublicExecution.js";
45
+ export { platformInputFileSchema, platformInputMimeSchema, platformInputFileMimeSchema, PLATFORM_INPUT_MAX_BYTES } from "../schema/platformPublicExecution.js";
46
+ import { PLATFORM_EXECUTION_MAX_WAIT_SECONDS, PLATFORM_EXECUTION_RESPONSE_MAX_BYTES, platformCatalogSchema, platformCatalogPageQuerySchema, platformCatalogPageSchema, platformQuoteSchema, platformExecutionSchema, platformExecutionContextSchema, platformExecutionResultPageSchema, platformExecutionIdSchema, platformCreateExecutionSchema, platformRecoverExecutionSchema, platformQuoteRequestSchema, platformInvalidArgumentFieldsSchema, platformToolNameSchema, platformArtifactIdSchema, platformArtifactDownloadSchema, platformExecutionListQuerySchema, platformExecutionListSchema } from "../schema/platformPublicExecution.js";
47
+ import { PLATFORM_EXECUTION_CONTEXT_BUDGET_ERROR, PlatformExecutionContextBudgetError, platformExecutionResultPageContext, retryAfterSeconds } from "../schema/platformPublicExecution.js";
48
+ import { PLATFORM_META_LIMITS, platformMetaSearchSchema, platformMetaSearchResultSchema, platformMetaSchemasSchema, platformMetaSchemasResultSchema, platformMetaConnectionsSchema, platformMetaConnectionsResultSchema, platformMetaExecuteSchema, platformMetaExecuteResultSchema, platformMetaWorkbenchSchema, platformMetaBashSchema, platformMetaRemoteResultSchema } from "../schema/platformMetaTools.js";
49
+ export { PLATFORM_META_LIMITS, platformMetaSearchSchema, platformMetaSchemasSchema, platformMetaConnectionsSchema, platformMetaExecuteSchema, platformMetaWorkbenchSchema, platformMetaBashSchema } from "../schema/platformMetaTools.js";
50
+ export { platformCreateExecutionSchema, platformToolInputSchema, platformQuoteRequestSchema, platformExecutionSchema, platformExecutionIdSchema, platformExecutionContextSchema, platformExecutionResultPageSchema, platformArtifactIdSchema, platformArtifactSchema, platformExecutionListQuerySchema } from "../schema/platformPublicExecution.js";
51
+ export const projectResultSchema = z.object({ data: z.object({ developerOrganizationId: z.string(), developerProjectId: z.string(), name: z.string() }), requestId: z.string() });
52
+ export const usageResultSchema = z.object({ data: z.object({ month: z.string(), requests: z.number().int().nonnegative(), amountCents: z.number().nonnegative(), currency: z.literal("usd") }), requestId: z.string() });
53
+ export const executionResultSchema = z.object({ data: platformExecutionSchema });
54
+ const executionContextResultSchema = z.object({ data: platformExecutionContextSchema });
55
+ const catalogResultSchema = z.object({ data: platformCatalogSchema });
56
+ export const platformToolDiscoverySchema = z.object({
57
+ query: z.string().trim().min(1).max(200).optional().describe("Substring of a tool's name, title, description, provider names or model IDs, ignoring case, whitespace, hyphens and underscores."),
58
+ category: platformCatalogSchema.shape.tools.element.shape.category.optional(),
59
+ provider: z.string().regex(/^[a-z][a-z0-9_]{1,63}$/).optional().describe("Filter discovery by a provider identifier from catalog metadata, such as anyapi or tikhub. Does not pin execution routing."),
60
+ availableOnly: z.boolean().optional().describe("Only tools with a configured provider; check executionEnabled separately."),
61
+ summary: z.boolean().optional().describe("Omit input schemas and examples; inspect a selected tool for those details."),
62
+ }).strict();
63
+ const quoteResultSchema = z.object({ data: platformQuoteSchema });
64
+ const customToolResult = z.object({ data: z.object({ tool: platformCustomToolSchema }) });
65
+ function customToolName(name) {
66
+ const parsed = platformCustomToolNameSchema.safeParse(name.startsWith(PLATFORM_CUSTOM_TOOL_PREFIX) ? name : `${PLATFORM_CUSTOM_TOOL_PREFIX}${name}`);
67
+ if (!parsed.success)
68
+ throw new Error("Provide a custom tool name such as custom__weekly_report.");
69
+ return parsed.data;
70
+ }
71
+ const teamSkillResult = z.object({ data: z.object({ skill: platformTeamSkillSchema }) });
72
+ function teamSkillSlug(slug) {
73
+ if (!PLATFORM_TEAM_SKILL_SLUG.test(slug))
74
+ throw new Error("Provide a team skill slug such as competitor_pricing.");
75
+ return slug;
76
+ }
77
+ const workflowResult = z.object({ data: z.object({ workflow: platformWorkflowSchema }) });
78
+ const workflowRunResult = z.object({ data: z.object({ run: platformWorkflowRunSchema }) });
79
+ function workflowSlug(slug) {
80
+ if (!PLATFORM_WORKFLOW_SLUG.test(slug))
81
+ throw new Error("Provide a workflow slug such as company_people.");
82
+ return slug;
83
+ }
84
+ function workflowRunId(id) {
85
+ if (!platformWorkflowRunIdSchema.safeParse(id).success)
86
+ throw new Error("Provide a workflow run ID such as wfr_….");
87
+ return id;
88
+ }
89
+ const triggerResult = z.object({ data: z.object({ trigger: platformTriggerSchema }) });
90
+ function triggerId(id) {
91
+ if (!platformTriggerIdSchema.safeParse(id).success)
92
+ throw new Error("Provide a trigger ID such as trg_….");
93
+ return id;
94
+ }
95
+ export const platformIdempotencyKeySchema = z.string().regex(/^[A-Za-z0-9._:-]{8,128}$/);
96
+ function normalizeToolSearch(value) { return value.toLowerCase().replace(/[\s_-]+/g, ""); }
97
+ const publicErrors = {
98
+ platform_team_administrator_project_access_required: "Team administrators automatically administer every project. Change their team role before removing this project access.",
99
+ platform_project_tools_version_conflict: "The project's allowed tools changed. Reload the project settings before saving your changes.",
100
+ platform_quote_changed: "The priceVersion is stale or the maximum charge is below the current quote. This request was not admitted. Request a new quote for the same arguments/provider, copy its priceVersion and use an authorized maximum covering it. Keep the same idempotency key.",
101
+ platform_idempotency_key_used_for_another_request: "This idempotency key was already used for another request. Keep the original body to retrieve its result, and never repeat that task under another key; only a different task gets its own new random key, such as a UUID.",
102
+ platform_executions_disabled: "Paid executions are paused.",
103
+ platform_provider_not_configured: "This tool's provider is unavailable.",
104
+ platform_provider_own_key_required: "This call needs the project's own provider API key. Connect it on the console's Connections page.",
105
+ platform_provider_credential_unavailable: "The selected provider credential is no longer usable. Ask the project owner/admin to check it in project integrations.",
106
+ platform_provider_not_supported: "This provider does not support the selected tool and arguments. Choose Auto or a compatible provider from the catalog.",
107
+ platform_no_compatible_provider: "No available provider supports these inputs together; remove or change a filter.",
108
+ platform_insufficient_balance_or_spending_limit_reached: "The prepaid balance or monthly spending limit is insufficient.",
109
+ platform_upload_requires_purchase: "Uploading files needs a purchased balance or your own provider key; the free signup credit does not cover it. Add funds in the console, then retry.",
110
+ platform_request_rate_exceeded: "The API key's request limit was reached. Wait before retrying.",
111
+ platform_api_key_suspended: "This API key is suspended for ignoring request limits. Wait for the suspension to end.",
112
+ platform_organization_suspended: "This organization's API access is suspended for ignoring request limits. Wait for the suspension to end.",
113
+ platform_authentication_unavailable: "Account verification is temporarily unavailable. Retry the same request shortly; the key does not need to change.",
114
+ platform_api_key_scope_required: "The API key is missing the required scope.",
115
+ platform_invalid_developer_api_key: "This API key is no longer valid: it expired, was revoked or rotated, or its member lost access. Run setup again to sign in, or create a new key in the console.",
116
+ platform_developer_api_key_required: "This is not a valid API key for this service. Check the key, or run setup again to sign in.",
117
+ platform_bearer_credential_required: "The request carries no API key. Send it as Authorization: Bearer <key>.",
118
+ platform_organization_permission_required: "Your role in this team does not allow this. Ask a team owner or admin.",
119
+ platform_tool_access_exceeds_own: "You cannot grant more access than you have yourself. Ask a team owner or admin.",
120
+ platform_connection_limit_reached: "This project holds as many connections as it can. Disconnect one in the console first.",
121
+ platform_connection_metadata_too_large: "A connection's details are too large to list here. See it on the console's Connections page.",
122
+ platform_connection_policy_invalid: "This connection's access rules cannot be read, so the call was refused. A team admin can save them again in the console.",
123
+ platform_app_method_unavailable: "That way of connecting this app is not available. Use a method the app lists.",
124
+ platform_artifact_unavailable: "This output file is unavailable or its retention period has ended.",
125
+ platform_artifact_download_limit_reached: "This output file reached its limit of download URLs for the past 24 hours. Reuse a URL issued in the last five minutes, or try again later.",
126
+ platform_meta_tools_unavailable: "Session meta tools are unavailable on this API deployment.",
127
+ platform_tool_session_required: "Start a tool session with searchMetaTools and retain its session ID.",
128
+ platform_tool_session_unavailable: "This session is expired or unavailable to this project key. Preserve any uncertain purchases for reconciliation.",
129
+ platform_tool_session_limit_reached: "This key has reached its active tool session limit. Reuse an existing session or wait for expiry.",
130
+ platform_tool_session_busy: "This sandbox session has an active execution. Wait on that existing execution; needs_review requires reconciliation.",
131
+ platform_tool_session_state_lost: "This sandbox session has no reusable checkpoint. Preserve uncertain purchases and do not claim earlier state survives.",
132
+ [PLATFORM_EXECUTION_CONTEXT_BUDGET_ERROR]: "Execution metadata exceeds the context budget. Retain the execution ID to recover the raw result or select a narrower path.",
133
+ platform_tool_result_selection_invalid: "This result has no such path or fields. Read it with a shorter path, or without path and fields, to see its shape.",
134
+ platform_invalid_tool_request: "The request does not match the quote/execution contract. Check tool, arguments, provider, sessionId, priceVersion and maximumChargeMicros.",
135
+ platform_invalid_tool_arguments: "The arguments do not match this tool's input schema. Inspect the tool's schema and quote again.",
136
+ platform_tool_unavailable: "This tool is not in this project's catalog. Search the catalog again for an available tool.",
137
+ platform_idempotency_key_required: "Paid submissions need an idempotency key of 8-128 letters, digits, '.', '_', ':' or '-'. Retain it for retries.",
138
+ platform_invalid_media_input: "A media input is invalid for this tool. Check its type, size and duration against the tool's schema.",
139
+ platform_media_type_mismatch: "The file content does not match its declared media type.",
140
+ platform_image_reference_required: "This tool needs a reference image as documented in its schema.",
141
+ platform_audio_duration_exceeds_quote: "The audio is longer than the quoted duration. Quote again with the actual duration.",
142
+ platform_input_file_unavailable: "The uploaded input file is unavailable or expired. Upload it again and use the new file ID.",
143
+ platform_upload_quota_exceeded: "This project's daily input upload allowance is used up. Wait before uploading more files.",
144
+ platform_media_source_unavailable: "A referenced media source could not be read. Use a public URL, a current project file or a project artifact.",
145
+ platform_model_tariff_unavailable: "This model has no current price and cannot be quoted. Choose another model.",
146
+ platform_connection_not_configured: "The server's account-connection service is not configured. An operator must configure it before this operation can run.",
147
+ platform_connection_reauthorize: "The connected account must be authorized again in the console before this tool can use it.",
148
+ platform_connection_scope_required: "The connected account lacks a permission this operation needs. Reconnect it with that permission in the console; if the page does not list it, reconnect with your own OAuth app, which can request it.",
149
+ platform_connection_auth_method_required: "This operation requires a different connection method. Choose a compatible connection in the console.",
150
+ platform_connection_policy_denied: "This connection's policy does not allow this operation or data range. A connection owner or project admin can change the policy in the console; retrying unchanged fails the same way.",
151
+ platform_approval_required: "This operation needs a team admin's approval on the console's Connections page. After approval, retry the same request.",
152
+ platform_connection_access_denied: "This key or member may not use this connection, or this operation on it. Use a connection from /tools/connections with a tool the tool list shows, or ask a team admin to grant it.",
153
+ platform_tool_access_denied: "This key or member may not use this tool. Add it to the key's tools in the console, ask a team admin to grant it, or use another key.",
154
+ platform_connection_authority_removed: "This key or member no longer has permission to use this connection.",
155
+ platform_connection_required: "No connection this key can use fits this tool. Connect an account in the console, or ask for access to a shared one.",
156
+ platform_connection_choice_required: "More than one connection fits this tool. Pass the connectionId of one from /tools/connections.",
157
+ platform_connection_provider_mismatch: "The selected connection is for a different service than this tool. Choose a connection of the tool's service.",
158
+ platform_connection_resource_mismatch: "The selected resource does not belong to this connected account. Use an ID listed for this connection.",
159
+ platform_connection_priced_operation_required: "X requests run only through X's separately priced tools. Use those instead of the generic request.",
160
+ platform_connection_provider_unavailable: "The connected account's service is temporarily unavailable. Retry later.",
161
+ platform_invalid_json: "The request body is not valid JSON.",
162
+ platform_body_too_large: "The request body is too large.",
163
+ platform_custom_tool_not_found: "No custom tool of that name exists in this project, or this key's member may not use it. List custom tools first.",
164
+ platform_custom_tool_exists: "This project already has a custom tool with that slug. Read it and update it instead, or choose another slug.",
165
+ platform_custom_tool_invalid: "The custom tool definition is invalid.",
166
+ platform_custom_tool_schema_invalid: "The input schema cannot be used: it must be a JSON Schema object with properties, must compile, and cannot define sourceExecutionIds.",
167
+ platform_custom_tool_version_conflict: "The custom tool changed since it was read. Read it again and apply the edit to its current version.",
168
+ platform_custom_tool_not_editable: "Only the tool's creator or an organization admin may change this custom tool.",
169
+ platform_custom_tool_endpoints_unavailable: "API endpoints are not available in this deployment.",
170
+ platform_custom_tool_kind_immutable: "A custom tool keeps its kind: a Python tool cannot become an API endpoint or the reverse. Create a new tool instead.",
171
+ platform_custom_tool_endpoint_secrets_required: "Configure the endpoint's secret bindings again in the console: its destination changed or a saved binding is missing. Saved secrets never travel to a new host.",
172
+ platform_custom_tool_restore_secret_missing: "That version needs a missing secret binding. Configure it in the console before restoring.",
173
+ platform_custom_tool_secret_ref_forbidden: "Only a team admin or the member who saved that secret may bind it to an endpoint. Ask them to configure it in the console.",
174
+ platform_custom_tool_secret_ref_console_only: "Bind the saved team secret on the console's customization secrets page, then retry. Never request credentials in chat or a tool input.",
175
+ platform_custom_tool_limit_reached: "This project has reached its custom tool limit. Delete an unused tool first.",
176
+ platform_custom_tool_version_not_found: "That version of the custom tool is not kept. List its versions first.",
177
+ platform_team_skill_not_found: "No team skill with that slug exists in this project. List team skills first.",
178
+ platform_team_skill_exists: "This project already has a team skill with that slug. Read it and update it instead, or choose another slug.",
179
+ platform_team_instructions_exists: "This project already has AGENTS.md. Read the existing instructions and update them instead of creating another document.",
180
+ platform_team_item_name_exists: "This environment name is already used by a team item. Choose another name; keep the stable slug when editing an existing item.",
181
+ platform_team_skill_invalid: "The team skill is invalid.",
182
+ platform_team_skill_version_conflict: "The team skill changed since it was read. Read it again and apply the edit to its current version.",
183
+ platform_team_skill_not_deletable: "Only the skill's creator or an organization admin may delete this team skill. Update it instead.",
184
+ platform_team_skill_read_only: "This member may read the team's skills but not create or change them. A team admin can allow it.",
185
+ platform_team_skill_not_editable: "This member may change only the team skills they wrote. Create a new skill instead, or ask its creator or a team admin.",
186
+ platform_team_skill_limit_reached: "This project has reached its team skill limit. Merge or delete skills first.",
187
+ platform_team_skill_version_not_found: "That version of the team skill is not kept. List its versions first.",
188
+ platform_team_item_admin_only: "This acts on every teammate's machine that installs it (a hook, MCP server or permission, or a skill or agent whose frontmatter holds hooks, allowed-tools, mcpServers or permissionMode): an organization owner or admin changes it in the Relvo console, never with an API key.",
189
+ platform_workflow_not_found: "No workflow with that slug exists in this project. List workflows first.",
190
+ platform_workflow_step_refused: "A connected app refused a step, so the run stopped. Read that step's execution for the app's answer.",
191
+ platform_workflow_exists: "This project already has a workflow with that slug. Read it and update it instead, or choose another slug.",
192
+ platform_workflow_invalid: "The workflow definition is invalid: every tool must be in the project's catalog, and a reference names a declared input or an earlier step.",
193
+ platform_workflow_version_conflict: "The workflow changed since it was read. Read it again and apply the edit to its current version.",
194
+ platform_workflow_not_editable: "Only the workflow's creator or an organization admin may change this workflow.",
195
+ platform_workflow_limit_reached: "This project has reached its workflow limit. Delete an unused workflow first.",
196
+ platform_workflow_version_not_found: "That version of the workflow is not kept. List its versions first.",
197
+ platform_workflow_inputs_invalid: "The run inputs do not match the workflow's input schema.",
198
+ platform_workflow_run_limit_reached: "This project already has the most workflow runs in progress. Wait for one to finish or cancel one.",
199
+ platform_workflow_run_not_found: "No workflow run with that ID exists in this project.",
200
+ platform_workflow_run_owner_required: "Only the member who started the run or an admin may cancel it.",
201
+ platform_workflow_budget_exceeded: "The run stopped before a step whose price no longer fit what was left of maximumChargeMicros. Steps before it were charged; run again with a larger budget.",
202
+ platform_workflow_reference_missing: "The run stopped before a step whose $input or $step reference had no value. Check the earlier step's output and the reference path.",
203
+ platform_workflow_run_cancelled: "The run was cancelled; no later step was bought.",
204
+ platform_workflow_unavailable: "The run could not continue after repeated failures. Start it again.",
205
+ platform_triggers_unavailable: "Triggers are not available in this deployment.",
206
+ platform_trigger_invalid: "The trigger request is invalid.",
207
+ platform_trigger_not_found: "No trigger with that ID exists in this project. List triggers first.",
208
+ platform_trigger_not_editable: "Only the trigger's creator or an organization admin may change this trigger.",
209
+ platform_trigger_limit_reached: "This project has reached its trigger limit. Delete an unused trigger first.",
210
+ platform_trigger_key_invalid: "The API key the trigger runs as is no longer valid. Create the trigger again with a key that may execute tools.",
211
+ platform_trigger_connection_app_mismatch: "The connection belongs to another app than the tool or trigger type. Use a connection of the same app.",
212
+ platform_trigger_operation_unknown: "That tool is not a read operation of the app. List trigger types for the app and choose one of its operations.",
213
+ platform_trigger_arguments_invalid: "The poll arguments do not match the operation's input schema.",
214
+ platform_trigger_items_path_not_found: "itemsPath points at nothing in the operation's response. Fix it or omit it to use the largest list.",
215
+ platform_trigger_poll_refused: "The app refused the trigger's last read. Check the connection's permissions; its snapshot is kept.",
216
+ platform_trigger_type_unknown: "No trigger type with that name exists. List trigger types for the app first.",
217
+ platform_trigger_type_unavailable: "This trigger type cannot be received in this environment yet.",
218
+ platform_trigger_config_invalid: "The config does not match the trigger type's configSchema.",
219
+ platform_trigger_destination_limit_reached: "This trigger has the most destinations allowed. Remove one first.",
220
+ platform_trigger_destination_not_found: "No destination with that ID exists on this trigger. Read the trigger first.",
221
+ platform_trigger_poll_price_above_maximum: "A poll now costs more than maximumChargeMicrosPerPoll, so the trigger stopped. Raise it or delete the trigger.",
222
+ platform_trigger_poll_failed: "The app's read operation failed, so the trigger stopped. Check the connection and enable the trigger again.",
223
+ platform_trigger_poll_unavailable: "The app's read operation is unavailable, so the trigger stopped. Enable the trigger again later.",
224
+ platform_trigger_registering: "Relvo is still registering the webhook with the app. Read the trigger again shortly.",
225
+ platform_monitors_unavailable: "Monitors are not available in this deployment.",
226
+ platform_monitor_not_found: "No monitor with that ID exists in this project. List monitors first.",
227
+ platform_monitor_unsupported: "This tool and provider cannot be monitored. List the monitorable tools and the provider to pin.",
228
+ platform_monitor_charge_not_accepted: "A run of this monitor costs more than maximumChargeMicrosPerRun. Quote it, get the user's approval of the recurring price, and pass at least its chargeCeilingMicros.",
229
+ platform_monitor_limit_reached: "This project has reached its monitor limit. Delete a monitor first.",
230
+ platform_monitor_destination_limit_reached: "This monitor has the most destinations allowed. Remove one first.",
231
+ platform_monitor_destination_not_found: "No destination with that ID exists on this monitor. Read the monitor first.",
232
+ platform_monitor_destination_url_blocked: "That webhook URL cannot be used. Use a public https URL.",
233
+ platform_monitor_email_destination_console_only: "Email destinations are added in the console, not with an API key.",
234
+ platform_app_not_found: "No app of that name or ID is in the catalog. Check its name, or search the catalog for it.",
235
+ platform_connection_not_found: "No connection with that ID exists in this project. List connections first.",
236
+ platform_access_unavailable: "This key cannot reach that resource. It may not exist in the selected project: check the ID and the project before retrying.",
237
+ platform_execution_owner_required: "Only the member who started this execution or a project admin may cancel it.",
238
+ platform_execution_output_unavailable: "This execution's stored output cannot be read right now. Read it again shortly; do not repurchase the work.",
239
+ platform_project_permission_required: "Your project role does not allow this. Ask a project administrator.",
240
+ platform_invalid_cursor: "This page cursor is not valid. Pass the returned value unchanged, or start from the first page.",
241
+ platform_invalid_media_url: "A media URL must be a public https URL.",
242
+ platform_invalid_request: "The request is invalid. Check it against the operation's schema.",
243
+ platform_invalid_artifact_id: "The file ID is invalid. Use an artifact ID from the execution's output.",
244
+ platform_invalid_connection_query: "The connection list request is invalid.",
245
+ platform_invalid_idempotency_key: "The idempotency key is invalid.",
246
+ platform_request_is_pending_or_failed_use_a_new_idempotency_key: "This idempotency key belongs to a request that is pending or failed. Use a new key.",
247
+ platform_operation_unavailable: "This operation is not available in this deployment.",
248
+ platform_endpoint_unavailable: "This endpoint is not available in this deployment.",
249
+ platform_post_required: "This endpoint takes POST.",
250
+ platform_custom_tools_unavailable: "Custom tools are not available in this deployment.",
251
+ platform_team_skills_unavailable: "Team skills are not available in this deployment.",
252
+ platform_workflows_unavailable: "Workflows are not available in this deployment.",
253
+ platform_voice_unavailable: "Realtime voice is not available in this deployment.",
254
+ platform_unknown_tool_name: "One of the named tools does not exist. Search the catalog for its exact name.",
255
+ platform_provider_action_unavailable: "This action is not available for this provider. Choose another tool.",
256
+ platform_provider_response_unknown: "The provider did not respond. Try again shortly, and connect again if this keeps happening.",
257
+ platform_app_credentials_rejected: "The app did not accept this connection's credentials. Ask the user to check them on the console's Connections page.",
258
+ platform_app_resource_credentials_rejected: "The app did not grant access to the selected resource. Ask the user to reconnect it.",
259
+ platform_app_unreachable: "The app could not be reached with this connection's details. Ask the user to check its subdomain, region or server address.",
260
+ platform_connection_changed: "The connection changed during this request. Read it again and retry.",
261
+ platform_connection_completion_rejected: "The request could not be completed because the connection or your permissions changed.",
262
+ platform_connection_review_pending: "This connection is not open yet: the provider's approval is pending.",
263
+ platform_execution_cancelled: "This execution was cancelled or its time limit was reached.",
264
+ platform_execution_deadline_exceeded: "Not enough time remained to finish this request. Try again.",
265
+ platform_execution_reference_unavailable: "This execution could not be prepared. Try again.",
266
+ platform_execution_unavailable: "This URL cannot be read. Use a publicly reachable http or https address.",
267
+ platform_artifact_storage_unavailable: "File storage is temporarily unavailable. Try again later.",
268
+ platform_input_storage_unavailable: "File uploads are unavailable right now. Try again later.",
269
+ platform_file_source_unavailable: "This file or byte range could not be read. Use a public URL that supports range requests, or upload the file.",
270
+ platform_monitor_changed: "This monitor changed before the run started, so nothing was charged.",
271
+ platform_monitor_destinations_unavailable: "Monitor destinations are not available right now. Try again later.",
272
+ platform_monitor_recipient_invalid: "Choose recipients from the members who have a verified email address.",
273
+ platform_team_skill_storage_limit: "The team's storage is full. Delete items no longer used.",
274
+ platform_team_skill_storage_unavailable: "Skill files are temporarily unavailable. Try again shortly.",
275
+ platform_browser_session_act_limit: "This browser session reached its action limit. Open a new session.",
276
+ platform_browser_session_busy: "Another action is running in this browser session. Wait for it to finish.",
277
+ platform_browser_session_unavailable: "This browser session is unavailable. Open a new session with the same project and API key.",
278
+ platform_sandbox_recipe_invalid: "The code execution settings are invalid or out of date.",
279
+ platform_sandbox_source_unavailable: "The selected source results are unavailable. Choose successful executions with results.",
280
+ platform_sandbox_sources_too_large: "The selected source results exceed the size limit. Select fewer or smaller results.",
281
+ platform_model_output_cap_unavailable: "A total output token limit is unavailable for this model. Choose another model.",
282
+ platform_native_text_budget_exceeded: "The maximum possible cost of this request exceeds maxCostUsd. Raise maxCostUsd, lower maxOutputTokens or shorten the prompt.",
283
+ platform_generation_model_retired: "This generation uses a retired model. Start a new generation with a current model.",
284
+ platform_seedance_model_deprecated: "This model is unavailable for new video generation. Choose an available model.",
285
+ platform_seedance_draft_unavailable: "The Seedance draft is unavailable. Create a new draft.",
286
+ platform_kling_faces_unavailable: "The Kling face detection result is unavailable. Run face detection again.",
287
+ platform_veo_video_unavailable: "The Veo video is unavailable or has expired. Generate a new video.",
288
+ platform_omni_source_unavailable: "The previous Omni video result is unavailable. Select another completed generation.",
289
+ platform_omni_extension_too_long: "Omni videos longer than 30 seconds cannot be extended.",
290
+ platform_omni_media_too_large: "The Omni request is too large. Use smaller media files or fewer references.",
291
+ platform_omni_reference_too_long: "Use MP4 reference videos of 3 seconds or less.",
292
+ platform_omni_source_too_long: "The Omni source video must be 15 seconds or less.",
293
+ platform_alpha_image_required: "Transparent backgrounds require a PNG reference image.",
294
+ platform_image_dimensions_unavailable: "The image dimensions could not be read. Choose another image.",
295
+ platform_image_reference_dimensions_unsupported: "The reference image does not meet the size or dimension requirements. Choose another image.",
296
+ platform_invalid_image_mask: "The image mask is invalid. Use a mask with an alpha channel and the same format and dimensions as the first input image.",
297
+ platform_reference_images_exceed_12_mib: "Reference images can total at most 12 MiB.",
298
+ platform_media_input_exceeds_quote: "The media is larger than the quote allows. Quote again with this file.",
299
+ platform_media_input_unsupported: "This media input is not supported. Use an uploaded file instead.",
300
+ platform_media_metadata_unavailable: "The media dimensions or duration could not be read. Choose another file.",
301
+ platform_media_reference_unsupported: "This media reference is not supported for this operation.",
302
+ platform_music_prompt_too_large: "The music prompt is too long. Shorten the prompt, lyrics or segment descriptions.",
303
+ platform_native_audio_deadline_exceeded: "Not enough time remained to process the audio. Try again.",
304
+ platform_native_audio_recipe_unavailable: "The audio processing settings are invalid or out of date.",
305
+ platform_native_audio_source_unsupported: "Use a WAV or MP3 audio file.",
306
+ platform_isolation_audio_format_unsupported: "Voice isolation needs an audio file in a supported format.",
307
+ platform_gemini_voice_unavailable: "The Gemini voice is unavailable or has expired. Create it again or choose another voice.",
308
+ platform_public_voice_catalog_paging_limit: "The public voice list is too large to verify the selected voice. Choose another voice.",
309
+ platform_public_voice_catalog_timeout: "Checking the public voice list timed out. Try again.",
310
+ platform_public_voice_catalog_unavailable: "The public voice list is temporarily unavailable. Try again later.",
311
+ platform_public_voice_not_found: "The selected public voice was not found. Choose another voice.",
312
+ platform_voice_audio_format_unsupported: "Use a valid WAV or MP3 audio file.",
313
+ platform_voice_base_requires_stored_voice: "Use a saved custom voice as the base voice.",
314
+ platform_voice_expired: "This voice has expired. Create it again or choose another voice.",
315
+ platform_voice_source_requires_10_to_30_seconds: "The speech sample must be 10 to 30 seconds long.",
316
+ platform_invalid_voice_configuration: "The voice session settings are invalid. Check them and try again.",
317
+ platform_invalid_voice_grant: "The voice session authorization is invalid or has expired. Request a new session.",
318
+ platform_voice_grant_already_used: "This voice session authorization has already been used. Request a new session.",
319
+ platform_voice_provider_unavailable: "The voice provider for this model is unavailable. Choose another model.",
320
+ platform_voice_session_cancelled: "The voice session was cancelled or has ended.",
321
+ };
322
+ const PLATFORM_ERROR_CODE = /^platform_[a-z0-9_]{1,100}$/;
323
+ const REFUSALS_WITH_DETAILS = new Set(["platform_invalid_tool_arguments", "platform_custom_tool_invalid", "platform_custom_tool_schema_invalid", "platform_custom_tool_version_conflict",
324
+ "platform_team_skill_invalid", "platform_team_skill_version_conflict", "platform_workflow_invalid", "platform_workflow_version_conflict", "platform_workflow_inputs_invalid",
325
+ "platform_trigger_invalid", "platform_trigger_arguments_invalid", "platform_trigger_config_invalid", "platform_monitor_charge_not_accepted"]);
326
+ const refusalDetailsSchema = z.object({ fields: platformInvalidArgumentFieldsSchema.optional(), reason: z.string().max(200).optional(), version: z.number().int().optional(),
327
+ chargeCeilingMicros: z.number().int().nonnegative().optional() });
328
+ const usd = (micros) => `$${(Number(micros) / 1_000_000).toFixed(micros.length > 4 ? 2 : 4)}`;
329
+ function shortfallText({ reason, requiredMicros, availableMicros }) {
330
+ if (reason === "balance")
331
+ return ` It needs ${usd(requiredMicros)} and the balance has ${usd(availableMicros)}. Top up the balance (relvo wallet top-up, or the console), then retry with the same idempotency key.`;
332
+ const scope = reason === "project_monthly_limit" ? "project's monthly" : reason === "member_monthly_limit" ? "member's monthly" : reason === "api_key_daily_limit" ? "API key's daily" : reason === "organization_daily_limit" ? "organization's daily" : "organization's monthly";
333
+ const reset = reason.endsWith("_daily_limit") ? " The daily count starts again at 00:00 UTC." : "";
334
+ return ` It needs ${usd(requiredMicros)} and ${usd(availableMicros)} remains of the ${scope} spending limit.${reset} Topping up does not help; a person must raise the limit in the console.`;
335
+ }
336
+ export class PlatformApiError extends Error {
337
+ status;
338
+ code;
339
+ fields;
340
+ shortfall;
341
+ provider;
342
+ scope;
343
+ reconnectUrl;
344
+ billingUrl;
345
+ actionUrl;
346
+ connectionProviders;
347
+ refusal;
348
+ guidance;
349
+ summary;
350
+ billing;
351
+ constructor(status, code, productName = PLATFORM_PRODUCT_NAMES.relvo, fields = [], shortfall, provider, scope, reconnectUrl, billingUrl, actionUrl, connectionProviders = [], noExecutionCharge = false, refusal = {}) {
352
+ const summary = code && Object.hasOwn(publicErrors, code) ? publicErrors[code] : code ? `The API refused this request: ${code}.`
353
+ : status >= 500 ? "The service failed this request. Retry shortly; the key and project do not need to change." : "Check the developer key, project access and limits in the console.";
354
+ super(`${productName} API request failed (${status}). ${summary}${refusal.retryAfterSeconds ? ` Retry after ${refusal.retryAfterSeconds} seconds; retrying sooner counts against this key.` : ""}${fields.length ? ` Invalid fields: ${fields.join(", ")}.` : ""}${refusal.reason ? ` Reason: ${refusal.reason}` : ""}${refusal.version === undefined ? "" : ` Current version: ${refusal.version}.`}${refusal.chargeCeilingMicros === undefined ? "" : ` chargeCeilingMicros: ${refusal.chargeCeilingMicros}.`}${shortfall ? shortfallText(shortfall) : ""}${provider ? ` Provider key needed: ${provider}.` : ""}${scope ? ` Missing scope: ${scope}; obtain the user's additional approval before retrying.` : ""}${reconnectUrl ? ` Show the user this page to reconnect it, then retry: ${reconnectUrl}` : ""}${billingUrl ? ` Billing: ${billingUrl}` : ""}${actionUrl ? ` Show the user this page to complete the required approval or configuration, then retry the original request: ${actionUrl}` : ""}`);
355
+ this.status = status;
356
+ this.code = code;
357
+ this.fields = fields;
358
+ this.shortfall = shortfall;
359
+ this.provider = provider;
360
+ this.scope = scope;
361
+ this.reconnectUrl = reconnectUrl;
362
+ this.billingUrl = billingUrl;
363
+ this.actionUrl = actionUrl;
364
+ this.connectionProviders = connectionProviders;
365
+ this.refusal = refusal;
366
+ this.name = "PlatformApiError";
367
+ this.summary = summary;
368
+ this.billing = noExecutionCharge ? unexecutedCallBilling() : undefined;
369
+ this.guidance = platformFailureGuidance(code, { provider, providers: connectionProviders.length ? connectionProviders : undefined, reconnectUrl, actionUrl });
370
+ }
371
+ }
372
+ function contextBudgetFailure(error, id, kind) {
373
+ if (error instanceof PlatformApiError && error.code === PLATFORM_EXECUTION_CONTEXT_BUDGET_ERROR)
374
+ throw new PlatformExecutionContextBudgetError(id, kind);
375
+ throw error;
376
+ }
377
+ async function responseJson(response, maximumBytes, productName) {
378
+ const body = await readBoundedBody(response, maximumBytes);
379
+ if (body.status === "missing")
380
+ throw new Error(`The ${productName} API returned an empty response.`);
381
+ if (body.status === "too_large")
382
+ throw new Error(`The ${productName} API response is too large.`);
383
+ try {
384
+ return JSON.parse(new TextDecoder().decode(body.bytes));
385
+ }
386
+ catch {
387
+ throw new Error(`The ${productName} API returned an invalid response.`);
388
+ }
389
+ }
390
+ export function hasRelvoEnvironment(env) {
391
+ return env.RELVO_API_URL !== undefined || env.RELVO_API_KEY !== undefined;
392
+ }
393
+ export function configFromEnvironment(env, source, distribution = PLATFORM_DISTRIBUTION) {
394
+ if (distribution.product === "noimos" && (!env.NOIMOS_PLATFORM_API_URL || !env.NOIMOS_PLATFORM_API_KEY))
395
+ throw new Error("Set NOIMOS_PLATFORM_API_URL and NOIMOS_PLATFORM_API_KEY from the NoimosAI Platform console.");
396
+ if (distribution.product === "relvo" && hasRelvoEnvironment(env)) {
397
+ const apiUrl = env.RELVO_API_URL, apiKey = env.RELVO_API_KEY;
398
+ if (!apiUrl || !apiKey)
399
+ throw new Error("Set RELVO_API_URL and RELVO_API_KEY from the Relvo console.");
400
+ return { apiUrl, apiKey, source, product: "relvo", local: env.RELVO_ENV === "local" };
401
+ }
402
+ if (!env.NOIMOS_PLATFORM_API_URL || !env.NOIMOS_PLATFORM_API_KEY) {
403
+ throw new Error(env.NOIMOS_PLATFORM_API_URL !== undefined || env.NOIMOS_PLATFORM_API_KEY !== undefined
404
+ ? "Set NOIMOS_PLATFORM_API_URL and NOIMOS_PLATFORM_API_KEY from the developer console."
405
+ : "Set RELVO_API_URL and RELVO_API_KEY from the Relvo console.");
406
+ }
407
+ return { apiUrl: env.NOIMOS_PLATFORM_API_URL, apiKey: env.NOIMOS_PLATFORM_API_KEY, source, product: "noimos", local: env.NOIMOS_PLATFORM_ENV === "local" };
408
+ }
409
+ export class PlatformClient {
410
+ config;
411
+ transport;
412
+ requestContext;
413
+ sharedProjectAccess;
414
+ toolNameHints = new ToolNameHints();
415
+ async requestJournalScope() {
416
+ const bytes = new TextEncoder().encode(JSON.stringify([this.baseUrl, this.config.apiKey]));
417
+ return Array.from(new Uint8Array(await crypto.subtle.digest("SHA-256", bytes)), value => value.toString(16).padStart(2, "0")).join("");
418
+ }
419
+ baseUrl;
420
+ productName;
421
+ holdsReads;
422
+ constructor(config, transport = (input, init) => fetch(input, init), requestContext = {}) {
423
+ this.config = config;
424
+ this.transport = transport;
425
+ this.requestContext = requestContext;
426
+ const product = config.product ?? platformKeyProduct(config.apiKey);
427
+ this.sharedProjectAccess = product === null || isRelvoProduct(product);
428
+ this.productName = PLATFORM_PRODUCT_NAMES[product === null || isRelvoProduct(product) ? "relvo" : "noimos"];
429
+ this.holdsReads = product !== null && isRelvoProduct(product);
430
+ const url = new URL(config.apiUrl);
431
+ if (url.username || url.password || url.search || url.hash || (url.protocol !== "https:" && !(config.local && url.protocol === "http:" && ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname))))
432
+ throw new Error(`The ${this.productName} API URL must use HTTPS. Explicit local mode permits loopback HTTP.`);
433
+ if (!PLATFORM_API_KEY_PATTERN.test(config.apiKey) || config.product && platformKeyProduct(config.apiKey) !== config.product)
434
+ throw new Error(`A dedicated ${PLATFORM_KEY_PREFIXES[config.product ?? "relvo"]} developer API key is required.`);
435
+ this.baseUrl = url.href.replace(/\/$/, "");
436
+ }
437
+ async request(path, schema, options) {
438
+ const body = options?.body === undefined ? undefined : JSON.stringify(options.body);
439
+ const upload = path === PLATFORM_ROUTES.upload, meta = PLATFORM_META_ROUTES.includes(path);
440
+ const maximumRequestBytes = options?.maximumRequestBytes ?? (upload ? PLATFORM_UPLOAD_MAX_BYTES : meta ? PLATFORM_META_LIMITS.requestBytes : options?.toolInput ? PLATFORM_TOOL_REQUEST_MAX_BYTES : 65536);
441
+ if (body && new TextEncoder().encode(body).byteLength > maximumRequestBytes)
442
+ throw new Error(options?.maximumRequestBytes ? "The skill package exceeds 20 MiB." : upload ? "The upload exceeds 12 MiB." : meta ? "The meta tool request exceeds 1 MiB." : options?.toolInput ? "The tool request exceeds 1 MiB." : "The tool request exceeds 64 KiB.");
443
+ let response;
444
+ const timeoutMs = options?.timeoutMs ?? 20000;
445
+ const signal = this.requestContext.signal && options?.signal ? AbortSignal.any([this.requestContext.signal, options.signal]) : options?.signal ?? this.requestContext.signal;
446
+ const checkSignal = () => {
447
+ if (this.requestContext.signal?.aborted)
448
+ throw new Error(`The ${this.productName} request was interrupted. Accepted work keeps running: retry only the same body and idempotency key, or read the same execution ID.`, { cause: this.requestContext.signal.reason });
449
+ signal?.throwIfAborted();
450
+ };
451
+ const timeoutMessage = `The ${this.productName} API did not answer within ${Math.round(timeoutMs / 1000)} s. If submitting an execution, retry with the same body and idempotency key.`;
452
+ let timeout;
453
+ checkSignal();
454
+ await this.requestContext.beforeRequest?.();
455
+ try {
456
+ checkSignal();
457
+ timeout = AbortSignal.timeout(timeoutMs);
458
+ response = await this.transport(`${this.baseUrl}${path}`, { method: options?.method ?? "GET", redirect: "error", signal: signal ? AbortSignal.any([timeout, signal]) : timeout,
459
+ headers: { authorization: `Bearer ${this.config.apiKey}`, [PLATFORM_CLIENT_HEADERS[this.config.product ?? platformKeyProduct(this.config.apiKey) ?? "relvo"]]: this.config.source, "idempotency-key": options?.idempotencyKey ?? crypto.randomUUID(),
460
+ ...(body === undefined ? {} : { "content-type": "application/json" }), ...(options?.prefer ? { prefer: options.prefer } : {}) }, ...(body === undefined ? {} : { body }) });
461
+ }
462
+ catch {
463
+ checkSignal();
464
+ throw new Error(timeout?.aborted
465
+ ? timeoutMessage
466
+ : `The ${this.productName} API could not be reached. If submitting an execution, retry with the same body and idempotency key.`);
467
+ }
468
+ const maximumBytes = options?.maximumResponseBytes ?? (meta ? PLATFORM_META_LIMITS.contextBytes : PLATFORM_EXECUTION_RESPONSE_MAX_BYTES);
469
+ const payload = await responseJson(response, maximumBytes, this.productName).catch(() => {
470
+ checkSignal();
471
+ if (!response.ok)
472
+ throw new PlatformApiError(response.status, null, this.productName);
473
+ if (timeout?.aborted)
474
+ throw new Error(timeoutMessage);
475
+ throw new Error(`The ${this.productName} API returned an invalid response. If submitting an execution, retain the same body and idempotency key.`);
476
+ });
477
+ if (!response.ok) {
478
+ const parsed = z.object({ error: z.object({ message: z.string(), details: z.unknown().optional() }) }).safeParse(payload);
479
+ const named = parsed.success ? [parsed.data.error.message, z.object({ code: z.string() }).safeParse(parsed.data.error.details).data?.code] : [];
480
+ const code = named.find(value => value !== undefined && Object.hasOwn(publicErrors, value)) ?? named.find(value => value !== undefined && PLATFORM_ERROR_CODE.test(value)) ?? null;
481
+ const details = parsed.success ? parsed.data.error.details : undefined;
482
+ const { fields = [], ...refusal } = (code && REFUSALS_WITH_DETAILS.has(code) ? refusalDetailsSchema.safeParse(details).data : undefined) ?? {};
483
+ const shortfall = code === "platform_insufficient_balance_or_spending_limit_reached" ? platformFundingShortfallSchema.safeParse(details).data : undefined;
484
+ const provider = code === "platform_provider_own_key_required" ? z.object({ provider: z.string().regex(/^[a-z0-9]{1,40}$/) }).safeParse(details).data?.provider : undefined;
485
+ const scope = code === "platform_api_key_scope_required" ? z.object({ scope: z.string().regex(/^[a-z]{1,20}:[a-z]{1,20}$/) }).safeParse(details).data?.scope : undefined;
486
+ const reconnectUrl = code === "platform_connection_reauthorize" ? z.object({ reconnectUrl: z.url({ protocol: /^https?$/ }) }).safeParse(details).data?.reconnectUrl : undefined;
487
+ const billingUrl = code === "platform_insufficient_balance_or_spending_limit_reached"
488
+ ? z.object({ billingUrl: z.url({ protocol: /^https?$/ }) }).safeParse(details).data?.billingUrl : undefined;
489
+ const handoff = code && ["platform_custom_tool_secret_ref_console_only", "platform_custom_tool_secret_ref_forbidden", "platform_custom_tool_endpoint_secrets_required", "platform_custom_tool_restore_secret_missing",
490
+ "platform_api_key_scope_required", "platform_provider_own_key_required", "platform_approval_required", "platform_connection_required", "platform_connection_scope_required", "platform_connection_auth_method_required",
491
+ "platform_connection_policy_denied", "platform_connection_authority_removed", "platform_connection_access_denied", "platform_connection_choice_required"].includes(code)
492
+ ? z.object({ settingsUrl: z.url({ protocol: /^https?$/ }).optional(), reviewUrl: z.url({ protocol: /^https?$/ }).optional() }).safeParse(details).data : undefined;
493
+ const connectionProviders = code === "platform_connection_required"
494
+ ? z.object({ providers: z.array(z.string().regex(/^(?:app:)?[a-zA-Z0-9_-]{1,100}$/)).max(10) }).safeParse(details).data?.providers : undefined;
495
+ const submission = options?.idempotencyKey ? platformCreateExecutionSchema.safeParse(options.body) : undefined;
496
+ const rejectedExecution = submission?.success && path === route(PLATFORM_ROUTES.tool, { tool: submission.data.tool })
497
+ && response.status >= 400 && response.status < 500 && response.status !== 408;
498
+ const retryAfter = response.status === 429 ? retryAfterSeconds(response.headers.get("retry-after") ?? undefined) : undefined;
499
+ throw new PlatformApiError(response.status, code, this.productName, fields, shortfall, provider, scope, reconnectUrl, billingUrl, handoff?.reviewUrl ?? handoff?.settingsUrl, connectionProviders, path.split("?")[0] === route(PLATFORM_ROUTES.quote) || Boolean(rejectedExecution), { ...refusal, ...(retryAfter ? { retryAfterSeconds: retryAfter } : {}) });
500
+ }
501
+ const parsed = schema.safeParse(payload);
502
+ if (!parsed.success)
503
+ throw new Error(`The ${this.productName} API returned an unexpected response. If submitting an execution, retain the same body and idempotency key.`);
504
+ return parsed.data;
505
+ }
506
+ project() { return this.request(route(PLATFORM_ROUTES.project), projectResultSchema); }
507
+ usage() { return this.request(route(PLATFORM_ROUTES.usage), usageResultSchema); }
508
+ createUserSession(input) {
509
+ return this.request(PLATFORM_END_USER_ROUTES.sessions, z.object({ data: platformEndUserContracts.createSession.output }), { method: "POST", body: platformEndUserContracts.createSession.input.parse(input) });
510
+ }
511
+ restoreUserSession(input) {
512
+ return this.request(PLATFORM_END_USER_ROUTES.restoreSession, z.object({ data: platformEndUserContracts.restoreSession.output }), { method: "POST", body: platformEndUserContracts.restoreSession.input.parse(input) });
513
+ }
514
+ userSession(id) {
515
+ const input = platformEndUserContracts.session.input.parse({ id });
516
+ return this.request(route(PLATFORM_END_USER_ROUTES.session, input), z.object({ data: platformEndUserContracts.session.output }));
517
+ }
518
+ revokeUserSession(id) {
519
+ const input = platformEndUserContracts.revokeSession.input.parse({ id });
520
+ return this.request(route(PLATFORM_END_USER_ROUTES.revokeSession, input), z.object({ data: platformEndUserContracts.revokeSession.output }), { method: "POST", body: {} });
521
+ }
522
+ createUserConnectLink(input) {
523
+ return this.request(PLATFORM_END_USER_ROUTES.links, z.object({ data: platformEndUserContracts.createLink.output }), { method: "POST", body: platformEndUserContracts.createLink.input.parse(input) });
524
+ }
525
+ userConnectLinkStatus(input, signal) {
526
+ return this.request(PLATFORM_END_USER_ROUTES.linkStatus, z.object({ data: platformEndUserContracts.linkStatus.output }), { method: "POST", body: platformEndUserContracts.linkStatus.input.parse(input), signal });
527
+ }
528
+ revokeUserConnectLink(input) {
529
+ return this.request(PLATFORM_END_USER_ROUTES.revokeLink, z.object({ data: platformEndUserContracts.revokeLink.output }), { method: "POST", body: platformEndUserContracts.revokeLink.input.parse(input) });
530
+ }
531
+ userConnections(input) {
532
+ const { userId, ...page } = platformEndUserContracts.connections.input.parse(input);
533
+ return this.request(`${route(PLATFORM_END_USER_ROUTES.connections, { userId: encodeURIComponent(userId) })}?${new URLSearchParams(Object.entries(page).map(([key, value]) => [key, String(value)]))}`, z.object({ data: platformEndUserContracts.connections.output }));
534
+ }
535
+ disconnectUserConnection(input) {
536
+ return this.request(PLATFORM_END_USER_ROUTES.disconnect, z.object({ data: platformEndUserContracts.disconnect.output }), { method: "POST", body: platformEndUserContracts.disconnect.input.parse(input) });
537
+ }
538
+ users(input = {}) {
539
+ const parsed = platformEndUserContracts.users.input.parse(input);
540
+ return this.request(`${PLATFORM_END_USER_ROUTES.users}?${new URLSearchParams(Object.entries(parsed).map(([key, value]) => [key, String(value)]))}`, z.object({ data: platformEndUserContracts.users.output }));
541
+ }
542
+ updateUser(input) {
543
+ return this.request(PLATFORM_END_USER_ROUTES.setUser, z.object({ data: platformEndUserContracts.setUser.output }), { method: "POST", body: platformEndUserContracts.setUser.input.parse(input) });
544
+ }
545
+ userUsage(input) {
546
+ const { userId, ...range } = platformEndUserUsageContract.input.parse(input);
547
+ return this.request(`${route(PLATFORM_END_USER_ROUTES.userUsage, { userId: encodeURIComponent(userId) })}?${new URLSearchParams(range)}`, z.object({ data: platformEndUserUsageContract.output }));
548
+ }
549
+ connections(input = {}) {
550
+ const { provider, offset, limit } = platformConnectionCatalogQuerySchema.parse(input);
551
+ const query = new URLSearchParams({ offset: String(offset), limit: String(limit) });
552
+ if (provider)
553
+ query.set("provider", provider);
554
+ return this.request(`${route(PLATFORM_ROUTES.connections)}?${query}`, z.object({ data: platformConnectionCatalogSchema }), { maximumResponseBytes: PLATFORM_CONNECTION_CATALOG_LIMITS.responseBytes });
555
+ }
556
+ connectApp(input) {
557
+ const { app } = platformAppConnectQuerySchema.parse(input);
558
+ return this.request(`${route(PLATFORM_ROUTES.connectApp)}?${new URLSearchParams({ app })}`, z.object({ data: platformAppConnectSchema }));
559
+ }
560
+ tools() { return this.request(route(PLATFORM_ROUTES.tools), catalogResultSchema); }
561
+ toolsPage(input = {}) {
562
+ const { offset, limit } = platformCatalogPageQuerySchema.parse(input);
563
+ return this.request(`${route(PLATFORM_ROUTES.tools)}?${new URLSearchParams({ offset: String(offset), limit: String(limit) })}`, z.object({ data: platformCatalogPageSchema }));
564
+ }
565
+ realtimeModels() { return this.request("/tools/realtime/models", z.object({ data: realtimeVoiceModelsSchema })); }
566
+ createRealtimeSession(input, signal) {
567
+ return this.request("/tools/realtime/sessions", z.object({ data: realtimeVoiceGrantSchema }), { method: "POST", body: realtimeVoiceConfigSchema.parse(input), signal });
568
+ }
569
+ async connectVoice(input, options = {}) {
570
+ const { data } = await this.createRealtimeSession(input, options.signal);
571
+ return connectRealtimeVoice(this.baseUrl, data, options);
572
+ }
573
+ async audioTools() {
574
+ const { data } = await this.tools();
575
+ return { data: { ...data, tools: data.tools.filter(tool => /(?:audio|speech|voice|music|sound)/.test(tool.name) || tool.name.startsWith("elevenlabs_")) } };
576
+ }
577
+ generateSpeech(input, idempotencyKey) { return this.createExecution({ ...input, tool: "generate_speech" }, idempotencyKey); }
578
+ transcribeAudio(input, idempotencyKey) { return this.createExecution({ ...input, tool: "transcribe_audio" }, idempotencyKey); }
579
+ translateAudio(input, idempotencyKey) { return this.createExecution({ ...input, tool: "translate_audio" }, idempotencyKey); }
580
+ generateAudioResponse(input, idempotencyKey) { return this.createExecution({ ...input, tool: "generate_audio_response" }, idempotencyKey); }
581
+ skills() { return this.request(PLATFORM_ROUTES.skills, z.object({ data: platformSkillsBundleSchema }), { maximumResponseBytes: 1048640 }); }
582
+ verifyMcpAccess() { return this.request("/oauth/verify?connectionOnly=1", z.object({ data: z.object({ active: z.literal(true) }) })); }
583
+ async skill(name) {
584
+ platformSkillSchema.shape.name.parse(name);
585
+ const { data } = await this.skills();
586
+ const skill = data.skills.find((entry) => entry.name === name);
587
+ if (!skill)
588
+ throw new Error(`The requested ${this.productName} guide is unavailable. Use platform_skills to discover current guides.`);
589
+ return { data: { version: data.version, ...skill } };
590
+ }
591
+ uploadInput(input) {
592
+ return this.request(PLATFORM_ROUTES.upload, z.object({ data: platformInputFileSchema }), { method: "POST", body: platformInputUploadSchema.parse(input) });
593
+ }
594
+ async discoverTools(options = {}) {
595
+ const { query, category, provider, availableOnly, summary } = platformToolDiscoverySchema.parse(options);
596
+ const { data } = await this.tools();
597
+ const search = query && normalizeToolSearch(query);
598
+ const tools = data.tools.filter((tool) => (!search || normalizeToolSearch(`${tool.name} ${tool.title ?? ""} ${tool.description} ${(tool.providers ?? []).map(row => row.name).join(" ")} ${(tool.models ?? []).map(row => row.id).join(" ")}`).includes(search)) &&
599
+ (!category || tool.category === category) && (!provider || tool.providers?.some(row => row.id === provider)) && (!availableOnly || tool.available));
600
+ return { data: { ...data, tools: summary ? tools.map(({ inputSchema, example, ...tool }) => tool) : tools } };
601
+ }
602
+ searchTools(options = {}) {
603
+ const credential = this.config.apiKey;
604
+ return this.request(PLATFORM_ROUTES.discover, z.object({ data: toolDiscoveryResultSchema }), { method: "POST", body: toolDiscoveryInputSchema.parse(options), timeoutMs: TOOL_DISCOVERY_REQUEST_TIMEOUT_MS, toolInput: true })
605
+ .then(result => { this.toolNameHints.remember(result.data.tools.map(tool => tool.name), credential); return result; });
606
+ }
607
+ searchMetaTools(input) {
608
+ const credential = this.config.apiKey;
609
+ return this.request(PLATFORM_ROUTES.metaSearch, z.object({ data: platformMetaSearchResultSchema }), { method: "POST", body: platformMetaSearchSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs })
610
+ .then(result => { this.toolNameHints.remember(result.data.results.flatMap(group => group.tools.map(tool => tool.name)), credential); return result; });
611
+ }
612
+ getToolSchemas(input) { return this.request(PLATFORM_ROUTES.metaSchemas, z.object({ data: platformMetaSchemasResultSchema }), { method: "POST", body: platformMetaSchemasSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
613
+ manageConnections(input) { return this.request(PLATFORM_ROUTES.metaConnections, z.object({ data: platformMetaConnectionsResultSchema }), { method: "POST", body: platformMetaConnectionsSchema.parse(input) }); }
614
+ multiExecute(input) { return this.request(PLATFORM_ROUTES.metaExecute, z.object({ data: platformMetaExecuteResultSchema }), { method: "POST", body: platformMetaExecuteSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
615
+ remoteWorkbench(input) { return this.request(PLATFORM_ROUTES.metaWorkbench, z.object({ data: platformMetaRemoteResultSchema }), { method: "POST", body: platformMetaWorkbenchSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
616
+ remoteBash(input) { return this.request(PLATFORM_ROUTES.metaBash, z.object({ data: platformMetaRemoteResultSchema }), { method: "POST", body: platformMetaBashSchema.parse(input), timeoutMs: PLATFORM_META_LIMITS.requestTimeoutMs }); }
617
+ inspectTool(name) {
618
+ platformToolNameSchema.parse(name);
619
+ const schema = platformCatalogSchema.shape.tools.element.extend(platformCatalogSchema.omit({ tools: true }).shape);
620
+ const credential = this.config.apiKey;
621
+ return this.request(route(PLATFORM_ROUTES.definition, { name: encodeURIComponent(name) }), z.object({ data: schema })).catch(error => {
622
+ if (error instanceof PlatformApiError && error.status === 404 && error.code === "platform_tool_unavailable")
623
+ error.message = `Tool is unavailable in the ${this.productName} catalog. ${this.toolNameHints.hint(name, credential) ?? "Run tools to discover supported operations."}`;
624
+ throw error;
625
+ });
626
+ }
627
+ async tool(name) { return this.inspectTool(name); }
628
+ executions(input = {}) {
629
+ const parsed = platformExecutionListQuerySchema.parse(input);
630
+ const query = new URLSearchParams({ limit: String(parsed.limit) });
631
+ if (parsed.before)
632
+ query.set("before", parsed.before);
633
+ if (parsed.status)
634
+ query.set("status", parsed.status);
635
+ if (parsed.tool)
636
+ query.set("tool", parsed.tool);
637
+ return this.request(`${route(PLATFORM_ROUTES.executions)}?${query}`, z.object({ data: platformExecutionListSchema }));
638
+ }
639
+ monitors() { return this.request(route(PLATFORM_ROUTES.monitors), z.object({ data: z.object({ monitors: z.array(platformMonitorSchema) }) })); }
640
+ createMonitor(input) {
641
+ return this.request(route(PLATFORM_ROUTES.monitors), z.object({ data: z.object({ monitor: platformMonitorSchema }) }), { method: "POST", body: platformMonitorCreateSchema.parse(input) });
642
+ }
643
+ deleteMonitor(id) {
644
+ if (!PLATFORM_MONITOR_ID.test(id))
645
+ throw new Error("Provide a valid monitor ID.");
646
+ return this.request(route(PLATFORM_ROUTES.monitorDelete, { id }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
647
+ }
648
+ monitorEvents(input = {}) {
649
+ const { monitorId, ...rest } = input;
650
+ if (monitorId !== undefined && !PLATFORM_MONITOR_ID.test(monitorId))
651
+ throw new Error("Provide a valid monitor ID.");
652
+ const parsed = platformMonitorEventsQuerySchema.parse(rest);
653
+ const query = new URLSearchParams({ limit: String(parsed.limit) });
654
+ if (parsed.after)
655
+ query.set("after", parsed.after);
656
+ const path = monitorId ? route(PLATFORM_ROUTES.monitorEvents, { id: monitorId }) : route(PLATFORM_ROUTES.monitorFeed);
657
+ return this.request(`${path}?${query}`, z.object({ data: platformMonitorEventsSchema }));
658
+ }
659
+ quoteMonitor(input) {
660
+ return this.request(route(PLATFORM_ROUTES.monitorQuote), z.object({ data: z.object({ quote: platformMonitorQuoteResultSchema }) }), { method: "POST", body: platformMonitorQuoteSchema.parse(input) });
661
+ }
662
+ resumeMonitor(id, input = {}) {
663
+ if (!PLATFORM_MONITOR_ID.test(id))
664
+ throw new Error("Provide a valid monitor ID.");
665
+ return this.request(route(PLATFORM_ROUTES.monitorResume, { id }), z.object({ data: z.object({ monitor: platformMonitorSchema }) }), { method: "POST", body: platformMonitorResumeSchema.parse(input) });
666
+ }
667
+ triggers() { return this.request(route(PLATFORM_ROUTES.triggers), z.object({ data: z.object({ triggers: z.array(platformTriggerSchema) }) })); }
668
+ trigger(id) { return this.request(route(PLATFORM_ROUTES.trigger, { id: triggerId(id) }), triggerResult); }
669
+ createTrigger(input) {
670
+ return this.request(route(PLATFORM_ROUTES.triggers), triggerResult, { method: "POST", body: platformTriggerCreateSchema.parse(input) });
671
+ }
672
+ updateTrigger(id, input) {
673
+ return this.request(route(PLATFORM_ROUTES.triggerUpdate, { id: triggerId(id) }), triggerResult, { method: "POST", body: platformTriggerUpdateSchema.parse(input) });
674
+ }
675
+ deleteTrigger(id) {
676
+ return this.request(route(PLATFORM_ROUTES.triggerDelete, { id: triggerId(id) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
677
+ }
678
+ triggerEvents(input = {}) {
679
+ const { triggerId: id, ...rest } = input;
680
+ const parsed = platformTriggerEventsQuerySchema.parse(rest);
681
+ const query = new URLSearchParams({ limit: String(parsed.limit) });
682
+ if (parsed.after)
683
+ query.set("after", parsed.after);
684
+ const path = id === undefined ? route(PLATFORM_ROUTES.triggerFeed) : route(PLATFORM_ROUTES.triggerEvents, { id: triggerId(id) });
685
+ return this.request(`${path}?${query}`, z.object({ data: platformTriggerEventsSchema }));
686
+ }
687
+ triggerTypes(input) {
688
+ const parsed = platformTriggerTypesQuerySchema.parse(input);
689
+ const query = new URLSearchParams({ app: parsed.app, offset: String(parsed.offset), limit: String(parsed.limit) });
690
+ if (parsed.query)
691
+ query.set("query", parsed.query);
692
+ if (parsed.connectionId)
693
+ query.set("connectionId", parsed.connectionId);
694
+ return this.request(`${route(PLATFORM_ROUTES.triggerTypes)}?${query}`, z.object({ data: platformTriggerTypesSchema }));
695
+ }
696
+ addMonitorDestination(id, destination) {
697
+ if (!PLATFORM_MONITOR_ID.test(id))
698
+ throw new Error("Provide a valid monitor ID.");
699
+ return this.request(route(PLATFORM_ROUTES.monitorDestinations, { id }), z.object({ data: z.object({ monitor: platformMonitorSchema }) }), { method: "POST", body: platformMonitorDestinationInputSchema.parse(destination) });
700
+ }
701
+ removeMonitorDestination(id, destinationId) {
702
+ if (!PLATFORM_MONITOR_ID.test(id))
703
+ throw new Error("Provide a valid monitor ID.");
704
+ if (!/^dst_[A-Za-z0-9_-]{1,100}$/.test(destinationId))
705
+ throw new Error("Provide a destination ID such as dst_….");
706
+ return this.request(route(PLATFORM_ROUTES.monitorDestinationDelete, { id, destinationId }), z.object({ data: z.object({ monitor: platformMonitorSchema }) }), { method: "POST", body: {} });
707
+ }
708
+ addTriggerDestination(id, destination) {
709
+ return this.request(route(PLATFORM_ROUTES.triggerDestinations, { id: triggerId(id) }), triggerResult, { method: "POST", body: platformTriggerDestinationInputSchema.parse(destination) });
710
+ }
711
+ removeTriggerDestination(id, destinationId) {
712
+ if (!/^dst_[A-Za-z0-9_-]{1,100}$/.test(destinationId))
713
+ throw new Error("Provide a destination ID such as dst_….");
714
+ return this.request(route(PLATFORM_ROUTES.triggerDestinationDelete, { id: triggerId(id), destinationId }), triggerResult, { method: "POST", body: {} });
715
+ }
716
+ popularMonitors() { return this.request(route(PLATFORM_ROUTES.monitorsPopular), z.object({ data: z.object({ monitors: z.array(platformPopularMonitorSchema) }) })); }
717
+ customTools() { return this.request(route(PLATFORM_ROUTES.customTools), z.object({ data: z.object({ tools: z.array(platformCustomToolSummarySchema) }) })); }
718
+ customTool(name) { return this.request(route(PLATFORM_ROUTES.customTool, { name: customToolName(name) }), customToolResult); }
719
+ createCustomTool(input) {
720
+ return this.request(route(PLATFORM_ROUTES.customTools), customToolResult, { method: "POST", body: platformCustomToolCreateSchema.parse(input), toolInput: true });
721
+ }
722
+ updateCustomTool(name, input) {
723
+ return this.request(route(PLATFORM_ROUTES.customToolUpdate, { name: customToolName(name) }), customToolResult, { method: "POST", body: platformCustomToolUpdateSchema.parse(input), toolInput: true });
724
+ }
725
+ deleteCustomTool(name) {
726
+ return this.request(route(PLATFORM_ROUTES.customToolDelete, { name: customToolName(name) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
727
+ }
728
+ customToolVersions(name) {
729
+ return this.request(route(PLATFORM_ROUTES.customToolVersions, { name: customToolName(name) }), z.object({ data: z.object({ versions: z.array(platformCustomToolVersionSchema) }) }));
730
+ }
731
+ restoreCustomTool(name, input) {
732
+ return this.request(route(PLATFORM_ROUTES.customToolRestore, { name: customToolName(name) }), customToolResult, { method: "POST", body: platformCustomToolRestoreSchema.parse(input) });
733
+ }
734
+ teamSkills(kind) {
735
+ const query = kind ? `?${new URLSearchParams({ kind: platformTeamSkillListKindSchema.parse(kind) })}` : "";
736
+ return this.request(`${route(PLATFORM_ROUTES.teamSkills)}${query}`, z.object({ data: z.object({ skills: z.array(platformTeamSkillSummarySchema) }) }));
737
+ }
738
+ teamSkill(slug) { return this.request(route(PLATFORM_ROUTES.teamSkill, { slug: teamSkillSlug(slug) }), teamSkillResult, { maximumResponseBytes: PLATFORM_TEAM_SKILL_LIMITS.requestBytes }); }
739
+ createTeamSkill(input) {
740
+ return this.request(route(PLATFORM_ROUTES.teamSkills), teamSkillResult, { method: "POST", body: platformTeamSkillCreateSchema.parse(input), maximumRequestBytes: PLATFORM_TEAM_SKILL_LIMITS.requestBytes, maximumResponseBytes: PLATFORM_TEAM_SKILL_LIMITS.requestBytes });
741
+ }
742
+ updateTeamSkill(slug, input) {
743
+ return this.request(route(PLATFORM_ROUTES.teamSkillUpdate, { slug: teamSkillSlug(slug) }), teamSkillResult, { method: "POST", body: platformTeamSkillUpdateSchema.parse(input), maximumRequestBytes: PLATFORM_TEAM_SKILL_LIMITS.requestBytes, maximumResponseBytes: PLATFORM_TEAM_SKILL_LIMITS.requestBytes });
744
+ }
745
+ deleteTeamSkill(slug) {
746
+ return this.request(route(PLATFORM_ROUTES.teamSkillDelete, { slug: teamSkillSlug(slug) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
747
+ }
748
+ teamSkillVersions(slug) {
749
+ return this.request(route(PLATFORM_ROUTES.teamSkillVersions, { slug: teamSkillSlug(slug) }), z.object({ data: z.object({ versions: z.array(platformTeamSkillVersionSchema) }) }));
750
+ }
751
+ restoreTeamSkill(slug, input) {
752
+ return this.request(route(PLATFORM_ROUTES.teamSkillRestore, { slug: teamSkillSlug(slug) }), teamSkillResult, { method: "POST", body: platformTeamSkillRestoreSchema.parse(input), maximumResponseBytes: PLATFORM_TEAM_SKILL_LIMITS.requestBytes });
753
+ }
754
+ suggestions() { return this.request(route(PLATFORM_ROUTES.suggestions), z.object({ data: platformSuggestionsSchema })); }
755
+ team(input = {}) {
756
+ const query = platformTeamQuerySchema.parse(input);
757
+ return this.request(`${route(PLATFORM_ROUTES.team)}?${new URLSearchParams(Object.entries(query).map(([name, value]) => [name, String(value)]))}`, z.object({ data: platformTeamContextSchema }));
758
+ }
759
+ projectStatus(input = {}) {
760
+ const query = platformProjectStatusQuerySchema.parse(input);
761
+ return this.request(`${route(PLATFORM_ROUTES.projectStatus)}?${new URLSearchParams(Object.entries(query).map(([name, value]) => [name, String(value)]))}`, z.object({ data: platformProjectStatusSchema }));
762
+ }
763
+ access() {
764
+ this.assertMemberPolicies();
765
+ return this.request(route(PLATFORM_ROUTES.access), z.object({ data: platformTeamAccessSchema }));
766
+ }
767
+ updateMemberAccess(id, input) {
768
+ this.assertMemberPolicies();
769
+ if (!/^[A-Za-z0-9-]{1,128}$/.test(id))
770
+ throw new Error("Provide a member ID as `access` lists it.");
771
+ return this.request(route(PLATFORM_ROUTES.memberAccess, { id }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: platformMemberAccessUpdateSchema.parse(input) });
772
+ }
773
+ assertMemberPolicies() {
774
+ if (this.sharedProjectAccess)
775
+ throw new Error("Relvo uses shared project access. Manage membership in project settings.");
776
+ }
777
+ workflows() { return this.request(route(PLATFORM_ROUTES.workflows), z.object({ data: z.object({ workflows: z.array(platformWorkflowSummarySchema) }) })); }
778
+ workflow(slug) { return this.request(route(PLATFORM_ROUTES.workflow, { slug: workflowSlug(slug) }), workflowResult); }
779
+ createWorkflow(input) {
780
+ return this.request(route(PLATFORM_ROUTES.workflows), workflowResult, { method: "POST", body: platformWorkflowCreateSchema.parse(input) });
781
+ }
782
+ updateWorkflow(slug, input) {
783
+ return this.request(route(PLATFORM_ROUTES.workflowUpdate, { slug: workflowSlug(slug) }), workflowResult, { method: "POST", body: platformWorkflowUpdateSchema.parse(input) });
784
+ }
785
+ deleteWorkflow(slug) {
786
+ return this.request(route(PLATFORM_ROUTES.workflowDelete, { slug: workflowSlug(slug) }), z.object({ data: z.object({ success: z.literal(true) }) }), { method: "POST", body: {} });
787
+ }
788
+ workflowVersions(slug) {
789
+ return this.request(route(PLATFORM_ROUTES.workflowVersions, { slug: workflowSlug(slug) }), z.object({ data: z.object({ versions: z.array(platformWorkflowVersionSchema) }) }));
790
+ }
791
+ runWorkflow(slug, input, idempotencyKey) {
792
+ if (!platformIdempotencyKeySchema.safeParse(idempotencyKey).success)
793
+ throw new Error("Keep an idempotency key of 8–128 letters, digits, periods, underscores, colons or hyphens for this run.");
794
+ return this.request(route(PLATFORM_ROUTES.workflowRuns, { slug: workflowSlug(slug) }), workflowRunResult, { method: "POST", body: platformWorkflowRunCreateSchema.parse(input), idempotencyKey });
795
+ }
796
+ workflowRuns(slug, query = {}) {
797
+ const parsed = platformWorkflowRunListQuerySchema.parse(query), search = new URLSearchParams({ limit: String(parsed.limit), ...(parsed.before ? { before: parsed.before } : {}) });
798
+ return this.request(`${route(PLATFORM_ROUTES.workflowRuns, { slug: workflowSlug(slug) })}?${search}`, z.object({ data: platformWorkflowRunListSchema }));
799
+ }
800
+ workflowRun(id) { return this.request(route(PLATFORM_ROUTES.workflowRun, { id: workflowRunId(id) }), workflowRunResult); }
801
+ cancelWorkflowRun(id) {
802
+ return this.request(route(PLATFORM_ROUTES.workflowRunCancel, { id: workflowRunId(id) }), workflowRunResult, { method: "POST", body: {} });
803
+ }
804
+ monitorableTools() { return this.request(route(PLATFORM_ROUTES.monitorTools), z.object({ data: z.object({ tools: z.array(platformMonitorableToolSchema) }) })); }
805
+ quote(input, signal) {
806
+ const parsed = platformQuoteRequestSchema.safeParse(input);
807
+ if (!parsed.success)
808
+ throw new Error("Provide a tool name, a JSON object of arguments and an optional valid provider identifier.");
809
+ return this.request(route(PLATFORM_ROUTES.quote), quoteResultSchema, { method: "POST", body: parsed.data, toolInput: true, signal }).then(value => {
810
+ const receivedAt = Date.now(), expiresAt = Date.parse(value.data.expiresAt);
811
+ return { ...value, timing: { receivedAtUtc: new Date(receivedAt).toISOString(),
812
+ expiresInSeconds: Number.isFinite(expiresAt) ? Math.max(0, Math.floor((expiresAt - receivedAt) / 1000)) : null } };
813
+ });
814
+ }
815
+ createExecution(input, idempotencyKey, signal) {
816
+ const parsed = platformCreateExecutionSchema.safeParse(input);
817
+ if (!parsed.success)
818
+ throw new Error("Provide a tool, arguments, the quote's priceVersion and maximumChargeMicros, and an optional valid provider identifier.");
819
+ if (!platformIdempotencyKeySchema.safeParse(idempotencyKey).success)
820
+ throw new Error("Keep an idempotency key of 8–128 letters, digits, periods, underscores, colons or hyphens for this execution.");
821
+ return this.request(route(PLATFORM_ROUTES.tool, { tool: parsed.data.tool }), executionResultSchema, { method: "POST", body: parsed.data, idempotencyKey, toolInput: true, signal });
822
+ }
823
+ recoverExecution(input, signal) {
824
+ return this.request(route(PLATFORM_ROUTES.executionRecovery), z.object({ data: z.object({ execution: platformExecutionSchema.nullable() }) }), { method: "POST", body: platformRecoverExecutionSchema.parse(input), signal });
825
+ }
826
+ execution(id, signal, waitMs = 0) {
827
+ if (!platformExecutionIdSchema.safeParse(id).success)
828
+ throw new Error("Provide a valid execution ID.");
829
+ return this.request(route(PLATFORM_ROUTES.execution, { id }), executionResultSchema, { signal, ...this.heldRead(waitMs) });
830
+ }
831
+ executionContext(id, signal, waitMs = 0) {
832
+ platformExecutionIdSchema.parse(id);
833
+ return this.request(route(PLATFORM_ROUTES.executionContext, { id }), executionContextResultSchema, { signal, maximumResponseBytes: TOOL_RESULT_CONTEXT_MAX_BYTES + 65536, ...this.heldRead(waitMs) })
834
+ .then(result => executionReceiptContext(result)).catch(error => contextBudgetFailure(error, id, "execution"));
835
+ }
836
+ heldRead(waitMs) {
837
+ const seconds = this.holdsReads ? Math.min(PLATFORM_EXECUTION_MAX_WAIT_SECONDS, Math.floor(waitMs / 1000)) : 0;
838
+ return seconds > 0 ? { prefer: `wait=${seconds}`, timeoutMs: seconds * 1000 + 20000 } : {};
839
+ }
840
+ readExecutionResult(id, selection = {}) {
841
+ platformExecutionIdSchema.parse(id);
842
+ const body = toolResultSelectionSchema.parse(selection);
843
+ return this.request(route(PLATFORM_ROUTES.executionResult, { id }), z.object({ data: platformExecutionResultPageSchema }), { method: "POST", body, maximumResponseBytes: TOOL_RESULT_CONTEXT_MAX_BYTES + 65536 })
844
+ .then(result => platformExecutionResultPageContext(result, { readWith: "readExecutionResult" }))
845
+ .catch(error => contextBudgetFailure(error, id, "page"));
846
+ }
847
+ waitExecution(id, timeoutMs = 20000, signal) {
848
+ return this.waitForExecution(id, timeoutMs, signal, (executionId, abort, waitMs) => this.execution(executionId, abort, waitMs));
849
+ }
850
+ waitExecutionContext(id, timeoutMs = 20000, signal) {
851
+ return this.waitForExecution(id, timeoutMs, signal, (executionId, abort, waitMs) => this.executionContext(executionId, abort, waitMs));
852
+ }
853
+ async waitForExecution(id, timeoutMs, signal, read) {
854
+ platformExecutionIdSchema.parse(id);
855
+ if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > 900000)
856
+ throw new Error("Wait must be between 1 and 900 seconds.");
857
+ const controller = new AbortController();
858
+ const timeout = new Error("Execution wait timed out; use the same execution ID to continue reading.");
859
+ const timer = setTimeout(() => controller.abort(timeout), timeoutMs);
860
+ const abort = () => controller.abort(signal?.reason);
861
+ signal?.addEventListener("abort", abort, { once: true });
862
+ if (signal?.aborted)
863
+ abort();
864
+ let latest = null;
865
+ const until = Date.now() + timeoutMs;
866
+ try {
867
+ for (;;) {
868
+ controller.signal.throwIfAborted();
869
+ const asked = Date.now();
870
+ latest = (await read(id, controller.signal, Math.max(0, until - asked - 1000))).data;
871
+ if (!["queued", "running", "waiting"].includes(latest.status))
872
+ return { data: latest, id, timedOut: false };
873
+ const pauseMs = this.holdsReads ? Math.max(0, 5000 - (Date.now() - asked)) : 5000;
874
+ if (pauseMs > 0)
875
+ await new Promise((resolve, reject) => {
876
+ const done = () => { controller.signal.removeEventListener("abort", cancelled); resolve(); };
877
+ const pause = setTimeout(done, pauseMs);
878
+ const cancelled = () => { clearTimeout(pause); controller.signal.removeEventListener("abort", cancelled); reject(controller.signal.reason); };
879
+ controller.signal.addEventListener("abort", cancelled, { once: true });
880
+ if (controller.signal.aborted)
881
+ cancelled();
882
+ });
883
+ }
884
+ }
885
+ catch (error) {
886
+ if (error !== timeout)
887
+ throw error;
888
+ return { data: latest, id, timedOut: true };
889
+ }
890
+ finally {
891
+ clearTimeout(timer);
892
+ signal?.removeEventListener("abort", abort);
893
+ }
894
+ }
895
+ cancelExecution(id) {
896
+ if (!platformExecutionIdSchema.safeParse(id).success)
897
+ throw new Error("Provide a valid execution ID.");
898
+ return this.request(route(PLATFORM_ROUTES.executionCancel, { id }), executionResultSchema, { method: "POST" });
899
+ }
900
+ artifactDownload(id, artifactId) {
901
+ if (!platformExecutionIdSchema.safeParse(id).success || !platformArtifactIdSchema.safeParse(artifactId).success)
902
+ throw new Error("Provide valid execution and artifact IDs.");
903
+ return this.request(route(PLATFORM_ROUTES.artifactDownload, { id, artifact: artifactId }), z.object({ data: platformArtifactDownloadSchema }));
904
+ }
905
+ }