@minionry/minion 0.7.36 → 0.7.39

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 (120) hide show
  1. package/bin/commands/minions.js +19 -0
  2. package/bin/minion.js +33 -4
  3. package/dist/server/cli/headless/claude-invoker-process.js +23 -7
  4. package/dist/server/cli/headless/claude-invoker-stream.js +8 -3
  5. package/dist/server/cli/headless/haiku-assessments.js +2 -1
  6. package/dist/server/cli/headless/mcp-config.js +2 -1
  7. package/dist/server/engines/claude/claude-command.js +1 -0
  8. package/dist/server/engines/factory.js +2 -0
  9. package/dist/server/engines/hermes/HermesEngine.js +112 -219
  10. package/dist/server/engines/hermes/hermes-gateway-registry.js +467 -0
  11. package/dist/server/index.js +10 -4
  12. package/dist/server/mcp/bouncer-integration.js +3 -2
  13. package/dist/server/mcp/classifier/ClaudeBouncerClassifier.js +35 -8
  14. package/dist/server/mcp/classifier/shadow-eval-scheduler.js +42 -0
  15. package/dist/server/mcp/classifier/telemetry.js +0 -0
  16. package/dist/server/mcp/permission-channels.js +176 -0
  17. package/dist/server/mcp/security-analysis.js +9 -1
  18. package/dist/server/mcp/security-audit.js +22 -2
  19. package/dist/server/mcp/security-patterns.js +73 -1
  20. package/dist/server/mcp/server.js +80 -50
  21. package/dist/server/routes/internal.js +6 -1
  22. package/dist/server/routes/notifications.js +6 -18
  23. package/dist/server/server-setup.js +24 -48
  24. package/dist/server/services/analytics.js +4 -2
  25. package/dist/server/services/browser/host-mock.js +6 -1
  26. package/dist/server/services/browser/host.js +27 -3
  27. package/dist/server/services/browser/reasoning-agent.js +3 -1
  28. package/dist/server/services/chain/chain-engine.js +494 -0
  29. package/dist/server/services/chain/chain-store.js +542 -0
  30. package/dist/server/services/git/haiku.js +2 -2
  31. package/dist/server/services/plan/agents/check-injection.md +43 -2
  32. package/dist/server/services/plan/agents/review-code.md +62 -3
  33. package/dist/server/services/plan/agents/review-quality.md +52 -2
  34. package/dist/server/services/plan/board-export.js +4 -0
  35. package/dist/server/services/plan/composer-prompt.js +27 -8
  36. package/dist/server/services/plan/composer.js +10 -4
  37. package/dist/server/services/plan/config-installer.js +4 -83
  38. package/dist/server/services/plan/executor.js +155 -73
  39. package/dist/server/services/plan/issue-effort.js +9 -21
  40. package/dist/server/services/plan/issue-prompt-builder.js +72 -9
  41. package/dist/server/services/plan/parser-core.js +6 -1
  42. package/dist/server/services/plan/quality-delta.js +6 -24
  43. package/dist/server/services/plan/readiness-planner.js +12 -4
  44. package/dist/server/services/plan/record-file.js +52 -0
  45. package/dist/server/services/plan/review-approval.js +64 -0
  46. package/dist/server/services/plan/review-gate.js +186 -77
  47. package/dist/server/services/plan/review-outcome.js +146 -0
  48. package/dist/server/services/plan/review-report.js +28 -1
  49. package/dist/server/services/plan/review-target.js +57 -0
  50. package/dist/server/services/plan/state-reconciler.js +3 -3
  51. package/dist/server/services/plan/template-diff.js +2 -0
  52. package/dist/server/services/plan/template-instantiator.js +4 -0
  53. package/dist/server/services/plan/watcher.js +11 -1
  54. package/dist/server/services/platform-reconnect.js +7 -0
  55. package/dist/server/services/platform-token-lifecycle.js +2 -1
  56. package/dist/server/services/platform.js +293 -50
  57. package/dist/server/services/relay-http-client.js +107 -0
  58. package/dist/server/services/schedule/agent-schedule-store.js +69 -8
  59. package/dist/server/services/schedule/agent-scheduler.js +48 -7
  60. package/dist/server/services/schedule/daily-timing.js +52 -0
  61. package/dist/server/services/schedule/prompt-file.js +55 -0
  62. package/dist/server/services/schedule/run-bootstrap.js +20 -5
  63. package/dist/server/services/schedule/schedule-store.js +54 -4
  64. package/dist/server/services/schedule/schedule-wire.js +17 -0
  65. package/dist/server/services/schedule/scheduler.js +43 -9
  66. package/dist/server/services/sdk/agent-schedule.js +294 -0
  67. package/dist/server/services/sdk/agents.js +95 -13
  68. package/dist/server/services/sdk/app-tools.js +44 -7
  69. package/dist/server/services/sdk/atomic-write.js +75 -0
  70. package/dist/server/services/sdk/browser.js +5 -1
  71. package/dist/server/services/sdk/composer.js +2 -0
  72. package/dist/server/services/sdk/file-change-notify.js +60 -13
  73. package/dist/server/services/sdk/files-app.js +12 -7
  74. package/dist/server/services/sdk/files-search.js +14 -0
  75. package/dist/server/services/sdk/files-transfer.js +1 -1
  76. package/dist/server/services/sdk/files-watch.js +721 -0
  77. package/dist/server/services/sdk/files-workspace.js +37 -11
  78. package/dist/server/services/sdk/inference-claude.js +4 -2
  79. package/dist/server/services/sdk/inference.js +16 -2
  80. package/dist/server/services/sdk/pm-chain.js +0 -0
  81. package/dist/server/services/sdk/pm-schedule.js +72 -15
  82. package/dist/server/services/sdk/pm.js +22 -0
  83. package/dist/server/services/sdk/rate-limits.js +32 -0
  84. package/dist/server/services/sdk/registry.js +40 -2
  85. package/dist/server/services/sdk/terminal.js +1 -1
  86. package/dist/server/services/sentry.js +5 -1
  87. package/dist/server/services/terminal/pty-manager.js +26 -11
  88. package/dist/server/services/terminal/pty-utils.js +12 -1
  89. package/dist/server/services/terminal/terminal-modes.js +87 -0
  90. package/dist/server/services/timeline/assembler.js +2 -2
  91. package/dist/server/services/timeline/index.js +1 -1
  92. package/dist/server/services/websocket/agent-schedule-handlers.js +62 -4
  93. package/dist/server/services/websocket/ask-user-question-bridge.js +2 -2
  94. package/dist/server/services/websocket/browser-agent-runtime.js +3 -1
  95. package/dist/server/services/websocket/browser-handlers.js +5 -2
  96. package/dist/server/services/websocket/browser-viewer-lifecycle.js +8 -0
  97. package/dist/server/services/websocket/file-explorer-handlers.js +17 -0
  98. package/dist/server/services/websocket/file-transfer-http.js +12 -6
  99. package/dist/server/services/websocket/handler.js +185 -33
  100. package/dist/server/services/websocket/msg-id-tracker.js +94 -19
  101. package/dist/server/services/websocket/plan-execution-handlers.js +33 -5
  102. package/dist/server/services/websocket/plan-handlers.js +10 -5
  103. package/dist/server/services/websocket/plan-helpers.js +23 -0
  104. package/dist/server/services/websocket/plan-issue-handlers.js +161 -4
  105. package/dist/server/services/websocket/plan-sprint-handlers.js +3 -2
  106. package/dist/server/services/websocket/schedule-handlers.js +8 -2
  107. package/dist/server/services/websocket/sdk-agent-host.js +5 -1
  108. package/dist/server/services/websocket/sdk-agent-schedule-host.js +30 -0
  109. package/dist/server/services/websocket/sdk-browser-host.js +11 -4
  110. package/dist/server/services/websocket/sdk-handlers.js +6 -1
  111. package/dist/server/services/websocket/sdk-pm-chain-host.js +20 -0
  112. package/dist/server/services/websocket/sdk-pm-execution-host.js +21 -14
  113. package/dist/server/services/websocket/sdk-pm-schedule-host.js +9 -0
  114. package/dist/server/services/websocket/sdk-terminal-host.js +7 -0
  115. package/dist/server/services/websocket/settings-handlers.js +6 -18
  116. package/dist/server/services/websocket/tab-broadcast.js +5 -1
  117. package/dist/server/services/websocket/terminal-handlers.js +51 -6
  118. package/dist/server/services/websocket/types.js +6 -5
  119. package/dist/server/services/websocket/viewer-interest.js +30 -0
  120. package/package.json +6 -6
@@ -66,6 +66,25 @@ export function describeMinions(instances = loadInstances()) {
66
66
  .sort((a, b) => a.port - b.port);
67
67
  }
68
68
 
69
+ export function pickLaunchedInstance(instances, { workingDirectory, environment, launchedAt }) {
70
+ return (
71
+ instances
72
+ .filter(
73
+ (instance) =>
74
+ Number.isInteger(instance?.pid) &&
75
+ instance.workingDirectory === workingDirectory &&
76
+ (instance.environment ?? 'production') === environment &&
77
+ typeof instance.startedAt === 'number' &&
78
+ instance.startedAt >= launchedAt
79
+ )
80
+ .sort((a, b) => b.startedAt - a.startedAt)[0] ?? null
81
+ );
82
+ }
83
+
84
+ export function findLaunchedMinion(criteria) {
85
+ return pickLaunchedInstance(loadInstances(), criteria);
86
+ }
87
+
69
88
  function printNoMinions() {
70
89
  log('\n No minion servers registered on this machine.\n', colors.bold);
71
90
  log(' Start one:', colors.bold);
package/bin/minion.js CHANGED
@@ -336,12 +336,32 @@ function resolveStartScript() {
336
336
  return 'dev';
337
337
  }
338
338
 
339
- function runNpmScriptDetached(script, args = [], envOverrides = {}) {
339
+ const REGISTER_WAIT_MS = 20000;
340
+ const REGISTER_POLL_MS = 150;
341
+
342
+ async function waitForLaunchedServerPid(child, criteria) {
343
+ const { findLaunchedMinion } = await import('./commands/minions.js');
344
+ const deadline = Date.now() + REGISTER_WAIT_MS;
345
+ let launchExited = false;
346
+ child.once('exit', () => {
347
+ launchExited = true;
348
+ });
349
+
350
+ for (;;) {
351
+ const instance = findLaunchedMinion(criteria);
352
+ if (instance) return instance.pid;
353
+ if (launchExited || Date.now() >= deadline) return null;
354
+ await new Promise((resolveTick) => setTimeout(resolveTick, REGISTER_POLL_MS));
355
+ }
356
+ }
357
+
358
+ async function runNpmScriptDetached(script, args = [], envOverrides = {}) {
340
359
  const logDir = machinePath('logs');
341
360
  if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });
342
361
  const logPath = join(logDir, `minionry-${new Date().toISOString().replace(/[:.]/g, '-')}.log`);
343
362
  const logFd = openSync(logPath, 'a');
344
363
 
364
+ const launchedAt = Date.now();
345
365
  const npmCmd = process.platform === 'win32' ? 'npm.cmd' : 'npm';
346
366
  const child = spawn(npmCmd, ['run', '--silent', script, ...args], {
347
367
  cwd: CLIENT_ROOT,
@@ -353,7 +373,6 @@ function runNpmScriptDetached(script, args = [], envOverrides = {}) {
353
373
 
354
374
  child.on('error', (err) => {
355
375
  log(`Error: ${err.message}`, colors.red);
356
- closeSync(logFd);
357
376
  process.exit(1);
358
377
  });
359
378
 
@@ -361,7 +380,17 @@ function runNpmScriptDetached(script, args = [], envOverrides = {}) {
361
380
  closeSync(logFd);
362
381
 
363
382
  log(`\n ${APP_NAME} started in background`, colors.bold + colors.green);
364
- log(` Stop: kill ${child.pid}`, colors.dim);
383
+ const serverPid = await waitForLaunchedServerPid(child, {
384
+ workingDirectory: USER_CWD,
385
+ environment: envOverrides.MINIONRY_ENV ?? 'production',
386
+ launchedAt,
387
+ });
388
+ log(
389
+ serverPid === null
390
+ ? ' Stop: minion list minions, then minion shutdown <pid>'
391
+ : ` Stop: minion shutdown ${serverPid}`,
392
+ colors.dim
393
+ );
365
394
  log(' Status: minion status', colors.dim);
366
395
  log(` Remote control: ${envOverrides.APP_ORIGIN || 'https://app.minionry.com'}\n`, colors.dim);
367
396
  process.exit(0);
@@ -657,7 +686,7 @@ async function startServer(envOverrides, { foreground = false } = {}) {
657
686
  if (foreground) {
658
687
  runNpmScript(script, [], envOverrides);
659
688
  } else {
660
- runNpmScriptDetached(script, [], envOverrides);
689
+ await runNpmScriptDetached(script, [], envOverrides);
661
690
  }
662
691
  }
663
692
 
@@ -5,8 +5,10 @@ import { randomUUID } from 'node:crypto';
5
5
  import { mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
6
6
  import { join } from 'node:path';
7
7
  import { machineFile } from '../../../bin/machine-layout.js';
8
+ import { TOOL_LESS_CLAUDE_ARGS } from '../../engines/claude/claude-command.js';
8
9
  import { resolveModelProfile } from '../../engines/credentials/model-profiles.js';
9
10
  import { ANTHROPIC_PROVIDER_ID, getApiKey, } from '../../engines/credentials/provider-credentials.js';
11
+ import { BOUNCER_HOOK_TOOLS, BOUNCER_PERMISSION_MODE, bouncerSpawnSettings, bouncerToolName, PERMISSION_PROMPT_TOOL, } from '../../mcp/permission-channels.js';
10
12
  import { isDirectory } from '../../services/pathUtils.js';
11
13
  import { profileAllowsBrowserControl } from '../../services/sdk/run-profile.js';
12
14
  import { getMaxConcurrentSessions, getSubagentModel, getSubagentModelForce, } from '../../services/settings.js';
@@ -84,7 +86,8 @@ export function handleSpawnError(error, config, reject) {
84
86
  }
85
87
  reject(new Error(formatted));
86
88
  }
87
- function buildSettingsOverride(config) {
89
+ const PERMISSION_REQUEST_HOOK_TIMEOUT_S = 31 * 60;
90
+ function buildSettingsOverride(config, bouncerActive) {
88
91
  const override = {};
89
92
  if (config.ultracode || config.effortLevel === 'ultracode') {
90
93
  override.ultracode = true;
@@ -92,6 +95,9 @@ function buildSettingsOverride(config) {
92
95
  if (config.fastMode) {
93
96
  override.fastMode = true;
94
97
  }
98
+ if (bouncerActive) {
99
+ Object.assign(override, bouncerSpawnSettings(PERMISSION_REQUEST_HOOK_TIMEOUT_S));
100
+ }
95
101
  return Object.keys(override).length > 0 ? JSON.stringify(override) : null;
96
102
  }
97
103
  export function isOpusTierModel(model) {
@@ -145,7 +151,7 @@ browserMcpActive = false) {
145
151
  if (effort) {
146
152
  args.push('--effort', effort);
147
153
  }
148
- const settingsOverride = config.inferenceOnly ? null : buildSettingsOverride(config);
154
+ const settingsOverride = config.inferenceOnly ? null : buildSettingsOverride(config, mcpConfigPath !== null);
149
155
  if (settingsOverride) {
150
156
  args.push('--settings', settingsOverride);
151
157
  }
@@ -156,7 +162,7 @@ browserMcpActive = false) {
156
162
  args.push('--input-format', 'stream-json');
157
163
  }
158
164
  if (config.inferenceOnly) {
159
- args.push('--tools', '', '--strict-mcp-config');
165
+ args.push(...TOOL_LESS_CLAUDE_ARGS);
160
166
  return args;
161
167
  }
162
168
  if (config.claudeSessionId && config.continueSession) {
@@ -165,19 +171,27 @@ browserMcpActive = false) {
165
171
  else if (config.continueSession) {
166
172
  args.push('--continue');
167
173
  }
168
- const disallowedTools = [
169
- ...new Set([...(config.disallowedTools ?? []), ...(config.appRunProfile?.disallowedTools ?? [])]),
170
- ];
174
+ const disallowedTools = resolveDisallowedTools(config, mcpConfigPath !== null);
171
175
  if (disallowedTools.length > 0) {
172
176
  args.push('--disallowedTools', disallowedTools.join(','));
173
177
  }
174
178
  pushPermissionAndPromptArgs(args, config, mcpConfigPath, browserMcpActive);
175
179
  return args;
176
180
  }
181
+ function resolveDisallowedTools(config, bouncerActive) {
182
+ return [
183
+ ...new Set([
184
+ ...(config.disallowedTools ?? []),
185
+ ...(config.appRunProfile?.disallowedTools ?? []),
186
+ ...(bouncerActive ? BOUNCER_HOOK_TOOLS : []),
187
+ ]),
188
+ ];
189
+ }
177
190
  function pushPermissionAndPromptArgs(args, config, mcpConfigPath, browserMcpActive) {
178
191
  if (mcpConfigPath) {
179
192
  args.push('--mcp-config', mcpConfigPath);
180
- args.push('--permission-prompt-tool', 'mcp__minionry-bouncer__approval_prompt');
193
+ args.push('--permission-prompt-tool', bouncerToolName(PERMISSION_PROMPT_TOOL));
194
+ args.push('--permission-mode', BOUNCER_PERMISSION_MODE);
181
195
  if (browserMcpActive) {
182
196
  args.push('--strict-mcp-config');
183
197
  }
@@ -216,6 +230,8 @@ function writeImageAttachmentsToStdin(claudeProcess, prompt, config) {
216
230
  const CLAUDE_SUBPROCESS_ENV_DEFAULTS = {
217
231
  CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS: '0',
218
232
  CLAUDE_CODE_ENABLE_TODO_TOOLS: '1',
233
+ CLAUDE_CODE_MAX_OUTPUT_TOKENS: '64000',
234
+ DISABLE_NONESSENTIAL_TRAFFIC: '1',
219
235
  };
220
236
  function claudeCredentialEnv(model) {
221
237
  const resolved = resolveModelProfile(model);
@@ -1,5 +1,6 @@
1
1
  // Copyright (c) 2025-present Minionry, Inc.
2
2
  // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
+ import { clampInput, clampText, DEFAULT_CHAT_TIMELINE_LIMITS } from '../../services/timeline/index.js';
3
4
  import { applyStreamEventToBackground } from './background-activity.js';
4
5
  import { engineAvailability } from './engine-availability.js';
5
6
  import { classifyEngineNotice, isSyntheticModel } from './engine-notice.js';
@@ -242,7 +243,12 @@ function handleToolResult(parsed, ctx) {
242
243
  toolEntry.duration = Date.now() - toolEntry.startTime;
243
244
  }
244
245
  if (ctx.config.toolUseCallback) {
245
- ctx.config.toolUseCallback({ type: 'tool_result', toolId, result: resultStr, isError });
246
+ ctx.config.toolUseCallback({
247
+ type: 'tool_result',
248
+ toolId,
249
+ result: clampText(resultStr, DEFAULT_CHAT_TIMELINE_LIMITS.maxToolResultChars),
250
+ isError,
251
+ });
246
252
  }
247
253
  }
248
254
  }
@@ -474,7 +480,6 @@ function handleToolInputDelta(event, ctx) {
474
480
  if (toolBuffer)
475
481
  toolBuffer.inputJson += partialJson;
476
482
  ctx.lastTokenActivityTime = Date.now();
477
- ctx.config.toolUseCallback?.({ type: 'tool_input_delta', partialJson, index });
478
483
  }
479
484
  function handleToolComplete(event, ctx) {
480
485
  if (event.type !== 'content_block_stop')
@@ -495,7 +500,7 @@ function handleToolComplete(event, ctx) {
495
500
  ctx.toolInputBuffers.delete(index);
496
501
  ctx.config.toolUseCallback?.({
497
502
  type: 'tool_complete', toolName: toolBuffer.name, toolId: toolBuffer.id,
498
- index, completeInput
503
+ index, completeInput: clampInput(completeInput, DEFAULT_CHAT_TIMELINE_LIMITS.maxToolInputChars)
499
504
  });
500
505
  }
501
506
  function handleToolStreamEvents(event, ctx) {
@@ -2,6 +2,7 @@
2
2
  // SPDX-License-Identifier: LicenseRef-Minionry-Software
3
3
  import { spawn } from 'node:child_process';
4
4
  import { homedir } from 'node:os';
5
+ import { TOOL_LESS_CLAUDE_ARGS } from '../../engines/claude/claude-command.js';
5
6
  import { isDirectory } from '../../services/pathUtils.js';
6
7
  import { loadSkillPrompt } from '../../services/plan/agent-loader.js';
7
8
  import { engineAvailability } from './engine-availability.js';
@@ -64,7 +65,7 @@ export function spawnHaikuRaw(prompt, claudeCommand, verbose, label, options = {
64
65
  };
65
66
  let proc;
66
67
  try {
67
- proc = spawn(claudeCommand, ['--print', '--model', 'haiku', prompt], { stdio: ['ignore', 'pipe', 'pipe'], cwd: options.cwd ?? resolveAssessorCwd() });
68
+ proc = spawn(claudeCommand, ['--print', '--model', 'haiku', ...TOOL_LESS_CLAUDE_ARGS, prompt], { stdio: ['ignore', 'pipe', 'pipe'], cwd: options.cwd ?? resolveAssessorCwd() });
68
69
  }
69
70
  catch (err) {
70
71
  settled = true;
@@ -2,6 +2,7 @@ import { existsSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync
2
2
  import { homedir } from 'node:os';
3
3
  import { join, resolve } from 'node:path';
4
4
  import { ensureMachineDir } from '../../../bin/machine-layout.js';
5
+ import { BOUNCER_MCP_SERVER_NAME } from '../../mcp/permission-channels.js';
5
6
  import { APP_TOOLS_MCP_SERVER_PATH, BROWSER_MCP_SERVER_PATH, IS_COMPILED, MCP_SERVER_PATH, MINIONRY_ROOT, QUALITY_MCP_SERVER_PATH, } from '../../utils/paths.js';
6
7
  import { herror, hlog } from './headless-logger.js';
7
8
  const APP_TOOLS_MCP_SERVER_NAME = 'minionry-app-tools';
@@ -286,7 +287,7 @@ export function generateMcpConfig(workingDir, verbose = false, options = {}) {
286
287
  const browserEntry = browserMcp ? buildBrowserMcpEntry(browserMcp) : null;
287
288
  const qualityEntry = qualityMcp ? buildQualityMcpEntry(qualityMcp) : null;
288
289
  const mcpServers = {
289
- 'minionry-bouncer': {
290
+ [BOUNCER_MCP_SERVER_NAME]: {
290
291
  command: launcher.command,
291
292
  args: [...launcher.argsPrefix, MCP_SERVER_PATH],
292
293
  description: 'Minionry security bouncer for approving/denying Claude Code tool use',
@@ -21,6 +21,7 @@ function isExecutableFile(path) {
21
21
  return false;
22
22
  }
23
23
  }
24
+ export const TOOL_LESS_CLAUDE_ARGS = ['--tools', '', '--strict-mcp-config'];
24
25
  export function resolveClaudeCommand({ env = process.env, home = homedir(), isExecutable = isExecutableFile, } = {}) {
25
26
  const override = env.CLAUDE_COMMAND?.trim();
26
27
  if (override)
@@ -8,6 +8,7 @@ import { ProviderCredentialStore } from './credentials/provider-credentials.js';
8
8
  import { HermesEngine } from './hermes/HermesEngine.js';
9
9
  import { createHermesAuth } from './hermes/hermes-auth.js';
10
10
  import { planHermesAppRunGatewayHome, planHermesGatewayHome, planHermesProfileGatewayHome, provisionHermesGatewayHome, } from './hermes/hermes-gateway-home.js';
11
+ import { hermesGatewayRegistry } from './hermes/hermes-gateway-registry.js';
11
12
  function provisionAppRunGatewayHome(model, appRun) {
12
13
  const owned = resolveModelProfile(model);
13
14
  const plan = owned?.profile.engine === 'hermes'
@@ -23,6 +24,7 @@ export function createEngine(engineId) {
23
24
  return new CodexEngine({ resolveCommand: () => resolveCodexCommand() });
24
25
  case 'hermes':
25
26
  return new HermesEngine({
27
+ gateways: hermesGatewayRegistry(),
26
28
  auth: createHermesAuth(),
27
29
  provisionGatewayHome: (model, browserMcp, appRun) => {
28
30
  if (appRun)