@poolzin/pool-bot 2026.2.23 → 2026.2.25
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/CHANGELOG.md +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -1,54 +1,66 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
|
2
|
-
import {
|
|
3
|
+
import { getActiveEmbeddedRunCount } from "../agents/pi-embedded-runner/runs.js";
|
|
3
4
|
import { registerSkillsChangeListener } from "../agents/skills/refresh.js";
|
|
5
|
+
import { initSubagentRegistry } from "../agents/subagent-registry.js";
|
|
6
|
+
import { getTotalPendingReplies } from "../auto-reply/reply/dispatcher-registry.js";
|
|
4
7
|
import { listChannelPlugins } from "../channels/plugins/index.js";
|
|
5
|
-
import { createDefaultDeps } from "../cli/deps.js";
|
|
6
8
|
import { formatCliCommand } from "../cli/command-format.js";
|
|
9
|
+
import { createDefaultDeps } from "../cli/deps.js";
|
|
10
|
+
import { isRestartEnabled } from "../config/commands.js";
|
|
7
11
|
import { CONFIG_PATH, isNixMode, loadConfig, migrateLegacyConfig, readConfigFileSnapshot, writeConfigFile, } from "../config/config.js";
|
|
8
|
-
import { isDiagnosticsEnabled } from "../infra/diagnostic-events.js";
|
|
9
|
-
import { logAcceptedEnvOption } from "../infra/env.js";
|
|
10
12
|
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
|
11
13
|
import { clearAgentRunContext, onAgentEvent } from "../infra/agent-events.js";
|
|
14
|
+
import { ensureControlUiAssetsBuilt, resolveControlUiRootOverrideSync, resolveControlUiRootSync, } from "../infra/control-ui-assets.js";
|
|
15
|
+
import { isDiagnosticsEnabled } from "../infra/diagnostic-events.js";
|
|
16
|
+
import { logAcceptedEnvOption } from "../infra/env.js";
|
|
17
|
+
import { createExecApprovalForwarder } from "../infra/exec-approval-forwarder.js";
|
|
12
18
|
import { onHeartbeatEvent } from "../infra/heartbeat-events.js";
|
|
13
19
|
import { startHeartbeatRunner } from "../infra/heartbeat-runner.js";
|
|
14
20
|
import { getMachineDisplayName } from "../infra/machine-name.js";
|
|
15
21
|
import { ensurePoolbotCliOnPath } from "../infra/path-env.js";
|
|
22
|
+
import { setGatewaySigusr1RestartPolicy, setPreRestartDeferralCheck } from "../infra/restart.js";
|
|
16
23
|
import { primeRemoteSkillsCache, refreshRemoteBinsForConnectedNodes, setSkillsRemoteRegistry, } from "../infra/skills-remote.js";
|
|
17
24
|
import { scheduleGatewayUpdateCheck } from "../infra/update-startup.js";
|
|
18
|
-
import { setGatewaySigusr1RestartPolicy } from "../infra/restart.js";
|
|
19
25
|
import { startDiagnosticHeartbeat, stopDiagnosticHeartbeat } from "../logging/diagnostic.js";
|
|
20
26
|
import { createSubsystemLogger, runtimeForLogger } from "../logging/subsystem.js";
|
|
27
|
+
import { getGlobalHookRunner, runGlobalGatewayStopSafely } from "../plugins/hook-runner-global.js";
|
|
28
|
+
import { createEmptyPluginRegistry } from "../plugins/registry.js";
|
|
29
|
+
import { getTotalQueueSize } from "../process/command-queue.js";
|
|
21
30
|
import { runOnboardingWizard } from "../wizard/onboarding.js";
|
|
31
|
+
import { createAuthRateLimiter } from "./auth-rate-limit.js";
|
|
32
|
+
import { startChannelHealthMonitor } from "./channel-health-monitor.js";
|
|
22
33
|
import { startGatewayConfigReloader } from "./config-reload.js";
|
|
23
|
-
import {
|
|
24
|
-
import { startGatewayDiscovery } from "./server-discovery-runtime.js";
|
|
34
|
+
import { GATEWAY_EVENT_UPDATE_AVAILABLE, } from "./events.js";
|
|
25
35
|
import { ExecApprovalManager } from "./exec-approval-manager.js";
|
|
26
|
-
import {
|
|
27
|
-
import { createExecApprovalForwarder } from "../infra/exec-approval-forwarder.js";
|
|
36
|
+
import { NodeRegistry } from "./node-registry.js";
|
|
28
37
|
import { createChannelManager } from "./server-channels.js";
|
|
29
38
|
import { createAgentEventHandler } from "./server-chat.js";
|
|
30
39
|
import { createGatewayCloseHandler } from "./server-close.js";
|
|
31
40
|
import { buildGatewayCronService } from "./server-cron.js";
|
|
41
|
+
import { startGatewayDiscovery } from "./server-discovery-runtime.js";
|
|
32
42
|
import { applyGatewayLaneConcurrency } from "./server-lanes.js";
|
|
33
43
|
import { startGatewayMaintenanceTimers } from "./server-maintenance.js";
|
|
34
|
-
import { coreGatewayHandlers } from "./server-methods.js";
|
|
35
44
|
import { GATEWAY_EVENTS, listGatewayMethods } from "./server-methods-list.js";
|
|
45
|
+
import { coreGatewayHandlers } from "./server-methods.js";
|
|
46
|
+
import { createExecApprovalHandlers } from "./server-methods/exec-approval.js";
|
|
47
|
+
import { safeParseJson } from "./server-methods/nodes.helpers.js";
|
|
48
|
+
import { hasConnectedMobileNode } from "./server-mobile-nodes.js";
|
|
36
49
|
import { loadGatewayModelCatalog } from "./server-model-catalog.js";
|
|
37
|
-
import { NodeRegistry } from "./node-registry.js";
|
|
38
50
|
import { createNodeSubscriptionManager } from "./server-node-subscriptions.js";
|
|
39
|
-
import { safeParseJson } from "./server-methods/nodes.helpers.js";
|
|
40
51
|
import { loadGatewayPlugins } from "./server-plugins.js";
|
|
41
52
|
import { createGatewayReloadHandlers } from "./server-reload-handlers.js";
|
|
42
53
|
import { resolveGatewayRuntimeConfig } from "./server-runtime-config.js";
|
|
43
54
|
import { createGatewayRuntimeState } from "./server-runtime-state.js";
|
|
44
|
-
import { hasConnectedMobileNode } from "./server-mobile-nodes.js";
|
|
45
55
|
import { resolveSessionKeyForRun } from "./server-session-key.js";
|
|
46
|
-
import { startGatewaySidecars } from "./server-startup.js";
|
|
47
56
|
import { logGatewayStartup } from "./server-startup-log.js";
|
|
57
|
+
import { startGatewaySidecars } from "./server-startup.js";
|
|
48
58
|
import { startGatewayTailscaleExposure } from "./server-tailscale.js";
|
|
49
|
-
import { loadGatewayTlsRuntime } from "./server/tls.js";
|
|
50
59
|
import { createWizardSessionTracker } from "./server-wizard-sessions.js";
|
|
51
60
|
import { attachGatewayWsHandlers } from "./server-ws-runtime.js";
|
|
61
|
+
import { getHealthCache, getHealthVersion, getPresenceVersion, incrementPresenceVersion, refreshGatewayHealthSnapshot, } from "./server/health-state.js";
|
|
62
|
+
import { loadGatewayTlsRuntime } from "./server/tls.js";
|
|
63
|
+
import { ensureGatewayStartupAuth } from "./startup-auth.js";
|
|
52
64
|
export { __resetModelCatalogCacheForTest } from "./server-model-catalog.js";
|
|
53
65
|
ensurePoolbotCliOnPath();
|
|
54
66
|
const log = createSubsystemLogger("gateway");
|
|
@@ -63,8 +75,10 @@ const logReload = log.child("reload");
|
|
|
63
75
|
const logHooks = log.child("hooks");
|
|
64
76
|
const logPlugins = log.child("plugins");
|
|
65
77
|
const logWsControl = log.child("ws");
|
|
78
|
+
const gatewayRuntime = runtimeForLogger(log);
|
|
66
79
|
const canvasRuntime = runtimeForLogger(logCanvas);
|
|
67
80
|
export async function startGatewayServer(port = 18789, opts = {}) {
|
|
81
|
+
const minimalTestGateway = process.env.VITEST === "1" && process.env.POOLBOT_TEST_MINIMAL_GATEWAY === "1";
|
|
68
82
|
// Ensure all default port derivations (browser/canvas) see the actual runtime port.
|
|
69
83
|
process.env.POOLBOT_GATEWAY_PORT = String(port);
|
|
70
84
|
process.env.CLAWDBOT_GATEWAY_PORT = String(port);
|
|
@@ -113,23 +127,43 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
113
127
|
log.warn(`gateway: failed to persist plugin auto-enable changes: ${String(err)}`);
|
|
114
128
|
}
|
|
115
129
|
}
|
|
116
|
-
|
|
130
|
+
let cfgAtStart = loadConfig();
|
|
131
|
+
const authBootstrap = await ensureGatewayStartupAuth({
|
|
132
|
+
cfg: cfgAtStart,
|
|
133
|
+
env: process.env,
|
|
134
|
+
authOverride: opts.auth,
|
|
135
|
+
tailscaleOverride: opts.tailscale,
|
|
136
|
+
persist: true,
|
|
137
|
+
});
|
|
138
|
+
cfgAtStart = authBootstrap.cfg;
|
|
139
|
+
if (authBootstrap.generatedToken) {
|
|
140
|
+
if (authBootstrap.persistedGeneratedToken) {
|
|
141
|
+
log.info("Gateway auth token was missing. Generated a new token and saved it to config (gateway.auth.token).");
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
log.warn("Gateway auth token was missing. Generated a runtime token for this startup without changing config; restart will generate a different token. Persist one with `poolbot config set gateway.auth.mode token` and `poolbot config set gateway.auth.token <token>`.");
|
|
145
|
+
}
|
|
146
|
+
}
|
|
117
147
|
const diagnosticsEnabled = isDiagnosticsEnabled(cfgAtStart);
|
|
118
148
|
if (diagnosticsEnabled) {
|
|
119
149
|
startDiagnosticHeartbeat();
|
|
120
150
|
}
|
|
121
|
-
setGatewaySigusr1RestartPolicy({ allowExternal: cfgAtStart
|
|
151
|
+
setGatewaySigusr1RestartPolicy({ allowExternal: isRestartEnabled(cfgAtStart) });
|
|
152
|
+
setPreRestartDeferralCheck(() => getTotalQueueSize() + getTotalPendingReplies() + getActiveEmbeddedRunCount());
|
|
122
153
|
initSubagentRegistry();
|
|
123
154
|
const defaultAgentId = resolveDefaultAgentId(cfgAtStart);
|
|
124
155
|
const defaultWorkspaceDir = resolveAgentWorkspaceDir(cfgAtStart, defaultAgentId);
|
|
125
156
|
const baseMethods = listGatewayMethods();
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
157
|
+
const emptyPluginRegistry = createEmptyPluginRegistry();
|
|
158
|
+
const { pluginRegistry, gatewayMethods: baseGatewayMethods } = minimalTestGateway
|
|
159
|
+
? { pluginRegistry: emptyPluginRegistry, gatewayMethods: baseMethods }
|
|
160
|
+
: loadGatewayPlugins({
|
|
161
|
+
cfg: cfgAtStart,
|
|
162
|
+
workspaceDir: defaultWorkspaceDir,
|
|
163
|
+
log,
|
|
164
|
+
coreGatewayHandlers,
|
|
165
|
+
baseMethods,
|
|
166
|
+
});
|
|
133
167
|
const channelLogs = Object.fromEntries(listChannelPlugins().map((plugin) => [plugin.id, logChannels.child(plugin.id)]));
|
|
134
168
|
const channelRuntimeEnvs = Object.fromEntries(Object.entries(channelLogs).map(([id, logger]) => [id, runtimeForLogger(logger)]));
|
|
135
169
|
const channelMethods = listChannelPlugins().flatMap((plugin) => plugin.gatewayMethods ?? []);
|
|
@@ -146,9 +180,46 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
146
180
|
auth: opts.auth,
|
|
147
181
|
tailscale: opts.tailscale,
|
|
148
182
|
});
|
|
149
|
-
const { bindHost, controlUiEnabled, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, controlUiBasePath, resolvedAuth, tailscaleConfig, tailscaleMode, } = runtimeConfig;
|
|
183
|
+
const { bindHost, controlUiEnabled, openAiChatCompletionsEnabled, openResponsesEnabled, openResponsesConfig, controlUiBasePath, controlUiRoot: controlUiRootOverride, resolvedAuth, tailscaleConfig, tailscaleMode, } = runtimeConfig;
|
|
150
184
|
let hooksConfig = runtimeConfig.hooksConfig;
|
|
151
185
|
const canvasHostEnabled = runtimeConfig.canvasHostEnabled;
|
|
186
|
+
// Create auth rate limiter only when explicitly configured.
|
|
187
|
+
const rateLimitConfig = cfgAtStart.gateway?.auth?.rateLimit;
|
|
188
|
+
const authRateLimiter = rateLimitConfig
|
|
189
|
+
? createAuthRateLimiter(rateLimitConfig)
|
|
190
|
+
: undefined;
|
|
191
|
+
let controlUiRootState;
|
|
192
|
+
if (controlUiRootOverride) {
|
|
193
|
+
const resolvedOverride = resolveControlUiRootOverrideSync(controlUiRootOverride);
|
|
194
|
+
const resolvedOverridePath = path.resolve(controlUiRootOverride);
|
|
195
|
+
controlUiRootState = resolvedOverride
|
|
196
|
+
? { kind: "resolved", path: resolvedOverride }
|
|
197
|
+
: { kind: "invalid", path: resolvedOverridePath };
|
|
198
|
+
if (!resolvedOverride) {
|
|
199
|
+
log.warn(`gateway: controlUi.root not found at ${resolvedOverridePath}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else if (controlUiEnabled) {
|
|
203
|
+
let resolvedRoot = resolveControlUiRootSync({
|
|
204
|
+
moduleUrl: import.meta.url,
|
|
205
|
+
argv1: process.argv[1],
|
|
206
|
+
cwd: process.cwd(),
|
|
207
|
+
});
|
|
208
|
+
if (!resolvedRoot) {
|
|
209
|
+
const ensureResult = await ensureControlUiAssetsBuilt(gatewayRuntime);
|
|
210
|
+
if (!ensureResult.ok && ensureResult.message) {
|
|
211
|
+
log.warn(`gateway: ${ensureResult.message}`);
|
|
212
|
+
}
|
|
213
|
+
resolvedRoot = resolveControlUiRootSync({
|
|
214
|
+
moduleUrl: import.meta.url,
|
|
215
|
+
argv1: process.argv[1],
|
|
216
|
+
cwd: process.cwd(),
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
controlUiRootState = resolvedRoot
|
|
220
|
+
? { kind: "resolved", path: resolvedRoot }
|
|
221
|
+
: { kind: "missing" };
|
|
222
|
+
}
|
|
152
223
|
const wizardRunner = opts.wizardRunner ?? runOnboardingWizard;
|
|
153
224
|
const { wizardSessions, findRunningWizard, purgeWizardSession } = createWizardSessionTracker();
|
|
154
225
|
const deps = createDefaultDeps();
|
|
@@ -157,16 +228,18 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
157
228
|
if (cfgAtStart.gateway?.tls?.enabled && !gatewayTls.enabled) {
|
|
158
229
|
throw new Error(gatewayTls.error ?? "gateway tls: failed to enable");
|
|
159
230
|
}
|
|
160
|
-
const { canvasHost, httpServer, httpServers, httpBindHosts, wss, clients, broadcast, agentRunSeq, dedupe, chatRunState, chatRunBuffers, chatDeltaSentAt, addChatRun, removeChatRun, chatAbortControllers,
|
|
231
|
+
const { canvasHost, httpServer, httpServers, httpBindHosts, wss, clients, broadcast, broadcastToConnIds, agentRunSeq, dedupe, chatRunState, chatRunBuffers, chatDeltaSentAt, addChatRun, removeChatRun, chatAbortControllers, toolEventRecipients, } = await createGatewayRuntimeState({
|
|
161
232
|
cfg: cfgAtStart,
|
|
162
233
|
bindHost,
|
|
163
234
|
port,
|
|
164
235
|
controlUiEnabled,
|
|
165
236
|
controlUiBasePath,
|
|
237
|
+
controlUiRoot: controlUiRootState,
|
|
166
238
|
openAiChatCompletionsEnabled,
|
|
167
239
|
openResponsesEnabled,
|
|
168
240
|
openResponsesConfig,
|
|
169
241
|
resolvedAuth,
|
|
242
|
+
rateLimiter: authRateLimiter,
|
|
170
243
|
gatewayTls,
|
|
171
244
|
hooksConfig: () => hooksConfig,
|
|
172
245
|
pluginRegistry,
|
|
@@ -209,68 +282,115 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
209
282
|
channelRuntimeEnvs,
|
|
210
283
|
});
|
|
211
284
|
const { getRuntimeSnapshot, startChannels, startChannel, stopChannel, markChannelLoggedOut } = channelManager;
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
285
|
+
if (!minimalTestGateway) {
|
|
286
|
+
const machineDisplayName = await getMachineDisplayName();
|
|
287
|
+
const discovery = await startGatewayDiscovery({
|
|
288
|
+
machineDisplayName,
|
|
289
|
+
port,
|
|
290
|
+
gatewayTls: gatewayTls.enabled
|
|
291
|
+
? { enabled: true, fingerprintSha256: gatewayTls.fingerprintSha256 }
|
|
292
|
+
: undefined,
|
|
293
|
+
wideAreaDiscoveryEnabled: cfgAtStart.discovery?.wideArea?.enabled === true,
|
|
294
|
+
wideAreaDiscoveryDomain: cfgAtStart.discovery?.wideArea?.domain,
|
|
295
|
+
tailscaleMode,
|
|
296
|
+
mdnsMode: cfgAtStart.discovery?.mdns?.mode,
|
|
297
|
+
logDiscovery,
|
|
298
|
+
});
|
|
299
|
+
bonjourStop = discovery.bonjourStop;
|
|
300
|
+
}
|
|
301
|
+
if (!minimalTestGateway) {
|
|
302
|
+
setSkillsRemoteRegistry(nodeRegistry);
|
|
303
|
+
void primeRemoteSkillsCache();
|
|
304
|
+
}
|
|
227
305
|
// Debounce skills-triggered node probes to avoid feedback loops and rapid-fire invokes.
|
|
228
306
|
// Skills changes can happen in bursts (e.g., file watcher events), and each probe
|
|
229
307
|
// takes time to complete. A 30-second delay ensures we batch changes together.
|
|
230
308
|
let skillsRefreshTimer = null;
|
|
231
309
|
const skillsRefreshDelayMs = 30_000;
|
|
232
|
-
const skillsChangeUnsub =
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
skillsRefreshTimer
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
}
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
310
|
+
const skillsChangeUnsub = minimalTestGateway
|
|
311
|
+
? () => { }
|
|
312
|
+
: registerSkillsChangeListener((event) => {
|
|
313
|
+
if (event.reason === "remote-node") {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
if (skillsRefreshTimer) {
|
|
317
|
+
clearTimeout(skillsRefreshTimer);
|
|
318
|
+
}
|
|
319
|
+
skillsRefreshTimer = setTimeout(() => {
|
|
320
|
+
skillsRefreshTimer = null;
|
|
321
|
+
const latest = loadConfig();
|
|
322
|
+
void refreshRemoteBinsForConnectedNodes(latest);
|
|
323
|
+
}, skillsRefreshDelayMs);
|
|
324
|
+
});
|
|
325
|
+
const noopInterval = () => setInterval(() => { }, 1 << 30);
|
|
326
|
+
let tickInterval = noopInterval();
|
|
327
|
+
let healthInterval = noopInterval();
|
|
328
|
+
let dedupeCleanup = noopInterval();
|
|
329
|
+
if (!minimalTestGateway) {
|
|
330
|
+
({ tickInterval, healthInterval, dedupeCleanup } = startGatewayMaintenanceTimers({
|
|
331
|
+
broadcast,
|
|
332
|
+
nodeSendToAllSubscribed,
|
|
333
|
+
getPresenceVersion,
|
|
334
|
+
getHealthVersion,
|
|
335
|
+
refreshGatewayHealthSnapshot,
|
|
336
|
+
logHealth,
|
|
337
|
+
dedupe,
|
|
338
|
+
chatAbortControllers,
|
|
339
|
+
chatRunState,
|
|
340
|
+
chatRunBuffers,
|
|
341
|
+
chatDeltaSentAt,
|
|
342
|
+
removeChatRun,
|
|
343
|
+
agentRunSeq,
|
|
344
|
+
nodeSendToSession,
|
|
345
|
+
}));
|
|
346
|
+
}
|
|
347
|
+
const agentUnsub = minimalTestGateway
|
|
348
|
+
? null
|
|
349
|
+
: onAgentEvent(createAgentEventHandler({
|
|
350
|
+
broadcast,
|
|
351
|
+
broadcastToConnIds,
|
|
352
|
+
nodeSendToSession,
|
|
353
|
+
agentRunSeq,
|
|
354
|
+
chatRunState,
|
|
355
|
+
resolveSessionKeyForRun,
|
|
356
|
+
clearAgentRunContext,
|
|
357
|
+
toolEventRecipients,
|
|
358
|
+
}));
|
|
359
|
+
const heartbeatUnsub = minimalTestGateway
|
|
360
|
+
? null
|
|
361
|
+
: onHeartbeatEvent((evt) => {
|
|
362
|
+
broadcast("heartbeat", evt, { dropIfSlow: true });
|
|
363
|
+
});
|
|
364
|
+
let heartbeatRunner = minimalTestGateway
|
|
365
|
+
? {
|
|
366
|
+
stop: () => { },
|
|
367
|
+
updateConfig: () => { },
|
|
368
|
+
}
|
|
369
|
+
: startHeartbeatRunner({ cfg: cfgAtStart });
|
|
370
|
+
const healthCheckMinutes = cfgAtStart.gateway?.channelHealthCheckMinutes;
|
|
371
|
+
const healthCheckDisabled = healthCheckMinutes === 0;
|
|
372
|
+
const channelHealthMonitor = healthCheckDisabled
|
|
373
|
+
? null
|
|
374
|
+
: startChannelHealthMonitor({
|
|
375
|
+
channelManager,
|
|
376
|
+
checkIntervalMs: (healthCheckMinutes ?? 5) * 60_000,
|
|
377
|
+
});
|
|
378
|
+
if (!minimalTestGateway) {
|
|
379
|
+
void cron.start().catch((err) => logCron.error(`failed to start: ${String(err)}`));
|
|
380
|
+
}
|
|
381
|
+
// Recover pending outbound deliveries from previous crash/restart.
|
|
382
|
+
if (!minimalTestGateway) {
|
|
383
|
+
void (async () => {
|
|
384
|
+
const { recoverPendingDeliveries } = await import("../infra/outbound/delivery-queue.js");
|
|
385
|
+
const { deliverOutboundPayloads } = await import("../infra/outbound/deliver.js");
|
|
386
|
+
const logRecovery = log.child("delivery-recovery");
|
|
387
|
+
await recoverPendingDeliveries({
|
|
388
|
+
deliver: deliverOutboundPayloads,
|
|
389
|
+
log: logRecovery,
|
|
390
|
+
cfg: cfgAtStart,
|
|
391
|
+
});
|
|
392
|
+
})().catch((err) => log.error(`Delivery recovery failed: ${String(err)}`));
|
|
393
|
+
}
|
|
274
394
|
const execApprovalManager = new ExecApprovalManager();
|
|
275
395
|
const execApprovalForwarder = createExecApprovalForwarder();
|
|
276
396
|
const execApprovalHandlers = createExecApprovalHandlers(execApprovalManager, {
|
|
@@ -285,6 +405,7 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
285
405
|
canvasHostEnabled: Boolean(canvasHost),
|
|
286
406
|
canvasHostServerPort,
|
|
287
407
|
resolvedAuth,
|
|
408
|
+
rateLimiter: authRateLimiter,
|
|
288
409
|
gatewayMethods,
|
|
289
410
|
events: GATEWAY_EVENTS,
|
|
290
411
|
logGateway: log,
|
|
@@ -299,6 +420,7 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
299
420
|
deps,
|
|
300
421
|
cron,
|
|
301
422
|
cronStorePath,
|
|
423
|
+
execApprovalManager,
|
|
302
424
|
loadGatewayModelCatalog,
|
|
303
425
|
getHealthCache,
|
|
304
426
|
refreshHealthSnapshot: refreshGatewayHealthSnapshot,
|
|
@@ -344,63 +466,90 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
344
466
|
log,
|
|
345
467
|
isNixMode,
|
|
346
468
|
});
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
469
|
+
if (!minimalTestGateway) {
|
|
470
|
+
scheduleGatewayUpdateCheck({
|
|
471
|
+
cfg: cfgAtStart,
|
|
472
|
+
log,
|
|
473
|
+
isNixMode,
|
|
474
|
+
onUpdateAvailableChange: (updateAvailable) => {
|
|
475
|
+
const payload = { updateAvailable };
|
|
476
|
+
broadcast(GATEWAY_EVENT_UPDATE_AVAILABLE, payload, { dropIfSlow: true });
|
|
477
|
+
},
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
const tailscaleCleanup = minimalTestGateway
|
|
481
|
+
? null
|
|
482
|
+
: await startGatewayTailscaleExposure({
|
|
483
|
+
tailscaleMode,
|
|
484
|
+
resetOnExit: tailscaleConfig.resetOnExit,
|
|
485
|
+
port,
|
|
486
|
+
controlUiBasePath,
|
|
487
|
+
logTailscale,
|
|
488
|
+
});
|
|
355
489
|
let browserControl = null;
|
|
356
|
-
(
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
490
|
+
if (!minimalTestGateway) {
|
|
491
|
+
({ browserControl, pluginServices } = await startGatewaySidecars({
|
|
492
|
+
cfg: cfgAtStart,
|
|
493
|
+
pluginRegistry,
|
|
494
|
+
defaultWorkspaceDir,
|
|
495
|
+
deps,
|
|
496
|
+
startChannels,
|
|
497
|
+
log,
|
|
498
|
+
logHooks,
|
|
499
|
+
logChannels,
|
|
500
|
+
logBrowser,
|
|
501
|
+
}));
|
|
502
|
+
}
|
|
503
|
+
// Run gateway_start plugin hook (fire-and-forget)
|
|
504
|
+
if (!minimalTestGateway) {
|
|
505
|
+
const hookRunner = getGlobalHookRunner();
|
|
506
|
+
if (hookRunner?.hasHooks("gateway_start")) {
|
|
507
|
+
void hookRunner.runGatewayStart({ port }, { port }).catch((err) => {
|
|
508
|
+
log.warn(`gateway_start hook failed: ${String(err)}`);
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
const configReloader = minimalTestGateway
|
|
513
|
+
? { stop: async () => { } }
|
|
514
|
+
: (() => {
|
|
515
|
+
const { applyHotReload, requestGatewayRestart } = createGatewayReloadHandlers({
|
|
516
|
+
deps,
|
|
517
|
+
broadcast,
|
|
518
|
+
getState: () => ({
|
|
519
|
+
hooksConfig,
|
|
520
|
+
heartbeatRunner,
|
|
521
|
+
cronState,
|
|
522
|
+
browserControl,
|
|
523
|
+
}),
|
|
524
|
+
setState: (nextState) => {
|
|
525
|
+
hooksConfig = nextState.hooksConfig;
|
|
526
|
+
heartbeatRunner = nextState.heartbeatRunner;
|
|
527
|
+
cronState = nextState.cronState;
|
|
528
|
+
cron = cronState.cron;
|
|
529
|
+
cronStorePath = cronState.storePath;
|
|
530
|
+
browserControl = nextState.browserControl;
|
|
531
|
+
},
|
|
532
|
+
startChannel,
|
|
533
|
+
stopChannel,
|
|
534
|
+
logHooks,
|
|
535
|
+
logBrowser,
|
|
536
|
+
logChannels,
|
|
537
|
+
logCron,
|
|
538
|
+
logReload,
|
|
539
|
+
});
|
|
540
|
+
return startGatewayConfigReloader({
|
|
541
|
+
initialConfig: cfgAtStart,
|
|
542
|
+
readSnapshot: readConfigFileSnapshot,
|
|
543
|
+
onHotReload: applyHotReload,
|
|
544
|
+
onRestart: requestGatewayRestart,
|
|
545
|
+
log: {
|
|
546
|
+
info: (msg) => logReload.info(msg),
|
|
547
|
+
warn: (msg) => logReload.warn(msg),
|
|
548
|
+
error: (msg) => logReload.error(msg),
|
|
549
|
+
},
|
|
550
|
+
watchPath: CONFIG_PATH,
|
|
551
|
+
});
|
|
552
|
+
})();
|
|
404
553
|
const close = createGatewayCloseHandler({
|
|
405
554
|
bonjourStop,
|
|
406
555
|
tailscaleCleanup,
|
|
@@ -427,6 +576,12 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
427
576
|
});
|
|
428
577
|
return {
|
|
429
578
|
close: async (opts) => {
|
|
579
|
+
// Run gateway_stop plugin hook before shutdown
|
|
580
|
+
await runGlobalGatewayStopSafely({
|
|
581
|
+
event: { reason: opts?.reason ?? "gateway stopping" },
|
|
582
|
+
ctx: { port },
|
|
583
|
+
onError: (err) => log.warn(`gateway_stop hook failed: ${String(err)}`),
|
|
584
|
+
});
|
|
430
585
|
if (diagnosticsEnabled) {
|
|
431
586
|
stopDiagnosticHeartbeat();
|
|
432
587
|
}
|
|
@@ -435,6 +590,8 @@ export async function startGatewayServer(port = 18789, opts = {}) {
|
|
|
435
590
|
skillsRefreshTimer = null;
|
|
436
591
|
}
|
|
437
592
|
skillsChangeUnsub();
|
|
593
|
+
authRateLimiter?.dispose();
|
|
594
|
+
channelHealthMonitor?.stop();
|
|
438
595
|
await close(opts);
|
|
439
596
|
},
|
|
440
597
|
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export function createToolSummaryPreviewTranscriptLines(sessionId) {
|
|
2
|
+
return [
|
|
3
|
+
JSON.stringify({ type: "session", version: 1, id: sessionId }),
|
|
4
|
+
JSON.stringify({ message: { role: "user", content: "Hello" } }),
|
|
5
|
+
JSON.stringify({ message: { role: "assistant", content: "Hi" } }),
|
|
6
|
+
JSON.stringify({
|
|
7
|
+
message: { role: "assistant", content: [{ type: "toolcall", name: "weather" }] },
|
|
8
|
+
}),
|
|
9
|
+
JSON.stringify({ message: { role: "assistant", content: "Forecast ready" } }),
|
|
10
|
+
];
|
|
11
|
+
}
|