@oisincoveney/pipeline 1.5.4 → 1.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/config.d.ts CHANGED
@@ -57,8 +57,11 @@ declare const configSchema: z.ZodObject<{
57
57
  }, z.core.$strict>>>;
58
58
  mcp_servers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
59
59
  args: z.ZodOptional<z.ZodArray<z.ZodString>>;
60
- command: z.ZodString;
60
+ bearer_token_env_var: z.ZodOptional<z.ZodString>;
61
+ command: z.ZodOptional<z.ZodString>;
61
62
  env: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
63
+ headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
64
+ url: z.ZodOptional<z.ZodString>;
62
65
  }, z.core.$strict>>>;
63
66
  orchestrator: z.ZodObject<{
64
67
  hooks: z.ZodOptional<z.ZodArray<z.ZodString>>;
package/dist/config.js CHANGED
@@ -21366,9 +21366,59 @@ var pathRefSchema = exports_external.object({
21366
21366
  }).strict();
21367
21367
  var mcpServerSchema = exports_external.object({
21368
21368
  args: exports_external.array(exports_external.string()).optional(),
21369
- command: exports_external.string().min(1),
21370
- env: exports_external.record(exports_external.string(), exports_external.string()).optional()
21371
- }).strict();
21369
+ bearer_token_env_var: exports_external.string().min(1).optional(),
21370
+ command: exports_external.string().min(1).optional(),
21371
+ env: exports_external.record(exports_external.string(), exports_external.string()).optional(),
21372
+ headers: exports_external.record(exports_external.string(), exports_external.string()).optional(),
21373
+ url: exports_external.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
21374
+ message: "MCP server url must use http or https"
21375
+ }).optional()
21376
+ }).strict().superRefine((server, ctx) => {
21377
+ const hasCommand = Boolean(server.command);
21378
+ const hasUrl = Boolean(server.url);
21379
+ if (hasCommand === hasUrl) {
21380
+ ctx.addIssue({
21381
+ code: "custom",
21382
+ message: "MCP server must declare exactly one of command or url",
21383
+ path: hasCommand ? ["url"] : ["command"]
21384
+ });
21385
+ }
21386
+ if (hasUrl && server.args) {
21387
+ ctx.addIssue({
21388
+ code: "custom",
21389
+ message: "args are only valid for command MCP servers",
21390
+ path: ["args"]
21391
+ });
21392
+ }
21393
+ if (hasUrl && server.env) {
21394
+ ctx.addIssue({
21395
+ code: "custom",
21396
+ message: "env is only valid for command MCP servers",
21397
+ path: ["env"]
21398
+ });
21399
+ }
21400
+ if (hasCommand && server.headers) {
21401
+ ctx.addIssue({
21402
+ code: "custom",
21403
+ message: "headers are only valid for url MCP servers",
21404
+ path: ["headers"]
21405
+ });
21406
+ }
21407
+ if (hasCommand && server.bearer_token_env_var) {
21408
+ ctx.addIssue({
21409
+ code: "custom",
21410
+ message: "bearer_token_env_var is only valid for url MCP servers",
21411
+ path: ["bearer_token_env_var"]
21412
+ });
21413
+ }
21414
+ if (hasUrl && server.bearer_token_env_var && Object.keys(server.headers ?? {}).some((key) => key.toLowerCase() === "authorization")) {
21415
+ ctx.addIssue({
21416
+ code: "custom",
21417
+ message: "headers.Authorization cannot be combined with bearer_token_env_var",
21418
+ path: ["bearer_token_env_var"]
21419
+ });
21420
+ }
21421
+ });
21372
21422
  var instructionsSchema = exports_external.object({
21373
21423
  inline: exports_external.string().min(1).optional(),
21374
21424
  path: exports_external.string().min(1).optional()
package/dist/index.js CHANGED
@@ -27439,9 +27439,59 @@ var pathRefSchema = exports_external.object({
27439
27439
  }).strict();
27440
27440
  var mcpServerSchema = exports_external.object({
27441
27441
  args: exports_external.array(exports_external.string()).optional(),
27442
- command: exports_external.string().min(1),
27443
- env: exports_external.record(exports_external.string(), exports_external.string()).optional()
27444
- }).strict();
27442
+ bearer_token_env_var: exports_external.string().min(1).optional(),
27443
+ command: exports_external.string().min(1).optional(),
27444
+ env: exports_external.record(exports_external.string(), exports_external.string()).optional(),
27445
+ headers: exports_external.record(exports_external.string(), exports_external.string()).optional(),
27446
+ url: exports_external.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
27447
+ message: "MCP server url must use http or https"
27448
+ }).optional()
27449
+ }).strict().superRefine((server, ctx) => {
27450
+ const hasCommand = Boolean(server.command);
27451
+ const hasUrl = Boolean(server.url);
27452
+ if (hasCommand === hasUrl) {
27453
+ ctx.addIssue({
27454
+ code: "custom",
27455
+ message: "MCP server must declare exactly one of command or url",
27456
+ path: hasCommand ? ["url"] : ["command"]
27457
+ });
27458
+ }
27459
+ if (hasUrl && server.args) {
27460
+ ctx.addIssue({
27461
+ code: "custom",
27462
+ message: "args are only valid for command MCP servers",
27463
+ path: ["args"]
27464
+ });
27465
+ }
27466
+ if (hasUrl && server.env) {
27467
+ ctx.addIssue({
27468
+ code: "custom",
27469
+ message: "env is only valid for command MCP servers",
27470
+ path: ["env"]
27471
+ });
27472
+ }
27473
+ if (hasCommand && server.headers) {
27474
+ ctx.addIssue({
27475
+ code: "custom",
27476
+ message: "headers are only valid for url MCP servers",
27477
+ path: ["headers"]
27478
+ });
27479
+ }
27480
+ if (hasCommand && server.bearer_token_env_var) {
27481
+ ctx.addIssue({
27482
+ code: "custom",
27483
+ message: "bearer_token_env_var is only valid for url MCP servers",
27484
+ path: ["bearer_token_env_var"]
27485
+ });
27486
+ }
27487
+ if (hasUrl && server.bearer_token_env_var && Object.keys(server.headers ?? {}).some((key) => key.toLowerCase() === "authorization")) {
27488
+ ctx.addIssue({
27489
+ code: "custom",
27490
+ message: "headers.Authorization cannot be combined with bearer_token_env_var",
27491
+ path: ["bearer_token_env_var"]
27492
+ });
27493
+ }
27494
+ });
27445
27495
  var instructionsSchema = exports_external.object({
27446
27496
  inline: exports_external.string().min(1).optional(),
27447
27497
  path: exports_external.string().min(1).optional()
@@ -28866,25 +28916,22 @@ function cliDispatchLine(route) {
28866
28916
  }
28867
28917
  function runnerCliCommand(route) {
28868
28918
  if (route.runnerId === "codex") {
28869
- return `codex exec --json -C <repo-root> --sandbox ${codexSandbox(route.profile)} --skip-git-repo-check <node prompt>`;
28919
+ return "codex exec --json -C <repo-root> --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check <node prompt>";
28870
28920
  }
28871
28921
  if (route.runnerId === "kimi") {
28872
- return `kimi --print --agent-file .kimi/agents/${route.profileId}.yaml --work-dir <repo-root> --final-message-only --prompt <node prompt>`;
28922
+ return `kimi --print --agent-file .kimi/agents/${route.profileId}.yaml --work-dir <repo-root> --yolo --final-message-only --prompt <node prompt>`;
28873
28923
  }
28874
28924
  if (route.runnerId === "opencode") {
28875
28925
  return `opencode run --agent ${route.profileId} --format json --dir <repo-root> <node prompt>`;
28876
28926
  }
28877
28927
  if (route.runnerId === "claude") {
28878
- return `claude --agent ${route.profileId} --print -p <node prompt>`;
28928
+ return `claude --agent ${route.profileId} --print --dangerously-skip-permissions -p <node prompt>`;
28879
28929
  }
28880
28930
  if (route.runnerId === "pi") {
28881
28931
  return "pi --print --no-session <node prompt>";
28882
28932
  }
28883
28933
  return `${route.runnerId} <node prompt>`;
28884
28934
  }
28885
- function codexSandbox(profile) {
28886
- return profile.filesystem?.mode === "workspace-write" ? "workspace-write" : "read-only";
28887
- }
28888
28935
  function nodePromptContract(workflowId, routes) {
28889
28936
  const hasCliRoutes = routes.some((route) => route.kind === "cli");
28890
28937
  const lead = hasCliRoutes ? "For each CLI node prompt include:" : "For each native node prompt include:";
@@ -36995,6 +37042,8 @@ import {
36995
37042
  } from "node:fs";
36996
37043
  import { tmpdir } from "node:os";
36997
37044
  import { dirname as dirname3, join as join5 } from "node:path";
37045
+ var TOML_BARE_KEY_PATTERN = /^[A-Za-z0-9_-]+$/;
37046
+
36998
37047
  class RunnerCapabilityError extends Error {
36999
37048
  constructor(message) {
37000
37049
  super(message);
@@ -37017,6 +37066,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options2 = {})
37017
37066
  ...claudeToolArgs(tools),
37018
37067
  ...mcpArgs,
37019
37068
  ...skillArgs,
37069
+ "--dangerously-skip-permissions",
37020
37070
  "-p",
37021
37071
  prompt
37022
37072
  ];
@@ -37029,10 +37079,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options2 = {})
37029
37079
  ...optionalModelArgs(harness, options2.runner, options2.actor),
37030
37080
  ...mcpArgs,
37031
37081
  ...skillArgs,
37032
- "--sandbox",
37033
- codexSandboxFor(options2.actor),
37034
- "--config",
37035
- 'approval_policy="never"',
37082
+ "--dangerously-bypass-approvals-and-sandbox",
37036
37083
  "--skip-git-repo-check",
37037
37084
  prompt
37038
37085
  ];
@@ -37070,6 +37117,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options2 = {})
37070
37117
  ...optionalModelArgs(harness, options2.runner, options2.actor),
37071
37118
  ...mcpArgs,
37072
37119
  ...skillArgs,
37120
+ "--yolo",
37073
37121
  "--final-message-only",
37074
37122
  "--prompt",
37075
37123
  prompt
@@ -37080,9 +37128,6 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options2 = {})
37080
37128
  }
37081
37129
  }
37082
37130
  }
37083
- function codexSandboxFor(actor) {
37084
- return actor?.filesystem?.mode === "read-only" ? "read-only" : "workspace-write";
37085
- }
37086
37131
  function createRunnerLaunchPlan(config2, input) {
37087
37132
  const profile = input.profileId ? config2.profiles[input.profileId] : undefined;
37088
37133
  if (input.profileId && !profile) {
@@ -37215,12 +37260,12 @@ function mcpArgsFor(runnerType, config2, actor) {
37215
37260
  if (runnerType === "claude") {
37216
37261
  return [
37217
37262
  "--mcp-config",
37218
- JSON.stringify(toClaudeKimiMcpConfig(servers)),
37263
+ JSON.stringify(toClaudeMcpConfig(servers)),
37219
37264
  "--strict-mcp-config"
37220
37265
  ];
37221
37266
  }
37222
37267
  if (runnerType === "kimi") {
37223
- return ["--mcp-config", JSON.stringify(toClaudeKimiMcpConfig(servers))];
37268
+ return ["--mcp-config", JSON.stringify(toKimiMcpConfig(servers))];
37224
37269
  }
37225
37270
  if (runnerType === "codex") {
37226
37271
  return codexMcpArgs(servers);
@@ -37240,39 +37285,120 @@ function runnerEnv(runnerType, config2, actor, worktreePath, nodeId) {
37240
37285
  PIPELINE_WORKTREE: worktreePath
37241
37286
  };
37242
37287
  }
37243
- function toClaudeKimiMcpConfig(servers) {
37244
- return { mcpServers: servers };
37288
+ function isRemoteMcpServer(server) {
37289
+ return typeof server.url === "string";
37245
37290
  }
37246
- function toOpenCodeMcpConfig(servers) {
37291
+ function headersWithBearerTokenEnv(server, renderTokenRef) {
37292
+ const headers = { ...server.headers ?? {} };
37293
+ if (server.bearer_token_env_var) {
37294
+ headers.Authorization = `Bearer ${renderTokenRef(server.bearer_token_env_var)}`;
37295
+ }
37296
+ return Object.keys(headers).length > 0 ? headers : undefined;
37297
+ }
37298
+ function toClaudeMcpConfig(servers) {
37299
+ return {
37300
+ mcpServers: Object.fromEntries(Object.entries(servers).map(([id, server]) => {
37301
+ if (isRemoteMcpServer(server)) {
37302
+ const headers = headersWithBearerTokenEnv(server, (envVar) => `\${${envVar}}`);
37303
+ return [
37304
+ id,
37305
+ {
37306
+ type: "http",
37307
+ url: server.url,
37308
+ ...headers ? { headers } : {}
37309
+ }
37310
+ ];
37311
+ }
37312
+ return [
37313
+ id,
37314
+ {
37315
+ command: server.command,
37316
+ ...server.args ? { args: server.args } : {},
37317
+ ...server.env ? { env: server.env } : {}
37318
+ }
37319
+ ];
37320
+ }))
37321
+ };
37322
+ }
37323
+ function toKimiMcpConfig(servers) {
37247
37324
  return {
37248
- mcp: Object.fromEntries(Object.entries(servers).map(([id, server]) => [
37325
+ mcpServers: Object.fromEntries(Object.entries(servers).map(([id, server]) => [
37249
37326
  id,
37250
- {
37251
- command: [server.command, ...server.args ?? []],
37252
- enabled: true,
37253
- ...server.env ? { environment: server.env } : {},
37254
- type: "local"
37327
+ isRemoteMcpServer(server) ? {
37328
+ url: server.url,
37329
+ ...server.headers ? { headers: server.headers } : {},
37330
+ ...server.bearer_token_env_var ? { bearerTokenEnvVar: server.bearer_token_env_var } : {}
37331
+ } : {
37332
+ command: server.command,
37333
+ ...server.args ? { args: server.args } : {},
37334
+ ...server.env ? { env: server.env } : {}
37255
37335
  }
37256
37336
  ]))
37257
37337
  };
37258
37338
  }
37339
+ function toOpenCodeMcpConfig(servers) {
37340
+ return {
37341
+ mcp: Object.fromEntries(Object.entries(servers).map(([id, server]) => {
37342
+ if (isRemoteMcpServer(server)) {
37343
+ const headers = headersWithBearerTokenEnv(server, (envVar) => `{env:${envVar}}`);
37344
+ return [
37345
+ id,
37346
+ {
37347
+ enabled: true,
37348
+ ...headers ? { headers } : {},
37349
+ type: "remote",
37350
+ url: server.url
37351
+ }
37352
+ ];
37353
+ }
37354
+ return [
37355
+ id,
37356
+ {
37357
+ command: [server.command, ...server.args ?? []],
37358
+ enabled: true,
37359
+ ...server.env ? { environment: server.env } : {},
37360
+ type: "local"
37361
+ }
37362
+ ];
37363
+ }))
37364
+ };
37365
+ }
37259
37366
  function codexMcpArgs(servers) {
37260
- return Object.entries(servers).flatMap(([id, server]) => [
37261
- "--config",
37262
- `mcp_servers.${id}.command=${tomlValue(server.command)}`,
37263
- ...server.args ? ["--config", `mcp_servers.${id}.args=${tomlValue(server.args)}`] : [],
37264
- ...server.env ? ["--config", `mcp_servers.${id}.env=${tomlValue(server.env)}`] : []
37265
- ]);
37367
+ return Object.entries(servers).flatMap(([id, server]) => {
37368
+ if (isRemoteMcpServer(server)) {
37369
+ return [
37370
+ "--config",
37371
+ `mcp_servers.${id}.url=${tomlValue(server.url)}`,
37372
+ ...server.headers ? [
37373
+ "--config",
37374
+ `mcp_servers.${id}.http_headers=${tomlValue(server.headers)}`
37375
+ ] : [],
37376
+ ...server.bearer_token_env_var ? [
37377
+ "--config",
37378
+ `mcp_servers.${id}.bearer_token_env_var=${tomlValue(server.bearer_token_env_var)}`
37379
+ ] : []
37380
+ ];
37381
+ }
37382
+ return [
37383
+ "--config",
37384
+ `mcp_servers.${id}.command=${tomlValue(server.command)}`,
37385
+ ...server.args ? ["--config", `mcp_servers.${id}.args=${tomlValue(server.args)}`] : [],
37386
+ ...server.env ? ["--config", `mcp_servers.${id}.env=${tomlValue(server.env)}`] : []
37387
+ ];
37388
+ });
37266
37389
  }
37267
37390
  function tomlValue(value) {
37268
37391
  if (Array.isArray(value)) {
37269
37392
  return `[${value.map(tomlValue).join(", ")}]`;
37270
37393
  }
37271
37394
  if (value && typeof value === "object") {
37272
- return `{ ${Object.entries(value).map(([key, item]) => `${key} = ${tomlValue(item)}`).join(", ")} }`;
37395
+ return `{ ${Object.entries(value).map(([key, item]) => `${tomlKey(key)} = ${tomlValue(item)}`).join(", ")} }`;
37273
37396
  }
37274
37397
  return JSON.stringify(value);
37275
37398
  }
37399
+ function tomlKey(key) {
37400
+ return TOML_BARE_KEY_PATTERN.test(key) ? key : JSON.stringify(key);
37401
+ }
37276
37402
  function nativeStrategy(config2, input, runnerId) {
37277
37403
  const runner = config2.runners[runnerId];
37278
37404
  const profile = input.profileId ? config2.profiles[input.profileId] : undefined;
@@ -37957,8 +38083,19 @@ function renderMcpReferences(ids, registry2) {
37957
38083
  "Loaded MCP servers:",
37958
38084
  ...ids.map((id) => {
37959
38085
  const server = registry2[id];
38086
+ if (server?.url) {
38087
+ return [
38088
+ `## ${id}`,
38089
+ "transport: http",
38090
+ `url: ${server.url}`,
38091
+ `headers: ${Object.keys(server.headers ?? {}).join(", ") || "none"}`,
38092
+ `bearer_token_env_var: ${server.bearer_token_env_var ?? "none"}`
38093
+ ].join(`
38094
+ `);
38095
+ }
37960
38096
  return [
37961
38097
  `## ${id}`,
38098
+ "transport: stdio",
37962
38099
  `command: ${server?.command ?? ""}`,
37963
38100
  `args: ${(server?.args ?? []).join(" ") || "none"}`,
37964
38101
  `env: ${Object.keys(server?.env ?? {}).join(", ") || "none"}`
@@ -28569,9 +28569,59 @@ var pathRefSchema = exports_external.object({
28569
28569
  }).strict();
28570
28570
  var mcpServerSchema = exports_external.object({
28571
28571
  args: exports_external.array(exports_external.string()).optional(),
28572
- command: exports_external.string().min(1),
28573
- env: exports_external.record(exports_external.string(), exports_external.string()).optional()
28574
- }).strict();
28572
+ bearer_token_env_var: exports_external.string().min(1).optional(),
28573
+ command: exports_external.string().min(1).optional(),
28574
+ env: exports_external.record(exports_external.string(), exports_external.string()).optional(),
28575
+ headers: exports_external.record(exports_external.string(), exports_external.string()).optional(),
28576
+ url: exports_external.string().url().refine((value) => ["http:", "https:"].includes(new URL(value).protocol), {
28577
+ message: "MCP server url must use http or https"
28578
+ }).optional()
28579
+ }).strict().superRefine((server, ctx) => {
28580
+ const hasCommand = Boolean(server.command);
28581
+ const hasUrl = Boolean(server.url);
28582
+ if (hasCommand === hasUrl) {
28583
+ ctx.addIssue({
28584
+ code: "custom",
28585
+ message: "MCP server must declare exactly one of command or url",
28586
+ path: hasCommand ? ["url"] : ["command"]
28587
+ });
28588
+ }
28589
+ if (hasUrl && server.args) {
28590
+ ctx.addIssue({
28591
+ code: "custom",
28592
+ message: "args are only valid for command MCP servers",
28593
+ path: ["args"]
28594
+ });
28595
+ }
28596
+ if (hasUrl && server.env) {
28597
+ ctx.addIssue({
28598
+ code: "custom",
28599
+ message: "env is only valid for command MCP servers",
28600
+ path: ["env"]
28601
+ });
28602
+ }
28603
+ if (hasCommand && server.headers) {
28604
+ ctx.addIssue({
28605
+ code: "custom",
28606
+ message: "headers are only valid for url MCP servers",
28607
+ path: ["headers"]
28608
+ });
28609
+ }
28610
+ if (hasCommand && server.bearer_token_env_var) {
28611
+ ctx.addIssue({
28612
+ code: "custom",
28613
+ message: "bearer_token_env_var is only valid for url MCP servers",
28614
+ path: ["bearer_token_env_var"]
28615
+ });
28616
+ }
28617
+ if (hasUrl && server.bearer_token_env_var && Object.keys(server.headers ?? {}).some((key) => key.toLowerCase() === "authorization")) {
28618
+ ctx.addIssue({
28619
+ code: "custom",
28620
+ message: "headers.Authorization cannot be combined with bearer_token_env_var",
28621
+ path: ["bearer_token_env_var"]
28622
+ });
28623
+ }
28624
+ });
28575
28625
  var instructionsSchema = exports_external.object({
28576
28626
  inline: exports_external.string().min(1).optional(),
28577
28627
  path: exports_external.string().min(1).optional()
@@ -29485,6 +29535,8 @@ import {
29485
29535
  } from "node:fs";
29486
29536
  import { tmpdir } from "node:os";
29487
29537
  import { dirname, join as join3 } from "node:path";
29538
+ var TOML_BARE_KEY_PATTERN = /^[A-Za-z0-9_-]+$/;
29539
+
29488
29540
  class RunnerCapabilityError extends Error {
29489
29541
  constructor(message) {
29490
29542
  super(message);
@@ -29507,6 +29559,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
29507
29559
  ...claudeToolArgs(tools),
29508
29560
  ...mcpArgs,
29509
29561
  ...skillArgs,
29562
+ "--dangerously-skip-permissions",
29510
29563
  "-p",
29511
29564
  prompt
29512
29565
  ];
@@ -29519,10 +29572,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
29519
29572
  ...optionalModelArgs(harness, options.runner, options.actor),
29520
29573
  ...mcpArgs,
29521
29574
  ...skillArgs,
29522
- "--sandbox",
29523
- codexSandboxFor(options.actor),
29524
- "--config",
29525
- 'approval_policy="never"',
29575
+ "--dangerously-bypass-approvals-and-sandbox",
29526
29576
  "--skip-git-repo-check",
29527
29577
  prompt
29528
29578
  ];
@@ -29560,6 +29610,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
29560
29610
  ...optionalModelArgs(harness, options.runner, options.actor),
29561
29611
  ...mcpArgs,
29562
29612
  ...skillArgs,
29613
+ "--yolo",
29563
29614
  "--final-message-only",
29564
29615
  "--prompt",
29565
29616
  prompt
@@ -29570,9 +29621,6 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
29570
29621
  }
29571
29622
  }
29572
29623
  }
29573
- function codexSandboxFor(actor) {
29574
- return actor?.filesystem?.mode === "read-only" ? "read-only" : "workspace-write";
29575
- }
29576
29624
  function createRunnerLaunchPlan(config2, input) {
29577
29625
  const profile = input.profileId ? config2.profiles[input.profileId] : undefined;
29578
29626
  if (input.profileId && !profile) {
@@ -29699,12 +29747,12 @@ function mcpArgsFor(runnerType, config2, actor) {
29699
29747
  if (runnerType === "claude") {
29700
29748
  return [
29701
29749
  "--mcp-config",
29702
- JSON.stringify(toClaudeKimiMcpConfig(servers)),
29750
+ JSON.stringify(toClaudeMcpConfig(servers)),
29703
29751
  "--strict-mcp-config"
29704
29752
  ];
29705
29753
  }
29706
29754
  if (runnerType === "kimi") {
29707
- return ["--mcp-config", JSON.stringify(toClaudeKimiMcpConfig(servers))];
29755
+ return ["--mcp-config", JSON.stringify(toKimiMcpConfig(servers))];
29708
29756
  }
29709
29757
  if (runnerType === "codex") {
29710
29758
  return codexMcpArgs(servers);
@@ -29724,39 +29772,120 @@ function runnerEnv(runnerType, config2, actor, worktreePath, nodeId) {
29724
29772
  PIPELINE_WORKTREE: worktreePath
29725
29773
  };
29726
29774
  }
29727
- function toClaudeKimiMcpConfig(servers) {
29728
- return { mcpServers: servers };
29775
+ function isRemoteMcpServer(server) {
29776
+ return typeof server.url === "string";
29729
29777
  }
29730
- function toOpenCodeMcpConfig(servers) {
29778
+ function headersWithBearerTokenEnv(server, renderTokenRef) {
29779
+ const headers = { ...server.headers ?? {} };
29780
+ if (server.bearer_token_env_var) {
29781
+ headers.Authorization = `Bearer ${renderTokenRef(server.bearer_token_env_var)}`;
29782
+ }
29783
+ return Object.keys(headers).length > 0 ? headers : undefined;
29784
+ }
29785
+ function toClaudeMcpConfig(servers) {
29731
29786
  return {
29732
- mcp: Object.fromEntries(Object.entries(servers).map(([id, server]) => [
29787
+ mcpServers: Object.fromEntries(Object.entries(servers).map(([id, server]) => {
29788
+ if (isRemoteMcpServer(server)) {
29789
+ const headers = headersWithBearerTokenEnv(server, (envVar) => `\${${envVar}}`);
29790
+ return [
29791
+ id,
29792
+ {
29793
+ type: "http",
29794
+ url: server.url,
29795
+ ...headers ? { headers } : {}
29796
+ }
29797
+ ];
29798
+ }
29799
+ return [
29800
+ id,
29801
+ {
29802
+ command: server.command,
29803
+ ...server.args ? { args: server.args } : {},
29804
+ ...server.env ? { env: server.env } : {}
29805
+ }
29806
+ ];
29807
+ }))
29808
+ };
29809
+ }
29810
+ function toKimiMcpConfig(servers) {
29811
+ return {
29812
+ mcpServers: Object.fromEntries(Object.entries(servers).map(([id, server]) => [
29733
29813
  id,
29734
- {
29735
- command: [server.command, ...server.args ?? []],
29736
- enabled: true,
29737
- ...server.env ? { environment: server.env } : {},
29738
- type: "local"
29814
+ isRemoteMcpServer(server) ? {
29815
+ url: server.url,
29816
+ ...server.headers ? { headers: server.headers } : {},
29817
+ ...server.bearer_token_env_var ? { bearerTokenEnvVar: server.bearer_token_env_var } : {}
29818
+ } : {
29819
+ command: server.command,
29820
+ ...server.args ? { args: server.args } : {},
29821
+ ...server.env ? { env: server.env } : {}
29739
29822
  }
29740
29823
  ]))
29741
29824
  };
29742
29825
  }
29826
+ function toOpenCodeMcpConfig(servers) {
29827
+ return {
29828
+ mcp: Object.fromEntries(Object.entries(servers).map(([id, server]) => {
29829
+ if (isRemoteMcpServer(server)) {
29830
+ const headers = headersWithBearerTokenEnv(server, (envVar) => `{env:${envVar}}`);
29831
+ return [
29832
+ id,
29833
+ {
29834
+ enabled: true,
29835
+ ...headers ? { headers } : {},
29836
+ type: "remote",
29837
+ url: server.url
29838
+ }
29839
+ ];
29840
+ }
29841
+ return [
29842
+ id,
29843
+ {
29844
+ command: [server.command, ...server.args ?? []],
29845
+ enabled: true,
29846
+ ...server.env ? { environment: server.env } : {},
29847
+ type: "local"
29848
+ }
29849
+ ];
29850
+ }))
29851
+ };
29852
+ }
29743
29853
  function codexMcpArgs(servers) {
29744
- return Object.entries(servers).flatMap(([id, server]) => [
29745
- "--config",
29746
- `mcp_servers.${id}.command=${tomlValue(server.command)}`,
29747
- ...server.args ? ["--config", `mcp_servers.${id}.args=${tomlValue(server.args)}`] : [],
29748
- ...server.env ? ["--config", `mcp_servers.${id}.env=${tomlValue(server.env)}`] : []
29749
- ]);
29854
+ return Object.entries(servers).flatMap(([id, server]) => {
29855
+ if (isRemoteMcpServer(server)) {
29856
+ return [
29857
+ "--config",
29858
+ `mcp_servers.${id}.url=${tomlValue(server.url)}`,
29859
+ ...server.headers ? [
29860
+ "--config",
29861
+ `mcp_servers.${id}.http_headers=${tomlValue(server.headers)}`
29862
+ ] : [],
29863
+ ...server.bearer_token_env_var ? [
29864
+ "--config",
29865
+ `mcp_servers.${id}.bearer_token_env_var=${tomlValue(server.bearer_token_env_var)}`
29866
+ ] : []
29867
+ ];
29868
+ }
29869
+ return [
29870
+ "--config",
29871
+ `mcp_servers.${id}.command=${tomlValue(server.command)}`,
29872
+ ...server.args ? ["--config", `mcp_servers.${id}.args=${tomlValue(server.args)}`] : [],
29873
+ ...server.env ? ["--config", `mcp_servers.${id}.env=${tomlValue(server.env)}`] : []
29874
+ ];
29875
+ });
29750
29876
  }
29751
29877
  function tomlValue(value) {
29752
29878
  if (Array.isArray(value)) {
29753
29879
  return `[${value.map(tomlValue).join(", ")}]`;
29754
29880
  }
29755
29881
  if (value && typeof value === "object") {
29756
- return `{ ${Object.entries(value).map(([key, item]) => `${key} = ${tomlValue(item)}`).join(", ")} }`;
29882
+ return `{ ${Object.entries(value).map(([key, item]) => `${tomlKey(key)} = ${tomlValue(item)}`).join(", ")} }`;
29757
29883
  }
29758
29884
  return JSON.stringify(value);
29759
29885
  }
29886
+ function tomlKey(key) {
29887
+ return TOML_BARE_KEY_PATTERN.test(key) ? key : JSON.stringify(key);
29888
+ }
29760
29889
  function nativeStrategy(config2, input, runnerId) {
29761
29890
  const runner = config2.runners[runnerId];
29762
29891
  const profile = input.profileId ? config2.profiles[input.profileId] : undefined;
@@ -30663,8 +30792,19 @@ function renderMcpReferences(ids, registry2) {
30663
30792
  "Loaded MCP servers:",
30664
30793
  ...ids.map((id) => {
30665
30794
  const server = registry2[id];
30795
+ if (server?.url) {
30796
+ return [
30797
+ `## ${id}`,
30798
+ "transport: http",
30799
+ `url: ${server.url}`,
30800
+ `headers: ${Object.keys(server.headers ?? {}).join(", ") || "none"}`,
30801
+ `bearer_token_env_var: ${server.bearer_token_env_var ?? "none"}`
30802
+ ].join(`
30803
+ `);
30804
+ }
30666
30805
  return [
30667
30806
  `## ${id}`,
30807
+ "transport: stdio",
30668
30808
  `command: ${server?.command ?? ""}`,
30669
30809
  `args: ${(server?.args ?? []).join(" ") || "none"}`,
30670
30810
  `env: ${Object.keys(server?.env ?? {}).join(", ") || "none"}`
package/dist/runner.js CHANGED
@@ -7215,6 +7215,8 @@ var {
7215
7215
  } = getIpcExport();
7216
7216
 
7217
7217
  // src/runner.ts
7218
+ var TOML_BARE_KEY_PATTERN = /^[A-Za-z0-9_-]+$/;
7219
+
7218
7220
  class RunnerCapabilityError extends Error {
7219
7221
  constructor(message) {
7220
7222
  super(message);
@@ -7270,6 +7272,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
7270
7272
  ...claudeToolArgs(tools),
7271
7273
  ...mcpArgs,
7272
7274
  ...skillArgs,
7275
+ "--dangerously-skip-permissions",
7273
7276
  "-p",
7274
7277
  prompt
7275
7278
  ];
@@ -7282,10 +7285,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
7282
7285
  ...optionalModelArgs(harness, options.runner, options.actor),
7283
7286
  ...mcpArgs,
7284
7287
  ...skillArgs,
7285
- "--sandbox",
7286
- codexSandboxFor(options.actor),
7287
- "--config",
7288
- 'approval_policy="never"',
7288
+ "--dangerously-bypass-approvals-and-sandbox",
7289
7289
  "--skip-git-repo-check",
7290
7290
  prompt
7291
7291
  ];
@@ -7323,6 +7323,7 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
7323
7323
  ...optionalModelArgs(harness, options.runner, options.actor),
7324
7324
  ...mcpArgs,
7325
7325
  ...skillArgs,
7326
+ "--yolo",
7326
7327
  "--final-message-only",
7327
7328
  "--prompt",
7328
7329
  prompt
@@ -7333,9 +7334,6 @@ function harnessArgv(harness, prompt, worktreePath, contextFile, options = {}) {
7333
7334
  }
7334
7335
  }
7335
7336
  }
7336
- function codexSandboxFor(actor) {
7337
- return actor?.filesystem?.mode === "read-only" ? "read-only" : "workspace-write";
7338
- }
7339
7337
  async function execaHarness(harness, prompt, contextFile, worktreePath) {
7340
7338
  if (harness === "pi") {
7341
7339
  return execaHarnessPi(prompt, contextFile, worktreePath);
@@ -7545,12 +7543,12 @@ function mcpArgsFor(runnerType, config, actor) {
7545
7543
  if (runnerType === "claude") {
7546
7544
  return [
7547
7545
  "--mcp-config",
7548
- JSON.stringify(toClaudeKimiMcpConfig(servers)),
7546
+ JSON.stringify(toClaudeMcpConfig(servers)),
7549
7547
  "--strict-mcp-config"
7550
7548
  ];
7551
7549
  }
7552
7550
  if (runnerType === "kimi") {
7553
- return ["--mcp-config", JSON.stringify(toClaudeKimiMcpConfig(servers))];
7551
+ return ["--mcp-config", JSON.stringify(toKimiMcpConfig(servers))];
7554
7552
  }
7555
7553
  if (runnerType === "codex") {
7556
7554
  return codexMcpArgs(servers);
@@ -7570,39 +7568,120 @@ function runnerEnv(runnerType, config, actor, worktreePath, nodeId) {
7570
7568
  PIPELINE_WORKTREE: worktreePath
7571
7569
  };
7572
7570
  }
7573
- function toClaudeKimiMcpConfig(servers) {
7574
- return { mcpServers: servers };
7571
+ function isRemoteMcpServer(server) {
7572
+ return typeof server.url === "string";
7575
7573
  }
7576
- function toOpenCodeMcpConfig(servers) {
7574
+ function headersWithBearerTokenEnv(server, renderTokenRef) {
7575
+ const headers = { ...server.headers ?? {} };
7576
+ if (server.bearer_token_env_var) {
7577
+ headers.Authorization = `Bearer ${renderTokenRef(server.bearer_token_env_var)}`;
7578
+ }
7579
+ return Object.keys(headers).length > 0 ? headers : undefined;
7580
+ }
7581
+ function toClaudeMcpConfig(servers) {
7577
7582
  return {
7578
- mcp: Object.fromEntries(Object.entries(servers).map(([id, server]) => [
7583
+ mcpServers: Object.fromEntries(Object.entries(servers).map(([id, server]) => {
7584
+ if (isRemoteMcpServer(server)) {
7585
+ const headers = headersWithBearerTokenEnv(server, (envVar) => `\${${envVar}}`);
7586
+ return [
7587
+ id,
7588
+ {
7589
+ type: "http",
7590
+ url: server.url,
7591
+ ...headers ? { headers } : {}
7592
+ }
7593
+ ];
7594
+ }
7595
+ return [
7596
+ id,
7597
+ {
7598
+ command: server.command,
7599
+ ...server.args ? { args: server.args } : {},
7600
+ ...server.env ? { env: server.env } : {}
7601
+ }
7602
+ ];
7603
+ }))
7604
+ };
7605
+ }
7606
+ function toKimiMcpConfig(servers) {
7607
+ return {
7608
+ mcpServers: Object.fromEntries(Object.entries(servers).map(([id, server]) => [
7579
7609
  id,
7580
- {
7581
- command: [server.command, ...server.args ?? []],
7582
- enabled: true,
7583
- ...server.env ? { environment: server.env } : {},
7584
- type: "local"
7610
+ isRemoteMcpServer(server) ? {
7611
+ url: server.url,
7612
+ ...server.headers ? { headers: server.headers } : {},
7613
+ ...server.bearer_token_env_var ? { bearerTokenEnvVar: server.bearer_token_env_var } : {}
7614
+ } : {
7615
+ command: server.command,
7616
+ ...server.args ? { args: server.args } : {},
7617
+ ...server.env ? { env: server.env } : {}
7585
7618
  }
7586
7619
  ]))
7587
7620
  };
7588
7621
  }
7622
+ function toOpenCodeMcpConfig(servers) {
7623
+ return {
7624
+ mcp: Object.fromEntries(Object.entries(servers).map(([id, server]) => {
7625
+ if (isRemoteMcpServer(server)) {
7626
+ const headers = headersWithBearerTokenEnv(server, (envVar) => `{env:${envVar}}`);
7627
+ return [
7628
+ id,
7629
+ {
7630
+ enabled: true,
7631
+ ...headers ? { headers } : {},
7632
+ type: "remote",
7633
+ url: server.url
7634
+ }
7635
+ ];
7636
+ }
7637
+ return [
7638
+ id,
7639
+ {
7640
+ command: [server.command, ...server.args ?? []],
7641
+ enabled: true,
7642
+ ...server.env ? { environment: server.env } : {},
7643
+ type: "local"
7644
+ }
7645
+ ];
7646
+ }))
7647
+ };
7648
+ }
7589
7649
  function codexMcpArgs(servers) {
7590
- return Object.entries(servers).flatMap(([id, server]) => [
7591
- "--config",
7592
- `mcp_servers.${id}.command=${tomlValue(server.command)}`,
7593
- ...server.args ? ["--config", `mcp_servers.${id}.args=${tomlValue(server.args)}`] : [],
7594
- ...server.env ? ["--config", `mcp_servers.${id}.env=${tomlValue(server.env)}`] : []
7595
- ]);
7650
+ return Object.entries(servers).flatMap(([id, server]) => {
7651
+ if (isRemoteMcpServer(server)) {
7652
+ return [
7653
+ "--config",
7654
+ `mcp_servers.${id}.url=${tomlValue(server.url)}`,
7655
+ ...server.headers ? [
7656
+ "--config",
7657
+ `mcp_servers.${id}.http_headers=${tomlValue(server.headers)}`
7658
+ ] : [],
7659
+ ...server.bearer_token_env_var ? [
7660
+ "--config",
7661
+ `mcp_servers.${id}.bearer_token_env_var=${tomlValue(server.bearer_token_env_var)}`
7662
+ ] : []
7663
+ ];
7664
+ }
7665
+ return [
7666
+ "--config",
7667
+ `mcp_servers.${id}.command=${tomlValue(server.command)}`,
7668
+ ...server.args ? ["--config", `mcp_servers.${id}.args=${tomlValue(server.args)}`] : [],
7669
+ ...server.env ? ["--config", `mcp_servers.${id}.env=${tomlValue(server.env)}`] : []
7670
+ ];
7671
+ });
7596
7672
  }
7597
7673
  function tomlValue(value) {
7598
7674
  if (Array.isArray(value)) {
7599
7675
  return `[${value.map(tomlValue).join(", ")}]`;
7600
7676
  }
7601
7677
  if (value && typeof value === "object") {
7602
- return `{ ${Object.entries(value).map(([key, item]) => `${key} = ${tomlValue(item)}`).join(", ")} }`;
7678
+ return `{ ${Object.entries(value).map(([key, item]) => `${tomlKey(key)} = ${tomlValue(item)}`).join(", ")} }`;
7603
7679
  }
7604
7680
  return JSON.stringify(value);
7605
7681
  }
7682
+ function tomlKey(key) {
7683
+ return TOML_BARE_KEY_PATTERN.test(key) ? key : JSON.stringify(key);
7684
+ }
7606
7685
  function nativeStrategy(config, input, runnerId) {
7607
7686
  const runner = config.runners[runnerId];
7608
7687
  const profile = input.profileId ? config.profiles[input.profileId] : undefined;
@@ -108,12 +108,33 @@ receive explicit grants:
108
108
 
109
109
  - `rules`: named markdown rule files.
110
110
  - `skills`: named skill files.
111
- - `mcp_servers`: named MCP command definitions.
111
+ - `mcp_servers`: named MCP server definitions. Servers may be local stdio
112
+ commands or remote streamable HTTP endpoints.
112
113
  - `tools`: allowed host tools only.
113
114
  - `filesystem`: read-only or workspace-write plus allow/deny paths.
114
115
  - `network`: inherited or disabled.
115
116
  - `output`: text, JSON, JSONL, or JSON Schema output.
116
117
 
118
+ MCP servers support two strict shapes:
119
+
120
+ ```yaml
121
+ mcp_servers:
122
+ docs:
123
+ command: npx
124
+ args: ["-y", "@example/docs-mcp"]
125
+ env:
126
+ DOCS_TOKEN: token
127
+ memory:
128
+ url: https://memory-mcp.momokaya.ee/mcp/
129
+ bearer_token_env_var: MEMORY_MCP_TOKEN
130
+ headers:
131
+ X-Memory-Region: eu
132
+ ```
133
+
134
+ Exactly one of `command` or `url` is required. `args` and `env` apply only to
135
+ command servers. `headers` and `bearer_token_env_var` apply only to URL
136
+ servers.
137
+
117
138
  JSON Schema outputs are hard contracts. The runtime validates normalized agent
118
139
  output before the node can pass. Schema outputs also get a bounded repair pass
119
140
  by default:
package/package.json CHANGED
@@ -73,7 +73,7 @@
73
73
  "prepack": "bun run build:cli"
74
74
  },
75
75
  "type": "module",
76
- "version": "1.5.4",
76
+ "version": "1.5.5",
77
77
  "description": "",
78
78
  "main": "index.js",
79
79
  "keywords": [],