@executor-js/sdk 1.5.17 → 1.5.18

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.
@@ -3167,7 +3167,14 @@ var ConnectionCreateInput = Schema2.Struct({
3167
3167
  }).check(
3168
3168
  Schema2.makeFilter((payload) => {
3169
3169
  const originCount = (payload.from === void 0 ? 0 : 1) + (payload.inputs === void 0 ? 0 : 1);
3170
- if (originCount !== 1) return "Expected exactly one provider credential origin";
3170
+ const isNoAuth = String(payload.template) === String(NO_AUTH_TEMPLATE);
3171
+ if (isNoAuth) {
3172
+ if (originCount > 0) {
3173
+ return 'A no-auth connection (template "none") takes no provider credential origin';
3174
+ }
3175
+ } else if (originCount !== 1) {
3176
+ return "Expected exactly one provider credential origin";
3177
+ }
3171
3178
  if (payload.inputs !== void 0 && Object.keys(payload.inputs).length === 0) {
3172
3179
  return "Expected at least one provider credential input";
3173
3180
  }
@@ -3251,9 +3258,23 @@ var OAuthCreateClientInput = Schema2.Struct({
3251
3258
  tokenUrl: Schema2.String,
3252
3259
  grant: OAuthGrantSchema,
3253
3260
  clientId: Schema2.String,
3254
- clientSecret: Schema2.String,
3255
3261
  resource: Schema2.optional(Schema2.NullOr(Schema2.String))
3256
3262
  });
3263
+ var OAuthCreateClientHandoffInput = Schema2.Struct({
3264
+ integration: Schema2.String,
3265
+ owner: Schema2.optional(OwnerSchema),
3266
+ slug: Schema2.optional(Schema2.String),
3267
+ grant: Schema2.optional(OAuthGrantSchema),
3268
+ clientId: Schema2.optional(Schema2.String),
3269
+ authorizationUrl: Schema2.optional(Schema2.String),
3270
+ tokenUrl: Schema2.optional(Schema2.String),
3271
+ resource: Schema2.optional(Schema2.NullOr(Schema2.String)),
3272
+ label: Schema2.optional(Schema2.String)
3273
+ });
3274
+ var OAuthCreateClientHandoffOutput = Schema2.Struct({
3275
+ url: Schema2.String,
3276
+ instructions: Schema2.String
3277
+ });
3257
3278
  var OAuthClientOutputRef = Schema2.Struct({
3258
3279
  client: Schema2.String
3259
3280
  });
@@ -3332,6 +3353,8 @@ var PolicyUpdateInputStd = schemaToStandard(PolicyUpdateInput);
3332
3353
  var PolicyRemoveInputStd = schemaToStandard(PolicyRemoveInput);
3333
3354
  var OAuthClientsListOutputStd = schemaToStandard(OAuthClientsListOutput);
3334
3355
  var OAuthCreateClientInputStd = schemaToStandard(OAuthCreateClientInput);
3356
+ var OAuthCreateClientHandoffInputStd = schemaToStandard(OAuthCreateClientHandoffInput);
3357
+ var OAuthCreateClientHandoffOutputStd = schemaToStandard(OAuthCreateClientHandoffOutput);
3335
3358
  var OAuthClientOutputRefStd = schemaToStandard(OAuthClientOutputRef);
3336
3359
  var OAuthRegisterDynamicInputStd = schemaToStandard(OAuthRegisterDynamicInput);
3337
3360
  var OAuthRemoveClientInputStd = schemaToStandard(OAuthRemoveClientInput);
@@ -3417,12 +3440,28 @@ var createConnectionInputFromTool = (input) => {
3417
3440
  )
3418
3441
  };
3419
3442
  };
3420
- var connectionCreateHandoffUrl = (webBaseUrl, input) => {
3443
+ var connectionCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
3421
3444
  const search = new URLSearchParams({ addAccount: "1" });
3422
3445
  if (input.owner !== void 0) search.set("owner", input.owner);
3423
3446
  if (input.template !== void 0) search.set("template", input.template);
3424
3447
  if (input.label !== void 0) search.set("label", input.label);
3425
- const path = `/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
3448
+ const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
3449
+ const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
3450
+ if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
3451
+ return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
3452
+ };
3453
+ var oauthClientCreateHandoffUrl = (webBaseUrl, orgSlug, input) => {
3454
+ const search = new URLSearchParams({ addAccount: "1", oauthClient: "1" });
3455
+ if (input.owner !== void 0) search.set("owner", input.owner);
3456
+ if (input.slug !== void 0) search.set("clientSlug", input.slug);
3457
+ if (input.grant !== void 0) search.set("grant", input.grant);
3458
+ if (input.clientId !== void 0) search.set("clientId", input.clientId);
3459
+ if (input.authorizationUrl !== void 0) search.set("authorizationUrl", input.authorizationUrl);
3460
+ if (input.tokenUrl !== void 0) search.set("tokenUrl", input.tokenUrl);
3461
+ if (input.resource != null && input.resource.length > 0) search.set("resource", input.resource);
3462
+ if (input.label !== void 0) search.set("label", input.label);
3463
+ const orgPrefix = orgSlug !== void 0 && orgSlug.length > 0 ? `/${orgSlug}` : "";
3464
+ const path = `${orgPrefix}/integrations/${encodeURIComponent(input.integration)}?${search.toString()}`;
3426
3465
  if (webBaseUrl === void 0 || webBaseUrl.length === 0) return path;
3427
3466
  return new URL(path, webBaseUrl.endsWith("/") ? webBaseUrl : `${webBaseUrl}/`).toString();
3428
3467
  };
@@ -3485,9 +3524,16 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3485
3524
  }),
3486
3525
  tool({
3487
3526
  name: "connections.create",
3488
- description: "Low-level create or replace for a saved connection from provider item references. For normal API keys/tokens, use `connections.createHandoff` so the user enters the credential in the web UI. OAuth credentials should use `oauth.start`.",
3527
+ description: 'Low-level create or replace for a saved connection from provider item references. For a no-auth integration (public MCP server, public REST API), pass `template: "none"` with no `from`/`inputs` to wire it up directly. For normal API keys/tokens, use `connections.createHandoff` so the user enters the credential in the web UI. OAuth credentials should use `oauth.start`.',
3489
3528
  inputSchema: ConnectionCreateInputStd,
3490
3529
  outputSchema: ConnectionOutputStd,
3530
+ // Creating a connection binds a credential reference and roots a new
3531
+ // tool catalog: every tool that connection produces then becomes
3532
+ // callable. Even the no-auth (`template: "none"`) path pulls tools
3533
+ // from an arbitrary endpoint. Prompt-injected code could silently
3534
+ // wire an attacker-chosen integration or credential, so this is
3535
+ // approval-gated (the v1 `sources.configure` carried the same guard).
3536
+ annotations: { requiresApproval: true },
3491
3537
  execute: (input, { ctx }) => Effect5.map(
3492
3538
  ctx.connections.create(createConnectionInputFromTool(input)),
3493
3539
  connectionToOutput
@@ -3499,7 +3545,7 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3499
3545
  inputSchema: ConnectionCreateHandoffInputStd,
3500
3546
  outputSchema: ConnectionCreateHandoffOutputStd,
3501
3547
  execute: (input) => {
3502
- const url = connectionCreateHandoffUrl(options.webBaseUrl, input);
3548
+ const url = connectionCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
3503
3549
  return Effect5.succeed({
3504
3550
  url,
3505
3551
  instructions: "Ask the user to open this URL and add the account in the Executor web UI. Do not ask them to paste the credential value into chat. After they finish, call connections.list for the integration to discover the created connection."
@@ -3511,6 +3557,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3511
3557
  description: "Remove a saved connection and its produced tools by owner, integration, and connection name.",
3512
3558
  inputSchema: ConnectionRefInputStd,
3513
3559
  outputSchema: RemovedOutputStd,
3560
+ // Deleting a connection drops it and every tool it produced, which
3561
+ // prompt-injected code could use to disrupt an integration or force a
3562
+ // re-add flow. Approval-gated, matching v1 `sources.remove`.
3563
+ annotations: { requiresApproval: true },
3514
3564
  execute: (input, { ctx }) => Effect5.map(ctx.connections.remove(connectionRefFromInput(input)), () => ({
3515
3565
  removed: true
3516
3566
  }))
@@ -3520,6 +3570,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3520
3570
  description: "Re-run an integration's tool production for a saved connection, replacing that connection's persisted tools.",
3521
3571
  inputSchema: ConnectionRefInputStd,
3522
3572
  outputSchema: ConnectionsRefreshOutputStd,
3573
+ // Refresh replaces a connection's persisted tool set; for a mutable
3574
+ // upstream (an MCP server whose catalog can change) this can swap in
3575
+ // different tools without confirmation. Approval-gated, matching v1
3576
+ // `sources.refresh`.
3577
+ annotations: { requiresApproval: true },
3523
3578
  execute: (input, { ctx }) => Effect5.map(ctx.connections.refresh(connectionRefFromInput(input)), (tools) => ({
3524
3579
  tools: tools.map(toolToOutput)
3525
3580
  }))
@@ -3563,9 +3618,20 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3563
3618
  }),
3564
3619
  tool({
3565
3620
  name: "oauth.clients.create",
3566
- description: "Register or replace an owner-scoped OAuth client from explicit client credentials. Use grant `client_credentials` for machine OAuth or `authorization_code` for browser consent flows.",
3621
+ description: "Register or replace an owner-scoped OAuth client WITHOUT a client secret: a PUBLIC client (PKCE / authorization_code) or a discovery-prefill placeholder. To register a CONFIDENTIAL client that has a secret, call `oauth.clients.createHandoff` instead so the human enters the secret in the web UI; never pass a client secret through this tool.",
3567
3622
  inputSchema: OAuthCreateClientInputStd,
3568
3623
  outputSchema: OAuthClientOutputRefStd,
3624
+ // This persists an OAuth client and REPLACES on slug collision. It
3625
+ // takes NO client secret: a secret would have to travel through the
3626
+ // agent's context window, so a confidential app is registered by the
3627
+ // human via `oauth.clients.createHandoff`. An empty secret registers a
3628
+ // PUBLIC client. The remaining risk is the write itself: prompt-injected
3629
+ // code could register a client with an attacker-controlled
3630
+ // authorizationUrl/tokenUrl, then drive `oauth.start` to mint a
3631
+ // connection and route the user's tokens to the attacker. The
3632
+ // highest-value gate here; matches v1 `sources.bindings.set`, which
3633
+ // guarded credential writes.
3634
+ annotations: { requiresApproval: true },
3569
3635
  execute: (input, { ctx }) => Effect5.map(
3570
3636
  ctx.oauth.createClient({
3571
3637
  owner: input.owner,
@@ -3574,17 +3640,40 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3574
3640
  tokenUrl: input.tokenUrl,
3575
3641
  grant: input.grant,
3576
3642
  clientId: input.clientId,
3577
- clientSecret: input.clientSecret,
3643
+ // No secret crosses the agent boundary; an empty secret registers
3644
+ // a public client. Confidential clients go through
3645
+ // `oauth.clients.createHandoff`.
3646
+ clientSecret: "",
3578
3647
  resource: input.resource ?? null
3579
3648
  }),
3580
3649
  (client) => ({ client: String(client) })
3581
3650
  )
3582
3651
  }),
3652
+ tool({
3653
+ name: "oauth.clients.createHandoff",
3654
+ description: "Return a browser URL that opens the Register-OAuth-app form for one integration, pre-filled with the non-secret fields (client id, endpoints, grant). Use this for any CONFIDENTIAL OAuth app: the user types the client secret directly in the web UI instead of sending it through the agent. After they register the app, call `oauth.clients.list` to discover its owner and slug, then `oauth.start`.",
3655
+ inputSchema: OAuthCreateClientHandoffInputStd,
3656
+ outputSchema: OAuthCreateClientHandoffOutputStd,
3657
+ // Pure URL builder: no DB write, no token, no secret. This is the SAFE
3658
+ // path (it routes the secret to the human in the browser), so it is
3659
+ // deliberately NOT approval-gated, mirroring `connections.createHandoff`.
3660
+ execute: (input) => {
3661
+ const url = oauthClientCreateHandoffUrl(options.webBaseUrl, options.orgSlug, input);
3662
+ return Effect5.succeed({
3663
+ url,
3664
+ instructions: "Ask the user to open this URL and register the OAuth app in the Executor web UI, entering the client secret there. Do not ask them to paste the client secret into chat. After they finish, call oauth.clients.list to find the registered client (owner + slug), then oauth.start."
3665
+ });
3666
+ }
3667
+ }),
3583
3668
  tool({
3584
3669
  name: "oauth.clients.registerDynamic",
3585
3670
  description: "Register an OAuth client through RFC 7591 Dynamic Client Registration and save the minted client for later `oauth.start` calls.",
3586
3671
  inputSchema: OAuthRegisterDynamicInputStd,
3587
3672
  outputSchema: OAuthClientOutputRefStd,
3673
+ // Same risk class as `oauth.clients.create`: registers a client at a
3674
+ // caller-supplied endpoint and persists the minted credentials for
3675
+ // later `oauth.start` abuse. Approval-gated. See `oauth.clients.create`.
3676
+ annotations: { requiresApproval: true },
3588
3677
  execute: (input, { ctx }) => Effect5.map(
3589
3678
  ctx.oauth.registerDynamicClient({
3590
3679
  owner: input.owner,
@@ -3607,6 +3696,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3607
3696
  description: "Remove an owner-scoped OAuth client by owner and slug. Existing connections are not cascaded.",
3608
3697
  inputSchema: OAuthRemoveClientInputStd,
3609
3698
  outputSchema: RemovedOutputStd,
3699
+ // Removing a client breaks token refresh for every connection that
3700
+ // depends on it (a silent DoS) and can force re-auth through an
3701
+ // attacker-supplied replacement. Approval-gated, matching v1
3702
+ // `sources.bindings.remove`.
3703
+ annotations: { requiresApproval: true },
3610
3704
  execute: (input, { ctx }) => Effect5.map(
3611
3705
  ctx.oauth.removeClient(input.owner, OAuthClientSlug.make(input.slug)),
3612
3706
  () => ({ removed: true })
@@ -3631,6 +3725,14 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3631
3725
  description: "Start OAuth through a registered client to mint a connection for an integration. `client_credentials` clients return `connected`; authorization-code clients return an authorization URL and state.",
3632
3726
  inputSchema: OAuthStartInputStd,
3633
3727
  outputSchema: OAuthStartOutputStd,
3728
+ // This is the materialization step that turns a registered client
3729
+ // into a live connection. For `client_credentials` it completes
3730
+ // synchronously (status `connected`) with no browser step, so a
3731
+ // prompt-injected call against an attacker-registered client mints a
3732
+ // credentialed connection with no human in the loop. The
3733
+ // authorization-code path already returns a URL the user must visit,
3734
+ // but one gate on the whole tool covers the silent path cleanly.
3735
+ annotations: { requiresApproval: true },
3634
3736
  execute: (input, { ctx }) => Effect5.map(
3635
3737
  ctx.oauth.start({
3636
3738
  client: OAuthClientSlug.make(input.client),
@@ -3678,6 +3780,11 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3678
3780
  description: "Create a tool policy. `pattern` matches a tool address tail (`integration.connection.tool`, `integration.*`, `*`); `action` is approve/require_approval/block. `owner` is org (workspace guardrail) or user (personal).",
3679
3781
  inputSchema: PolicyCreateInputStd,
3680
3782
  outputSchema: PolicyOutputStd,
3783
+ // A policy decides which tools run without confirmation, so creating
3784
+ // one can silence every other approval gate (e.g. `approve *`). It
3785
+ // must itself require approval, otherwise prompt-injected code could
3786
+ // disable approvals by writing its own bypass policy.
3787
+ annotations: { requiresApproval: true },
3681
3788
  execute: (input, { ctx }) => Effect5.map(
3682
3789
  ctx.core.policies.create({
3683
3790
  owner: input.owner,
@@ -3698,6 +3805,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3698
3805
  description: "Update a tool policy's pattern and/or action by id + owner.",
3699
3806
  inputSchema: PolicyUpdateInputStd,
3700
3807
  outputSchema: PolicyOutputStd,
3808
+ // Editing a policy can broaden a pattern or flip an action to
3809
+ // `approve`, weakening an approval gate just as creation can, so it
3810
+ // requires approval too. See `policies.create`.
3811
+ annotations: { requiresApproval: true },
3701
3812
  execute: (input, { ctx }) => Effect5.map(
3702
3813
  ctx.core.policies.update({
3703
3814
  id: input.id,
@@ -3719,6 +3830,10 @@ var coreToolsPlugin = definePlugin((options = {}) => ({
3719
3830
  description: "Remove a tool policy by id + owner.",
3720
3831
  inputSchema: PolicyRemoveInputStd,
3721
3832
  outputSchema: RemovedOutputStd,
3833
+ // Removing a policy can drop a `block` or `require_approval`
3834
+ // guardrail, so deletion is also approval-gated. See
3835
+ // `policies.create`.
3836
+ annotations: { requiresApproval: true },
3722
3837
  execute: (input, { ctx }) => Effect5.map(
3723
3838
  ctx.core.policies.remove({
3724
3839
  id: input.id,
@@ -5178,6 +5293,7 @@ var createExecutor = (config) => Effect8.gen(function* () {
5178
5293
  const plugins = config.coreTools ? [
5179
5294
  coreToolsPlugin({
5180
5295
  webBaseUrl: config.coreTools.webBaseUrl,
5296
+ orgSlug: config.coreTools.orgSlug,
5181
5297
  includeProviders: config.coreTools.includeProviders
5182
5298
  }),
5183
5299
  ...userPlugins
@@ -6739,4 +6855,4 @@ export {
6739
6855
  collectTables,
6740
6856
  createExecutor
6741
6857
  };
6742
- //# sourceMappingURL=chunk-5Q35SELN.js.map
6858
+ //# sourceMappingURL=chunk-UWBP7WLB.js.map