@hybridaione/hybridclaw 0.2.3 → 0.2.7
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/.github/workflows/ci.yml +70 -0
- package/.husky/pre-commit +1 -0
- package/CHANGELOG.md +95 -0
- package/CONTRIBUTING.md +33 -0
- package/README.md +57 -19
- package/SECURITY.md +17 -0
- package/biome.json +35 -0
- package/config.example.json +72 -7
- package/container/package-lock.json +2 -2
- package/container/package.json +1 -1
- package/container/src/approval-policy.ts +1405 -0
- package/container/src/browser-tools.ts +672 -136
- package/container/src/extensions.ts +36 -12
- package/container/src/hybridai-client.ts +65 -16
- package/container/src/index.ts +463 -110
- package/container/src/ipc.ts +5 -3
- package/container/src/token-usage.ts +107 -18
- package/container/src/tools.ts +600 -225
- package/container/src/types.ts +49 -2
- package/container/src/web-fetch.ts +89 -32
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +15 -4
- package/dist/agent.js.map +1 -1
- package/dist/audit-cli.d.ts.map +1 -1
- package/dist/audit-cli.js +4 -2
- package/dist/audit-cli.js.map +1 -1
- package/dist/audit-events.d.ts.map +1 -1
- package/dist/audit-events.js +53 -3
- package/dist/audit-events.js.map +1 -1
- package/dist/audit-trail.d.ts.map +1 -1
- package/dist/audit-trail.js +17 -8
- package/dist/audit-trail.js.map +1 -1
- package/dist/channels/discord/attachments.d.ts.map +1 -1
- package/dist/channels/discord/attachments.js +14 -7
- package/dist/channels/discord/attachments.js.map +1 -1
- package/dist/channels/discord/debounce.d.ts +9 -0
- package/dist/channels/discord/debounce.d.ts.map +1 -0
- package/dist/channels/discord/debounce.js +20 -0
- package/dist/channels/discord/debounce.js.map +1 -0
- package/dist/channels/discord/delivery.d.ts +4 -1
- package/dist/channels/discord/delivery.d.ts.map +1 -1
- package/dist/channels/discord/delivery.js +24 -4
- package/dist/channels/discord/delivery.js.map +1 -1
- package/dist/channels/discord/human-delay.d.ts +16 -0
- package/dist/channels/discord/human-delay.d.ts.map +1 -0
- package/dist/channels/discord/human-delay.js +29 -0
- package/dist/channels/discord/human-delay.js.map +1 -0
- package/dist/channels/discord/inbound.d.ts +31 -0
- package/dist/channels/discord/inbound.d.ts.map +1 -1
- package/dist/channels/discord/inbound.js +156 -6
- package/dist/channels/discord/inbound.js.map +1 -1
- package/dist/channels/discord/mentions.d.ts.map +1 -1
- package/dist/channels/discord/mentions.js +16 -4
- package/dist/channels/discord/mentions.js.map +1 -1
- package/dist/channels/discord/presence.d.ts +33 -0
- package/dist/channels/discord/presence.d.ts.map +1 -0
- package/dist/channels/discord/presence.js +111 -0
- package/dist/channels/discord/presence.js.map +1 -0
- package/dist/channels/discord/rate-limiter.d.ts +14 -0
- package/dist/channels/discord/rate-limiter.d.ts.map +1 -0
- package/dist/channels/discord/rate-limiter.js +49 -0
- package/dist/channels/discord/rate-limiter.js.map +1 -0
- package/dist/channels/discord/reactions.d.ts +38 -0
- package/dist/channels/discord/reactions.d.ts.map +1 -0
- package/dist/channels/discord/reactions.js +151 -0
- package/dist/channels/discord/reactions.js.map +1 -0
- package/dist/channels/discord/runtime.d.ts +7 -4
- package/dist/channels/discord/runtime.d.ts.map +1 -1
- package/dist/channels/discord/runtime.js +755 -150
- package/dist/channels/discord/runtime.js.map +1 -1
- package/dist/channels/discord/stream.d.ts +4 -1
- package/dist/channels/discord/stream.d.ts.map +1 -1
- package/dist/channels/discord/stream.js +19 -12
- package/dist/channels/discord/stream.js.map +1 -1
- package/dist/channels/discord/tool-actions.d.ts.map +1 -1
- package/dist/channels/discord/tool-actions.js +24 -12
- package/dist/channels/discord/tool-actions.js.map +1 -1
- package/dist/channels/discord/typing.d.ts +15 -0
- package/dist/channels/discord/typing.d.ts.map +1 -0
- package/dist/channels/discord/typing.js +106 -0
- package/dist/channels/discord/typing.js.map +1 -0
- package/dist/chunk.d.ts.map +1 -1
- package/dist/chunk.js +4 -2
- package/dist/chunk.js.map +1 -1
- package/dist/cli.js +77 -23
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +113 -18
- package/dist/config.js.map +1 -1
- package/dist/container-runner.d.ts.map +1 -1
- package/dist/container-runner.js +59 -26
- package/dist/container-runner.js.map +1 -1
- package/dist/container-setup.d.ts.map +1 -1
- package/dist/container-setup.js +10 -9
- package/dist/container-setup.js.map +1 -1
- package/dist/conversation.d.ts +2 -2
- package/dist/conversation.d.ts.map +1 -1
- package/dist/conversation.js +1 -1
- package/dist/conversation.js.map +1 -1
- package/dist/db.d.ts +118 -2
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +1568 -50
- package/dist/db.js.map +1 -1
- package/dist/delegation-manager.d.ts.map +1 -1
- package/dist/delegation-manager.js +3 -2
- package/dist/delegation-manager.js.map +1 -1
- package/dist/gateway-client.d.ts +2 -2
- package/dist/gateway-client.d.ts.map +1 -1
- package/dist/gateway-client.js +10 -4
- package/dist/gateway-client.js.map +1 -1
- package/dist/gateway-service.d.ts +3 -3
- package/dist/gateway-service.d.ts.map +1 -1
- package/dist/gateway-service.js +624 -87
- package/dist/gateway-service.js.map +1 -1
- package/dist/gateway-types.d.ts +24 -0
- package/dist/gateway-types.d.ts.map +1 -1
- package/dist/gateway-types.js.map +1 -1
- package/dist/gateway.js +303 -26
- package/dist/gateway.js.map +1 -1
- package/dist/health.d.ts.map +1 -1
- package/dist/health.js +20 -10
- package/dist/health.js.map +1 -1
- package/dist/heartbeat.d.ts +4 -0
- package/dist/heartbeat.d.ts.map +1 -1
- package/dist/heartbeat.js +62 -20
- package/dist/heartbeat.js.map +1 -1
- package/dist/hybridai-bots.d.ts.map +1 -1
- package/dist/hybridai-bots.js +4 -2
- package/dist/hybridai-bots.js.map +1 -1
- package/dist/hybridai-models.d.ts +8 -0
- package/dist/hybridai-models.d.ts.map +1 -0
- package/dist/hybridai-models.js +94 -0
- package/dist/hybridai-models.js.map +1 -0
- package/dist/instruction-approval-audit.d.ts.map +1 -1
- package/dist/instruction-approval-audit.js.map +1 -1
- package/dist/instruction-integrity.d.ts.map +1 -1
- package/dist/instruction-integrity.js +8 -2
- package/dist/instruction-integrity.js.map +1 -1
- package/dist/ipc.d.ts.map +1 -1
- package/dist/ipc.js +6 -1
- package/dist/ipc.js.map +1 -1
- package/dist/logger.js.map +1 -1
- package/dist/memory-consolidation.d.ts +17 -0
- package/dist/memory-consolidation.d.ts.map +1 -0
- package/dist/memory-consolidation.js +25 -0
- package/dist/memory-consolidation.js.map +1 -0
- package/dist/memory-service.d.ts +200 -0
- package/dist/memory-service.d.ts.map +1 -0
- package/dist/memory-service.js +294 -0
- package/dist/memory-service.js.map +1 -0
- package/dist/mount-security.d.ts.map +1 -1
- package/dist/mount-security.js +31 -7
- package/dist/mount-security.js.map +1 -1
- package/dist/observability-ingest.d.ts.map +1 -1
- package/dist/observability-ingest.js +32 -11
- package/dist/observability-ingest.js.map +1 -1
- package/dist/onboarding.d.ts.map +1 -1
- package/dist/onboarding.js +32 -9
- package/dist/onboarding.js.map +1 -1
- package/dist/proactive-policy.d.ts.map +1 -1
- package/dist/proactive-policy.js +2 -1
- package/dist/proactive-policy.js.map +1 -1
- package/dist/prompt-hooks.d.ts.map +1 -1
- package/dist/prompt-hooks.js +11 -7
- package/dist/prompt-hooks.js.map +1 -1
- package/dist/runtime-config.d.ts +104 -1
- package/dist/runtime-config.d.ts.map +1 -1
- package/dist/runtime-config.js +503 -24
- package/dist/runtime-config.js.map +1 -1
- package/dist/scheduled-task-runner.d.ts +1 -0
- package/dist/scheduled-task-runner.d.ts.map +1 -1
- package/dist/scheduled-task-runner.js +43 -10
- package/dist/scheduled-task-runner.js.map +1 -1
- package/dist/scheduler.d.ts +43 -4
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +530 -56
- package/dist/scheduler.js.map +1 -1
- package/dist/session-export.d.ts +26 -0
- package/dist/session-export.d.ts.map +1 -0
- package/dist/session-export.js +149 -0
- package/dist/session-export.js.map +1 -0
- package/dist/session-maintenance.d.ts.map +1 -1
- package/dist/session-maintenance.js +75 -13
- package/dist/session-maintenance.js.map +1 -1
- package/dist/session-transcripts.d.ts.map +1 -1
- package/dist/session-transcripts.js.map +1 -1
- package/dist/side-effects.d.ts.map +1 -1
- package/dist/side-effects.js +14 -2
- package/dist/side-effects.js.map +1 -1
- package/dist/skills-guard.d.ts.map +1 -1
- package/dist/skills-guard.js +893 -130
- package/dist/skills-guard.js.map +1 -1
- package/dist/skills.d.ts +5 -0
- package/dist/skills.d.ts.map +1 -1
- package/dist/skills.js +29 -15
- package/dist/skills.js.map +1 -1
- package/dist/token-efficiency.d.ts.map +1 -1
- package/dist/token-efficiency.js.map +1 -1
- package/dist/tui.js +92 -11
- package/dist/tui.js.map +1 -1
- package/dist/types.d.ts +150 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +24 -1
- package/dist/types.js.map +1 -1
- package/dist/update.d.ts.map +1 -1
- package/dist/update.js +42 -14
- package/dist/update.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +49 -9
- package/dist/workspace.js.map +1 -1
- package/docs/chat.html +9 -3
- package/docs/index.html +72 -13
- package/package.json +8 -2
- package/src/agent.ts +31 -4
- package/src/audit-cli.ts +44 -16
- package/src/audit-events.ts +69 -5
- package/src/audit-trail.ts +41 -15
- package/src/channels/discord/attachments.ts +81 -27
- package/src/channels/discord/debounce.ts +25 -0
- package/src/channels/discord/delivery.ts +65 -14
- package/src/channels/discord/human-delay.ts +48 -0
- package/src/channels/discord/inbound.ts +190 -8
- package/src/channels/discord/mentions.ts +42 -18
- package/src/channels/discord/presence.ts +148 -0
- package/src/channels/discord/rate-limiter.ts +58 -0
- package/src/channels/discord/reactions.ts +211 -0
- package/src/channels/discord/runtime.ts +1161 -205
- package/src/channels/discord/stream.ts +85 -31
- package/src/channels/discord/tool-actions.ts +78 -37
- package/src/channels/discord/typing.ts +140 -0
- package/src/chunk.ts +12 -4
- package/src/cli.ts +174 -57
- package/src/config.ts +208 -36
- package/src/container-runner.ts +134 -42
- package/src/container-setup.ts +57 -22
- package/src/conversation.ts +9 -7
- package/src/db.ts +2217 -84
- package/src/delegation-manager.ts +6 -2
- package/src/gateway-client.ts +41 -17
- package/src/gateway-service.ts +996 -220
- package/src/gateway-types.ts +33 -0
- package/src/gateway.ts +487 -52
- package/src/health.ts +66 -26
- package/src/heartbeat.ts +99 -22
- package/src/hybridai-bots.ts +14 -5
- package/src/hybridai-models.ts +158 -0
- package/src/instruction-approval-audit.ts +4 -1
- package/src/instruction-integrity.ts +30 -9
- package/src/ipc.ts +23 -5
- package/src/logger.ts +4 -1
- package/src/memory-consolidation.ts +41 -0
- package/src/memory-service.ts +606 -0
- package/src/mount-security.ts +58 -13
- package/src/observability-ingest.ts +134 -35
- package/src/onboarding.ts +126 -35
- package/src/proactive-policy.ts +3 -1
- package/src/prompt-hooks.ts +42 -17
- package/src/runtime-config.ts +1112 -122
- package/src/scheduled-task-runner.ts +77 -11
- package/src/scheduler.ts +683 -60
- package/src/session-export.ts +196 -0
- package/src/session-maintenance.ts +125 -22
- package/src/session-transcripts.ts +12 -3
- package/src/side-effects.ts +28 -5
- package/src/skills-guard.ts +1067 -219
- package/src/skills.ts +163 -65
- package/src/token-efficiency.ts +31 -9
- package/src/tui.ts +166 -25
- package/src/types.ts +199 -2
- package/src/update.ts +79 -23
- package/src/workspace.ts +63 -11
- package/tests/approval-policy.test.ts +335 -0
- package/tests/discord.basic.test.ts +213 -4
- package/tests/discord.human-presence.test.ts +85 -0
- package/tests/gateway-service.media-routing.test.ts +8 -2
- package/tests/hybridai-client.test.ts +112 -0
- package/tests/hybridai-models.test.ts +46 -0
- package/tests/memory-service.test.ts +1114 -0
- package/tests/token-efficiency.basic.test.ts +8 -2
- package/tests/token-usage.cache.test.ts +128 -0
- package/vitest.e2e.config.ts +3 -1
- package/vitest.integration.config.ts +3 -1
- package/vitest.live.config.ts +3 -1
- package/vitest.unit.config.ts +9 -0
package/dist/runtime-config.js
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
export const CONFIG_FILE_NAME = 'config.json';
|
|
4
|
-
export const CONFIG_VERSION =
|
|
4
|
+
export const CONFIG_VERSION = 5;
|
|
5
5
|
export const SECURITY_POLICY_VERSION = '2026-02-28';
|
|
6
|
-
const KNOWN_LOG_LEVELS = new Set([
|
|
6
|
+
const KNOWN_LOG_LEVELS = new Set([
|
|
7
|
+
'fatal',
|
|
8
|
+
'error',
|
|
9
|
+
'warn',
|
|
10
|
+
'info',
|
|
11
|
+
'debug',
|
|
12
|
+
'trace',
|
|
13
|
+
'silent',
|
|
14
|
+
]);
|
|
7
15
|
const DEFAULT_RUNTIME_CONFIG = {
|
|
8
16
|
version: CONFIG_VERSION,
|
|
9
17
|
security: {
|
|
@@ -21,12 +29,54 @@ const DEFAULT_RUNTIME_CONFIG = {
|
|
|
21
29
|
presenceIntent: false,
|
|
22
30
|
respondToAllMessages: false,
|
|
23
31
|
commandsOnly: false,
|
|
32
|
+
commandMode: 'public',
|
|
33
|
+
commandAllowedUserIds: [],
|
|
24
34
|
commandUserId: '',
|
|
35
|
+
groupPolicy: 'open',
|
|
36
|
+
freeResponseChannels: [],
|
|
37
|
+
textChunkLimit: 2_000,
|
|
38
|
+
maxLinesPerMessage: 17,
|
|
39
|
+
humanDelay: {
|
|
40
|
+
mode: 'natural',
|
|
41
|
+
minMs: 800,
|
|
42
|
+
maxMs: 2_500,
|
|
43
|
+
},
|
|
44
|
+
typingMode: 'thinking',
|
|
45
|
+
presence: {
|
|
46
|
+
enabled: true,
|
|
47
|
+
intervalMs: 30_000,
|
|
48
|
+
healthyText: 'Watching the channels',
|
|
49
|
+
degradedText: 'Thinking slowly...',
|
|
50
|
+
exhaustedText: 'Taking a break',
|
|
51
|
+
activityType: 'watching',
|
|
52
|
+
},
|
|
53
|
+
lifecycleReactions: {
|
|
54
|
+
enabled: true,
|
|
55
|
+
removeOnComplete: true,
|
|
56
|
+
phases: {
|
|
57
|
+
queued: '⏳',
|
|
58
|
+
thinking: '🤔',
|
|
59
|
+
toolUse: '⚙️',
|
|
60
|
+
streaming: '✍️',
|
|
61
|
+
done: '✅',
|
|
62
|
+
error: '❌',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
ackReaction: '👀',
|
|
66
|
+
ackReactionScope: 'group-mentions',
|
|
67
|
+
removeAckAfterReply: true,
|
|
68
|
+
debounceMs: 2_500,
|
|
69
|
+
rateLimitPerUser: 0,
|
|
70
|
+
rateLimitExemptRoles: [],
|
|
71
|
+
suppressPatterns: ['/stop', '/pause', 'brb', 'afk'],
|
|
72
|
+
maxConcurrentPerChannel: 2,
|
|
73
|
+
guilds: {},
|
|
25
74
|
},
|
|
26
75
|
hybridai: {
|
|
27
76
|
baseUrl: 'https://hybridai.one',
|
|
28
77
|
defaultModel: 'gpt-5-nano',
|
|
29
78
|
defaultChatbotId: '',
|
|
79
|
+
maxTokens: 4_096,
|
|
30
80
|
enableRag: true,
|
|
31
81
|
models: ['gpt-5-nano', 'gpt-5-mini', 'gpt-5'],
|
|
32
82
|
},
|
|
@@ -44,6 +94,10 @@ const DEFAULT_RUNTIME_CONFIG = {
|
|
|
44
94
|
intervalMs: 1_800_000,
|
|
45
95
|
channel: '',
|
|
46
96
|
},
|
|
97
|
+
memory: {
|
|
98
|
+
decayRate: 0.1,
|
|
99
|
+
consolidationIntervalHours: 24,
|
|
100
|
+
},
|
|
47
101
|
ops: {
|
|
48
102
|
healthHost: '127.0.0.1',
|
|
49
103
|
healthPort: 9090,
|
|
@@ -67,7 +121,9 @@ const DEFAULT_RUNTIME_CONFIG = {
|
|
|
67
121
|
},
|
|
68
122
|
sessionCompaction: {
|
|
69
123
|
enabled: true,
|
|
70
|
-
|
|
124
|
+
tokenBudget: 100_000,
|
|
125
|
+
budgetRatio: 0.7,
|
|
126
|
+
threshold: 200,
|
|
71
127
|
keepRecent: 40,
|
|
72
128
|
summaryMaxChars: 8_000,
|
|
73
129
|
preCompactionMemoryFlush: {
|
|
@@ -106,6 +162,9 @@ const DEFAULT_RUNTIME_CONFIG = {
|
|
|
106
162
|
maxIterations: 0,
|
|
107
163
|
},
|
|
108
164
|
},
|
|
165
|
+
scheduler: {
|
|
166
|
+
jobs: [],
|
|
167
|
+
},
|
|
109
168
|
};
|
|
110
169
|
const CONFIG_PATH = path.join(process.cwd(), CONFIG_FILE_NAME);
|
|
111
170
|
let currentConfig = cloneConfig(DEFAULT_RUNTIME_CONFIG);
|
|
@@ -164,6 +223,25 @@ function normalizeInteger(value, fallback, opts) {
|
|
|
164
223
|
parsed = opts.max;
|
|
165
224
|
return parsed;
|
|
166
225
|
}
|
|
226
|
+
function normalizeNumber(value, fallback, opts) {
|
|
227
|
+
let parsed;
|
|
228
|
+
if (typeof value === 'number') {
|
|
229
|
+
parsed = value;
|
|
230
|
+
}
|
|
231
|
+
else if (typeof value === 'string' && value.trim()) {
|
|
232
|
+
parsed = Number.parseFloat(value);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
parsed = fallback;
|
|
236
|
+
}
|
|
237
|
+
if (!Number.isFinite(parsed))
|
|
238
|
+
parsed = fallback;
|
|
239
|
+
if (opts?.min != null && parsed < opts.min)
|
|
240
|
+
parsed = opts.min;
|
|
241
|
+
if (opts?.max != null && parsed > opts.max)
|
|
242
|
+
parsed = opts.max;
|
|
243
|
+
return parsed;
|
|
244
|
+
}
|
|
167
245
|
function normalizeStringArray(value, fallback) {
|
|
168
246
|
if (Array.isArray(value)) {
|
|
169
247
|
const normalized = value
|
|
@@ -182,8 +260,346 @@ function normalizeStringArray(value, fallback) {
|
|
|
182
260
|
}
|
|
183
261
|
return fallback;
|
|
184
262
|
}
|
|
263
|
+
function normalizeDiscordGroupPolicy(value, fallback) {
|
|
264
|
+
if (typeof value !== 'string')
|
|
265
|
+
return fallback;
|
|
266
|
+
const normalized = value.trim().toLowerCase();
|
|
267
|
+
if (normalized === 'open' ||
|
|
268
|
+
normalized === 'allowlist' ||
|
|
269
|
+
normalized === 'disabled') {
|
|
270
|
+
return normalized;
|
|
271
|
+
}
|
|
272
|
+
return fallback;
|
|
273
|
+
}
|
|
274
|
+
function normalizeDiscordCommandMode(value, fallback) {
|
|
275
|
+
if (typeof value !== 'string')
|
|
276
|
+
return fallback;
|
|
277
|
+
const normalized = value.trim().toLowerCase();
|
|
278
|
+
if (normalized === 'public' || normalized === 'restricted') {
|
|
279
|
+
return normalized;
|
|
280
|
+
}
|
|
281
|
+
return fallback;
|
|
282
|
+
}
|
|
283
|
+
function normalizeDiscordChannelMode(value, fallback) {
|
|
284
|
+
if (typeof value !== 'string')
|
|
285
|
+
return fallback;
|
|
286
|
+
const normalized = value.trim().toLowerCase();
|
|
287
|
+
if (normalized === 'off' || normalized === 'mention' || normalized === 'free')
|
|
288
|
+
return normalized;
|
|
289
|
+
if (normalized === 'free-response' || normalized === 'free_response')
|
|
290
|
+
return 'free';
|
|
291
|
+
return fallback;
|
|
292
|
+
}
|
|
293
|
+
function normalizeDiscordTypingMode(value, fallback) {
|
|
294
|
+
if (typeof value !== 'string')
|
|
295
|
+
return fallback;
|
|
296
|
+
const normalized = value.trim().toLowerCase();
|
|
297
|
+
if (normalized === 'instant' ||
|
|
298
|
+
normalized === 'thinking' ||
|
|
299
|
+
normalized === 'streaming' ||
|
|
300
|
+
normalized === 'never') {
|
|
301
|
+
return normalized;
|
|
302
|
+
}
|
|
303
|
+
return fallback;
|
|
304
|
+
}
|
|
305
|
+
function normalizeDiscordHumanDelayMode(value, fallback) {
|
|
306
|
+
if (typeof value !== 'string')
|
|
307
|
+
return fallback;
|
|
308
|
+
const normalized = value.trim().toLowerCase();
|
|
309
|
+
if (normalized === 'off' ||
|
|
310
|
+
normalized === 'natural' ||
|
|
311
|
+
normalized === 'custom') {
|
|
312
|
+
return normalized;
|
|
313
|
+
}
|
|
314
|
+
return fallback;
|
|
315
|
+
}
|
|
316
|
+
function normalizeDiscordAckReactionScope(value, fallback) {
|
|
317
|
+
if (typeof value !== 'string')
|
|
318
|
+
return fallback;
|
|
319
|
+
const normalized = value.trim().toLowerCase();
|
|
320
|
+
if (normalized === 'all' ||
|
|
321
|
+
normalized === 'group-mentions' ||
|
|
322
|
+
normalized === 'direct' ||
|
|
323
|
+
normalized === 'off') {
|
|
324
|
+
return normalized;
|
|
325
|
+
}
|
|
326
|
+
return fallback;
|
|
327
|
+
}
|
|
328
|
+
function normalizeDiscordPresenceActivityType(value, fallback) {
|
|
329
|
+
if (typeof value !== 'string')
|
|
330
|
+
return fallback;
|
|
331
|
+
const normalized = value.trim().toLowerCase();
|
|
332
|
+
if (normalized === 'playing' ||
|
|
333
|
+
normalized === 'watching' ||
|
|
334
|
+
normalized === 'listening' ||
|
|
335
|
+
normalized === 'competing' ||
|
|
336
|
+
normalized === 'custom') {
|
|
337
|
+
return normalized;
|
|
338
|
+
}
|
|
339
|
+
return fallback;
|
|
340
|
+
}
|
|
341
|
+
function normalizeDiscordHumanDelayConfig(value, fallback) {
|
|
342
|
+
const raw = isRecord(value) ? value : {};
|
|
343
|
+
const mode = normalizeDiscordHumanDelayMode(raw.mode, fallback.mode);
|
|
344
|
+
const minMs = normalizeInteger(raw.minMs, fallback.minMs, {
|
|
345
|
+
min: 0,
|
|
346
|
+
max: 120_000,
|
|
347
|
+
});
|
|
348
|
+
const maxMsRaw = normalizeInteger(raw.maxMs, fallback.maxMs, {
|
|
349
|
+
min: 0,
|
|
350
|
+
max: 120_000,
|
|
351
|
+
});
|
|
352
|
+
const maxMs = Math.max(minMs, maxMsRaw);
|
|
353
|
+
return { mode, minMs, maxMs };
|
|
354
|
+
}
|
|
355
|
+
function normalizeDiscordPresenceConfig(value, fallback) {
|
|
356
|
+
const raw = isRecord(value) ? value : {};
|
|
357
|
+
return {
|
|
358
|
+
enabled: normalizeBoolean(raw.enabled, fallback.enabled),
|
|
359
|
+
intervalMs: normalizeInteger(raw.intervalMs, fallback.intervalMs, {
|
|
360
|
+
min: 5_000,
|
|
361
|
+
max: 300_000,
|
|
362
|
+
}),
|
|
363
|
+
healthyText: normalizeString(raw.healthyText, fallback.healthyText, {
|
|
364
|
+
allowEmpty: false,
|
|
365
|
+
}),
|
|
366
|
+
degradedText: normalizeString(raw.degradedText, fallback.degradedText, {
|
|
367
|
+
allowEmpty: false,
|
|
368
|
+
}),
|
|
369
|
+
exhaustedText: normalizeString(raw.exhaustedText, fallback.exhaustedText, {
|
|
370
|
+
allowEmpty: false,
|
|
371
|
+
}),
|
|
372
|
+
activityType: normalizeDiscordPresenceActivityType(raw.activityType, fallback.activityType),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function normalizeDiscordLifecycleReactionsConfig(value, fallback) {
|
|
376
|
+
const raw = isRecord(value) ? value : {};
|
|
377
|
+
const rawPhases = isRecord(raw.phases) ? raw.phases : {};
|
|
378
|
+
return {
|
|
379
|
+
enabled: normalizeBoolean(raw.enabled, fallback.enabled),
|
|
380
|
+
removeOnComplete: normalizeBoolean(raw.removeOnComplete, fallback.removeOnComplete),
|
|
381
|
+
phases: {
|
|
382
|
+
queued: normalizeString(rawPhases.queued, fallback.phases.queued, {
|
|
383
|
+
allowEmpty: false,
|
|
384
|
+
}),
|
|
385
|
+
thinking: normalizeString(rawPhases.thinking, fallback.phases.thinking, {
|
|
386
|
+
allowEmpty: false,
|
|
387
|
+
}),
|
|
388
|
+
toolUse: normalizeString(rawPhases.toolUse, fallback.phases.toolUse, {
|
|
389
|
+
allowEmpty: false,
|
|
390
|
+
}),
|
|
391
|
+
streaming: normalizeString(rawPhases.streaming, fallback.phases.streaming, { allowEmpty: false }),
|
|
392
|
+
done: normalizeString(rawPhases.done, fallback.phases.done, {
|
|
393
|
+
allowEmpty: false,
|
|
394
|
+
}),
|
|
395
|
+
error: normalizeString(rawPhases.error, fallback.phases.error, {
|
|
396
|
+
allowEmpty: false,
|
|
397
|
+
}),
|
|
398
|
+
},
|
|
399
|
+
};
|
|
400
|
+
}
|
|
401
|
+
function normalizeDiscordChannelConfig(value, fallback, defaultMode) {
|
|
402
|
+
const channelFallback = {
|
|
403
|
+
...fallback,
|
|
404
|
+
mode: fallback.mode || defaultMode,
|
|
405
|
+
};
|
|
406
|
+
if (typeof value === 'string') {
|
|
407
|
+
return { mode: normalizeDiscordChannelMode(value, channelFallback.mode) };
|
|
408
|
+
}
|
|
409
|
+
if (!isRecord(value))
|
|
410
|
+
return null;
|
|
411
|
+
const channelConfig = {
|
|
412
|
+
mode: normalizeDiscordChannelMode(value.mode, channelFallback.mode),
|
|
413
|
+
};
|
|
414
|
+
if (value.typingMode !== undefined ||
|
|
415
|
+
channelFallback.typingMode !== undefined) {
|
|
416
|
+
channelConfig.typingMode = normalizeDiscordTypingMode(value.typingMode, channelFallback.typingMode ?? DEFAULT_RUNTIME_CONFIG.discord.typingMode);
|
|
417
|
+
}
|
|
418
|
+
if (value.debounceMs !== undefined ||
|
|
419
|
+
channelFallback.debounceMs !== undefined) {
|
|
420
|
+
channelConfig.debounceMs = normalizeInteger(value.debounceMs, channelFallback.debounceMs ?? DEFAULT_RUNTIME_CONFIG.discord.debounceMs, { min: 0, max: 120_000 });
|
|
421
|
+
}
|
|
422
|
+
if (value.ackReaction !== undefined ||
|
|
423
|
+
channelFallback.ackReaction !== undefined) {
|
|
424
|
+
channelConfig.ackReaction = normalizeString(value.ackReaction, channelFallback.ackReaction ?? DEFAULT_RUNTIME_CONFIG.discord.ackReaction, { allowEmpty: false });
|
|
425
|
+
}
|
|
426
|
+
if (value.ackReactionScope !== undefined ||
|
|
427
|
+
channelFallback.ackReactionScope !== undefined) {
|
|
428
|
+
channelConfig.ackReactionScope = normalizeDiscordAckReactionScope(value.ackReactionScope, channelFallback.ackReactionScope ??
|
|
429
|
+
DEFAULT_RUNTIME_CONFIG.discord.ackReactionScope);
|
|
430
|
+
}
|
|
431
|
+
if (value.removeAckAfterReply !== undefined ||
|
|
432
|
+
channelFallback.removeAckAfterReply !== undefined) {
|
|
433
|
+
channelConfig.removeAckAfterReply = normalizeBoolean(value.removeAckAfterReply, channelFallback.removeAckAfterReply ??
|
|
434
|
+
DEFAULT_RUNTIME_CONFIG.discord.removeAckAfterReply);
|
|
435
|
+
}
|
|
436
|
+
if (value.humanDelay !== undefined ||
|
|
437
|
+
channelFallback.humanDelay !== undefined) {
|
|
438
|
+
channelConfig.humanDelay = normalizeDiscordHumanDelayConfig(value.humanDelay, channelFallback.humanDelay ?? DEFAULT_RUNTIME_CONFIG.discord.humanDelay);
|
|
439
|
+
}
|
|
440
|
+
if (value.rateLimitPerUser !== undefined ||
|
|
441
|
+
channelFallback.rateLimitPerUser !== undefined) {
|
|
442
|
+
channelConfig.rateLimitPerUser = normalizeInteger(value.rateLimitPerUser, channelFallback.rateLimitPerUser ??
|
|
443
|
+
DEFAULT_RUNTIME_CONFIG.discord.rateLimitPerUser, { min: 0, max: 300 });
|
|
444
|
+
}
|
|
445
|
+
if (value.suppressPatterns !== undefined ||
|
|
446
|
+
channelFallback.suppressPatterns !== undefined) {
|
|
447
|
+
channelConfig.suppressPatterns = normalizeStringArray(value.suppressPatterns, channelFallback.suppressPatterns ??
|
|
448
|
+
DEFAULT_RUNTIME_CONFIG.discord.suppressPatterns);
|
|
449
|
+
}
|
|
450
|
+
if (value.maxConcurrentPerChannel !== undefined ||
|
|
451
|
+
channelFallback.maxConcurrentPerChannel !== undefined) {
|
|
452
|
+
channelConfig.maxConcurrentPerChannel = normalizeInteger(value.maxConcurrentPerChannel, channelFallback.maxConcurrentPerChannel ??
|
|
453
|
+
DEFAULT_RUNTIME_CONFIG.discord.maxConcurrentPerChannel, { min: 1, max: 16 });
|
|
454
|
+
}
|
|
455
|
+
return channelConfig;
|
|
456
|
+
}
|
|
457
|
+
function normalizeDiscordGuildConfig(value, fallback) {
|
|
458
|
+
if (!isRecord(value))
|
|
459
|
+
return fallback;
|
|
460
|
+
const defaultMode = normalizeDiscordChannelMode(value.defaultMode, fallback.defaultMode);
|
|
461
|
+
const rawChannels = isRecord(value.channels) ? value.channels : {};
|
|
462
|
+
const channels = {};
|
|
463
|
+
for (const [rawChannelId, rawChannelConfig] of Object.entries(rawChannels)) {
|
|
464
|
+
const channelId = rawChannelId.trim();
|
|
465
|
+
if (!channelId)
|
|
466
|
+
continue;
|
|
467
|
+
const fallbackChannel = fallback.channels[channelId] ?? {
|
|
468
|
+
mode: defaultMode,
|
|
469
|
+
};
|
|
470
|
+
const channelConfig = normalizeDiscordChannelConfig(rawChannelConfig, fallbackChannel, defaultMode);
|
|
471
|
+
if (!channelConfig)
|
|
472
|
+
continue;
|
|
473
|
+
channels[channelId] = channelConfig;
|
|
474
|
+
}
|
|
475
|
+
return { defaultMode, channels };
|
|
476
|
+
}
|
|
477
|
+
function normalizeDiscordGuildMap(value, fallback) {
|
|
478
|
+
if (!isRecord(value))
|
|
479
|
+
return fallback;
|
|
480
|
+
const guilds = {};
|
|
481
|
+
for (const [rawGuildId, rawGuildConfig] of Object.entries(value)) {
|
|
482
|
+
const guildId = rawGuildId.trim();
|
|
483
|
+
if (!guildId)
|
|
484
|
+
continue;
|
|
485
|
+
const fallbackGuild = fallback[guildId] ?? {
|
|
486
|
+
defaultMode: 'mention',
|
|
487
|
+
channels: {},
|
|
488
|
+
};
|
|
489
|
+
guilds[guildId] = normalizeDiscordGuildConfig(rawGuildConfig, fallbackGuild);
|
|
490
|
+
}
|
|
491
|
+
return guilds;
|
|
492
|
+
}
|
|
493
|
+
function normalizeSchedulerScheduleKind(value, fallback) {
|
|
494
|
+
if (typeof value !== 'string')
|
|
495
|
+
return fallback;
|
|
496
|
+
const normalized = value.trim().toLowerCase();
|
|
497
|
+
if (normalized === 'at' || normalized === 'every' || normalized === 'cron')
|
|
498
|
+
return normalized;
|
|
499
|
+
return fallback;
|
|
500
|
+
}
|
|
501
|
+
function normalizeSchedulerActionKind(value, fallback) {
|
|
502
|
+
if (typeof value !== 'string')
|
|
503
|
+
return fallback;
|
|
504
|
+
const normalized = value.trim().toLowerCase();
|
|
505
|
+
if (normalized === 'agent_turn' || normalized === 'system_event')
|
|
506
|
+
return normalized;
|
|
507
|
+
return fallback;
|
|
508
|
+
}
|
|
509
|
+
function normalizeSchedulerDeliveryKind(value, fallback) {
|
|
510
|
+
if (typeof value !== 'string')
|
|
511
|
+
return fallback;
|
|
512
|
+
const normalized = value.trim().toLowerCase();
|
|
513
|
+
if (normalized === 'channel' ||
|
|
514
|
+
normalized === 'last-channel' ||
|
|
515
|
+
normalized === 'webhook')
|
|
516
|
+
return normalized;
|
|
517
|
+
return fallback;
|
|
518
|
+
}
|
|
519
|
+
function normalizeSchedulerJobList(value, fallback) {
|
|
520
|
+
if (!Array.isArray(value))
|
|
521
|
+
return fallback;
|
|
522
|
+
const jobs = [];
|
|
523
|
+
for (const item of value) {
|
|
524
|
+
if (!isRecord(item))
|
|
525
|
+
continue;
|
|
526
|
+
const jobId = normalizeString(item.id, '', { allowEmpty: false });
|
|
527
|
+
if (!jobId)
|
|
528
|
+
continue;
|
|
529
|
+
const rawSchedule = isRecord(item.schedule) ? item.schedule : {};
|
|
530
|
+
const rawAction = isRecord(item.action) ? item.action : {};
|
|
531
|
+
const rawDelivery = isRecord(item.delivery) ? item.delivery : {};
|
|
532
|
+
const scheduleKind = normalizeSchedulerScheduleKind(rawSchedule.kind, 'cron');
|
|
533
|
+
const everyMs = scheduleKind === 'every'
|
|
534
|
+
? normalizeInteger(rawSchedule.everyMs, 60_000, {
|
|
535
|
+
min: 10_000,
|
|
536
|
+
max: 86_400_000,
|
|
537
|
+
})
|
|
538
|
+
: null;
|
|
539
|
+
const atRaw = scheduleKind === 'at'
|
|
540
|
+
? normalizeString(rawSchedule.at, '', { allowEmpty: false })
|
|
541
|
+
: '';
|
|
542
|
+
const atIso = scheduleKind === 'at'
|
|
543
|
+
? (() => {
|
|
544
|
+
const parsed = new Date(atRaw);
|
|
545
|
+
return Number.isNaN(parsed.getTime()) ? null : parsed.toISOString();
|
|
546
|
+
})()
|
|
547
|
+
: null;
|
|
548
|
+
const expr = scheduleKind === 'cron'
|
|
549
|
+
? normalizeString(rawSchedule.expr, '', { allowEmpty: false })
|
|
550
|
+
: '';
|
|
551
|
+
if (scheduleKind === 'at' && !atIso)
|
|
552
|
+
continue;
|
|
553
|
+
if (scheduleKind === 'cron' && !expr)
|
|
554
|
+
continue;
|
|
555
|
+
const deliveryKind = normalizeSchedulerDeliveryKind(rawDelivery.kind, 'channel');
|
|
556
|
+
const to = normalizeString(rawDelivery.to, '', { allowEmpty: true });
|
|
557
|
+
const webhookUrl = normalizeString(rawDelivery.webhookUrl ?? rawDelivery.url, '', { allowEmpty: true });
|
|
558
|
+
if (deliveryKind === 'channel' && !to)
|
|
559
|
+
continue;
|
|
560
|
+
if (deliveryKind === 'webhook' && !webhookUrl)
|
|
561
|
+
continue;
|
|
562
|
+
const actionMessage = normalizeString(rawAction.message, '', {
|
|
563
|
+
allowEmpty: false,
|
|
564
|
+
});
|
|
565
|
+
if (!actionMessage)
|
|
566
|
+
continue;
|
|
567
|
+
const name = normalizeString(item.name, '', { allowEmpty: true });
|
|
568
|
+
const description = normalizeString(item.description, '', {
|
|
569
|
+
allowEmpty: true,
|
|
570
|
+
});
|
|
571
|
+
jobs.push({
|
|
572
|
+
id: jobId,
|
|
573
|
+
...(name ? { name } : {}),
|
|
574
|
+
...(description ? { description } : {}),
|
|
575
|
+
schedule: {
|
|
576
|
+
kind: scheduleKind,
|
|
577
|
+
at: atIso,
|
|
578
|
+
everyMs,
|
|
579
|
+
expr: scheduleKind === 'cron' ? expr : null,
|
|
580
|
+
tz: normalizeString(rawSchedule.tz, '', { allowEmpty: true }),
|
|
581
|
+
},
|
|
582
|
+
action: {
|
|
583
|
+
kind: normalizeSchedulerActionKind(rawAction.kind, 'agent_turn'),
|
|
584
|
+
message: actionMessage,
|
|
585
|
+
},
|
|
586
|
+
delivery: {
|
|
587
|
+
kind: deliveryKind,
|
|
588
|
+
channel: normalizeString(rawDelivery.channel, 'discord', {
|
|
589
|
+
allowEmpty: true,
|
|
590
|
+
}),
|
|
591
|
+
to,
|
|
592
|
+
webhookUrl,
|
|
593
|
+
},
|
|
594
|
+
enabled: normalizeBoolean(item.enabled, true),
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
return jobs;
|
|
598
|
+
}
|
|
185
599
|
function normalizeLogLevel(value, fallback) {
|
|
186
|
-
const normalized = normalizeString(value, fallback, {
|
|
600
|
+
const normalized = normalizeString(value, fallback, {
|
|
601
|
+
allowEmpty: false,
|
|
602
|
+
}).toLowerCase();
|
|
187
603
|
if (KNOWN_LOG_LEVELS.has(normalized))
|
|
188
604
|
return normalized;
|
|
189
605
|
return fallback;
|
|
@@ -193,7 +609,10 @@ function normalizeBaseUrl(value, fallback) {
|
|
|
193
609
|
return candidate.replace(/\/+$/, '') || fallback;
|
|
194
610
|
}
|
|
195
611
|
function normalizeApiPath(value, fallback) {
|
|
196
|
-
const normalized = normalizeString(value, fallback, {
|
|
612
|
+
const normalized = normalizeString(value, fallback, {
|
|
613
|
+
allowEmpty: false,
|
|
614
|
+
trim: true,
|
|
615
|
+
});
|
|
197
616
|
if (/^https?:\/\//i.test(normalized)) {
|
|
198
617
|
return normalized.replace(/\/+$/, '');
|
|
199
618
|
}
|
|
@@ -214,27 +633,45 @@ function normalizeRuntimeConfig(patch) {
|
|
|
214
633
|
const rawHybridAi = isRecord(raw.hybridai) ? raw.hybridai : {};
|
|
215
634
|
const rawContainer = isRecord(raw.container) ? raw.container : {};
|
|
216
635
|
const rawHeartbeat = isRecord(raw.heartbeat) ? raw.heartbeat : {};
|
|
636
|
+
const rawMemory = isRecord(raw.memory) ? raw.memory : {};
|
|
217
637
|
const rawOps = isRecord(raw.ops) ? raw.ops : {};
|
|
218
638
|
const rawObservability = isRecord(raw.observability) ? raw.observability : {};
|
|
219
|
-
const rawSessionCompaction = isRecord(raw.sessionCompaction)
|
|
639
|
+
const rawSessionCompaction = isRecord(raw.sessionCompaction)
|
|
640
|
+
? raw.sessionCompaction
|
|
641
|
+
: {};
|
|
220
642
|
const rawPreFlush = isRecord(rawSessionCompaction.preCompactionMemoryFlush)
|
|
221
643
|
? rawSessionCompaction.preCompactionMemoryFlush
|
|
222
644
|
: {};
|
|
223
645
|
const rawPromptHooks = isRecord(raw.promptHooks) ? raw.promptHooks : {};
|
|
224
646
|
const rawProactive = isRecord(raw.proactive) ? raw.proactive : {};
|
|
225
|
-
const rawActiveHours = isRecord(rawProactive.activeHours)
|
|
226
|
-
|
|
227
|
-
|
|
647
|
+
const rawActiveHours = isRecord(rawProactive.activeHours)
|
|
648
|
+
? rawProactive.activeHours
|
|
649
|
+
: {};
|
|
650
|
+
const rawDelegation = isRecord(rawProactive.delegation)
|
|
651
|
+
? rawProactive.delegation
|
|
652
|
+
: {};
|
|
653
|
+
const rawAutoRetry = isRecord(rawProactive.autoRetry)
|
|
654
|
+
? rawProactive.autoRetry
|
|
655
|
+
: {};
|
|
228
656
|
const rawRalph = isRecord(rawProactive.ralph) ? rawProactive.ralph : {};
|
|
657
|
+
const rawScheduler = isRecord(raw.scheduler) ? raw.scheduler : {};
|
|
229
658
|
const defaultOps = DEFAULT_RUNTIME_CONFIG.ops;
|
|
230
659
|
const healthPort = normalizeInteger(rawOps.healthPort, defaultOps.healthPort, { min: 1, max: 65_535 });
|
|
231
660
|
const webApiToken = normalizeString(rawOps.webApiToken, defaultOps.webApiToken, { allowEmpty: true });
|
|
232
661
|
const hybridBaseUrl = normalizeBaseUrl(rawHybridAi.baseUrl, DEFAULT_RUNTIME_CONFIG.hybridai.baseUrl);
|
|
233
662
|
const hybridDefaultChatbotId = normalizeString(rawHybridAi.defaultChatbotId, DEFAULT_RUNTIME_CONFIG.hybridai.defaultChatbotId, { allowEmpty: true });
|
|
234
663
|
const threshold = normalizeInteger(rawSessionCompaction.threshold, DEFAULT_RUNTIME_CONFIG.sessionCompaction.threshold, { min: 20 });
|
|
664
|
+
const tokenBudget = normalizeInteger(rawSessionCompaction.tokenBudget, DEFAULT_RUNTIME_CONFIG.sessionCompaction.tokenBudget, { min: 1_000 });
|
|
665
|
+
const budgetRatio = normalizeNumber(rawSessionCompaction.budgetRatio, DEFAULT_RUNTIME_CONFIG.sessionCompaction.budgetRatio, { min: 0.05, max: 1 });
|
|
235
666
|
const keepRecentRaw = normalizeInteger(rawSessionCompaction.keepRecent, DEFAULT_RUNTIME_CONFIG.sessionCompaction.keepRecent, { min: 1 });
|
|
236
667
|
const keepRecent = Math.min(keepRecentRaw, Math.max(1, threshold - 1));
|
|
237
668
|
const modelList = normalizeStringArray(rawHybridAi.models, DEFAULT_RUNTIME_CONFIG.hybridai.models);
|
|
669
|
+
const normalizedCommandUserId = normalizeString(rawDiscord.commandUserId, DEFAULT_RUNTIME_CONFIG.discord.commandUserId, { allowEmpty: true });
|
|
670
|
+
const normalizedCommandAllowedUserIds = normalizeStringArray(rawDiscord.commandAllowedUserIds, DEFAULT_RUNTIME_CONFIG.discord.commandAllowedUserIds);
|
|
671
|
+
const legacyCommandModeFallback = normalizedCommandUserId
|
|
672
|
+
? 'restricted'
|
|
673
|
+
: DEFAULT_RUNTIME_CONFIG.discord.commandMode;
|
|
674
|
+
const normalizedCommandMode = normalizeDiscordCommandMode(rawDiscord.commandMode, legacyCommandModeFallback);
|
|
238
675
|
return {
|
|
239
676
|
version: CONFIG_VERSION,
|
|
240
677
|
security: {
|
|
@@ -252,12 +689,32 @@ function normalizeRuntimeConfig(patch) {
|
|
|
252
689
|
presenceIntent: normalizeBoolean(rawDiscord.presenceIntent, DEFAULT_RUNTIME_CONFIG.discord.presenceIntent),
|
|
253
690
|
respondToAllMessages: normalizeBoolean(rawDiscord.respondToAllMessages, DEFAULT_RUNTIME_CONFIG.discord.respondToAllMessages),
|
|
254
691
|
commandsOnly: normalizeBoolean(rawDiscord.commandsOnly, DEFAULT_RUNTIME_CONFIG.discord.commandsOnly),
|
|
255
|
-
|
|
692
|
+
commandMode: normalizedCommandMode,
|
|
693
|
+
commandAllowedUserIds: normalizedCommandAllowedUserIds,
|
|
694
|
+
commandUserId: normalizedCommandUserId,
|
|
695
|
+
groupPolicy: normalizeDiscordGroupPolicy(rawDiscord.groupPolicy, DEFAULT_RUNTIME_CONFIG.discord.groupPolicy),
|
|
696
|
+
freeResponseChannels: normalizeStringArray(rawDiscord.freeResponseChannels, DEFAULT_RUNTIME_CONFIG.discord.freeResponseChannels),
|
|
697
|
+
textChunkLimit: normalizeInteger(rawDiscord.textChunkLimit, DEFAULT_RUNTIME_CONFIG.discord.textChunkLimit, { min: 200, max: 2_000 }),
|
|
698
|
+
maxLinesPerMessage: normalizeInteger(rawDiscord.maxLinesPerMessage, DEFAULT_RUNTIME_CONFIG.discord.maxLinesPerMessage, { min: 4, max: 200 }),
|
|
699
|
+
humanDelay: normalizeDiscordHumanDelayConfig(rawDiscord.humanDelay, DEFAULT_RUNTIME_CONFIG.discord.humanDelay),
|
|
700
|
+
typingMode: normalizeDiscordTypingMode(rawDiscord.typingMode, DEFAULT_RUNTIME_CONFIG.discord.typingMode),
|
|
701
|
+
presence: normalizeDiscordPresenceConfig(rawDiscord.presence, DEFAULT_RUNTIME_CONFIG.discord.presence),
|
|
702
|
+
lifecycleReactions: normalizeDiscordLifecycleReactionsConfig(rawDiscord.lifecycleReactions, DEFAULT_RUNTIME_CONFIG.discord.lifecycleReactions),
|
|
703
|
+
ackReaction: normalizeString(rawDiscord.ackReaction, DEFAULT_RUNTIME_CONFIG.discord.ackReaction, { allowEmpty: false }),
|
|
704
|
+
ackReactionScope: normalizeDiscordAckReactionScope(rawDiscord.ackReactionScope, DEFAULT_RUNTIME_CONFIG.discord.ackReactionScope),
|
|
705
|
+
removeAckAfterReply: normalizeBoolean(rawDiscord.removeAckAfterReply, DEFAULT_RUNTIME_CONFIG.discord.removeAckAfterReply),
|
|
706
|
+
debounceMs: normalizeInteger(rawDiscord.debounceMs, DEFAULT_RUNTIME_CONFIG.discord.debounceMs, { min: 0, max: 120_000 }),
|
|
707
|
+
rateLimitPerUser: normalizeInteger(rawDiscord.rateLimitPerUser, DEFAULT_RUNTIME_CONFIG.discord.rateLimitPerUser, { min: 0, max: 300 }),
|
|
708
|
+
rateLimitExemptRoles: normalizeStringArray(rawDiscord.rateLimitExemptRoles, DEFAULT_RUNTIME_CONFIG.discord.rateLimitExemptRoles),
|
|
709
|
+
suppressPatterns: normalizeStringArray(rawDiscord.suppressPatterns, DEFAULT_RUNTIME_CONFIG.discord.suppressPatterns),
|
|
710
|
+
maxConcurrentPerChannel: normalizeInteger(rawDiscord.maxConcurrentPerChannel, DEFAULT_RUNTIME_CONFIG.discord.maxConcurrentPerChannel, { min: 1, max: 16 }),
|
|
711
|
+
guilds: normalizeDiscordGuildMap(rawDiscord.guilds, DEFAULT_RUNTIME_CONFIG.discord.guilds),
|
|
256
712
|
},
|
|
257
713
|
hybridai: {
|
|
258
714
|
baseUrl: hybridBaseUrl,
|
|
259
715
|
defaultModel: normalizeString(rawHybridAi.defaultModel, DEFAULT_RUNTIME_CONFIG.hybridai.defaultModel, { allowEmpty: false }),
|
|
260
716
|
defaultChatbotId: hybridDefaultChatbotId,
|
|
717
|
+
maxTokens: normalizeInteger(rawHybridAi.maxTokens, DEFAULT_RUNTIME_CONFIG.hybridai.maxTokens, { min: 256, max: 32_768 }),
|
|
261
718
|
enableRag: normalizeBoolean(rawHybridAi.enableRag, DEFAULT_RUNTIME_CONFIG.hybridai.enableRag),
|
|
262
719
|
models: modelList,
|
|
263
720
|
},
|
|
@@ -275,13 +732,23 @@ function normalizeRuntimeConfig(patch) {
|
|
|
275
732
|
intervalMs: normalizeInteger(rawHeartbeat.intervalMs, DEFAULT_RUNTIME_CONFIG.heartbeat.intervalMs, { min: 10_000 }),
|
|
276
733
|
channel: normalizeString(rawHeartbeat.channel, DEFAULT_RUNTIME_CONFIG.heartbeat.channel, { allowEmpty: true }),
|
|
277
734
|
},
|
|
735
|
+
memory: {
|
|
736
|
+
decayRate: normalizeNumber(rawMemory.decayRate, DEFAULT_RUNTIME_CONFIG.memory.decayRate, { min: 0, max: 0.95 }),
|
|
737
|
+
consolidationIntervalHours: normalizeInteger(rawMemory.consolidationIntervalHours, DEFAULT_RUNTIME_CONFIG.memory.consolidationIntervalHours, { min: 0, max: 24 * 30 }),
|
|
738
|
+
},
|
|
278
739
|
ops: {
|
|
279
|
-
healthHost: normalizeString(rawOps.healthHost, defaultOps.healthHost, {
|
|
740
|
+
healthHost: normalizeString(rawOps.healthHost, defaultOps.healthHost, {
|
|
741
|
+
allowEmpty: false,
|
|
742
|
+
}),
|
|
280
743
|
healthPort,
|
|
281
744
|
webApiToken,
|
|
282
745
|
gatewayBaseUrl: normalizeBaseUrl(rawOps.gatewayBaseUrl, `http://127.0.0.1:${healthPort}`),
|
|
283
|
-
gatewayApiToken: normalizeString(rawOps.gatewayApiToken, webApiToken, {
|
|
284
|
-
|
|
746
|
+
gatewayApiToken: normalizeString(rawOps.gatewayApiToken, webApiToken, {
|
|
747
|
+
allowEmpty: true,
|
|
748
|
+
}),
|
|
749
|
+
dbPath: normalizeString(rawOps.dbPath, defaultOps.dbPath, {
|
|
750
|
+
allowEmpty: false,
|
|
751
|
+
}),
|
|
285
752
|
logLevel: normalizeLogLevel(rawOps.logLevel, defaultOps.logLevel),
|
|
286
753
|
},
|
|
287
754
|
observability: {
|
|
@@ -289,7 +756,9 @@ function normalizeRuntimeConfig(patch) {
|
|
|
289
756
|
baseUrl: normalizeBaseUrl(rawObservability.baseUrl, hybridBaseUrl),
|
|
290
757
|
ingestPath: normalizeApiPath(rawObservability.ingestPath, DEFAULT_RUNTIME_CONFIG.observability.ingestPath),
|
|
291
758
|
statusPath: normalizeApiPath(rawObservability.statusPath, DEFAULT_RUNTIME_CONFIG.observability.statusPath),
|
|
292
|
-
botId: normalizeString(rawObservability.botId, hybridDefaultChatbotId, {
|
|
759
|
+
botId: normalizeString(rawObservability.botId, hybridDefaultChatbotId, {
|
|
760
|
+
allowEmpty: true,
|
|
761
|
+
}),
|
|
293
762
|
agentId: normalizeString(rawObservability.agentId, DEFAULT_RUNTIME_CONFIG.observability.agentId, { allowEmpty: false }),
|
|
294
763
|
label: normalizeString(rawObservability.label, DEFAULT_RUNTIME_CONFIG.observability.label, { allowEmpty: true }),
|
|
295
764
|
environment: normalizeString(rawObservability.environment, DEFAULT_RUNTIME_CONFIG.observability.environment, { allowEmpty: false }),
|
|
@@ -298,13 +767,18 @@ function normalizeRuntimeConfig(patch) {
|
|
|
298
767
|
},
|
|
299
768
|
sessionCompaction: {
|
|
300
769
|
enabled: normalizeBoolean(rawSessionCompaction.enabled, DEFAULT_RUNTIME_CONFIG.sessionCompaction.enabled),
|
|
770
|
+
tokenBudget,
|
|
771
|
+
budgetRatio,
|
|
301
772
|
threshold,
|
|
302
773
|
keepRecent,
|
|
303
774
|
summaryMaxChars: normalizeInteger(rawSessionCompaction.summaryMaxChars, DEFAULT_RUNTIME_CONFIG.sessionCompaction.summaryMaxChars, { min: 1_000 }),
|
|
304
775
|
preCompactionMemoryFlush: {
|
|
305
|
-
enabled: normalizeBoolean(rawPreFlush.enabled, DEFAULT_RUNTIME_CONFIG.sessionCompaction.preCompactionMemoryFlush
|
|
306
|
-
|
|
307
|
-
|
|
776
|
+
enabled: normalizeBoolean(rawPreFlush.enabled, DEFAULT_RUNTIME_CONFIG.sessionCompaction.preCompactionMemoryFlush
|
|
777
|
+
.enabled),
|
|
778
|
+
maxMessages: normalizeInteger(rawPreFlush.maxMessages, DEFAULT_RUNTIME_CONFIG.sessionCompaction.preCompactionMemoryFlush
|
|
779
|
+
.maxMessages, { min: 8 }),
|
|
780
|
+
maxChars: normalizeInteger(rawPreFlush.maxChars, DEFAULT_RUNTIME_CONFIG.sessionCompaction.preCompactionMemoryFlush
|
|
781
|
+
.maxChars, { min: 4_000 }),
|
|
308
782
|
},
|
|
309
783
|
},
|
|
310
784
|
promptHooks: {
|
|
@@ -337,6 +811,9 @@ function normalizeRuntimeConfig(patch) {
|
|
|
337
811
|
maxIterations: normalizeInteger(rawRalph.maxIterations, DEFAULT_RUNTIME_CONFIG.proactive.ralph.maxIterations, { min: -1, max: 64 }),
|
|
338
812
|
},
|
|
339
813
|
},
|
|
814
|
+
scheduler: {
|
|
815
|
+
jobs: normalizeSchedulerJobList(rawScheduler.jobs, DEFAULT_RUNTIME_CONFIG.scheduler.jobs),
|
|
816
|
+
},
|
|
340
817
|
};
|
|
341
818
|
}
|
|
342
819
|
function loadConfigPatchFromDisk() {
|
|
@@ -397,7 +874,7 @@ function scheduleWatcherRestart(reason) {
|
|
|
397
874
|
return;
|
|
398
875
|
}
|
|
399
876
|
watcherRetryAttempt += 1;
|
|
400
|
-
const delay = Math.min(WATCHER_RETRY_BASE_DELAY_MS *
|
|
877
|
+
const delay = Math.min(WATCHER_RETRY_BASE_DELAY_MS * 2 ** (watcherRetryAttempt - 1), WATCHER_RETRY_MAX_DELAY_MS);
|
|
401
878
|
console.warn(`[runtime-config] watcher restart in ${delay}ms (attempt ${watcherRetryAttempt}/${WATCHER_RETRY_MAX_ATTEMPTS})`);
|
|
402
879
|
watcherRestartTimer = setTimeout(() => {
|
|
403
880
|
watcherRestartTimer = null;
|
|
@@ -521,13 +998,15 @@ export function updateRuntimeConfig(mutator) {
|
|
|
521
998
|
return saveRuntimeConfig(draft);
|
|
522
999
|
}
|
|
523
1000
|
export function isSecurityTrustAccepted(config = currentConfig) {
|
|
524
|
-
return Boolean(config.security.trustModelAccepted
|
|
525
|
-
|
|
526
|
-
|
|
1001
|
+
return Boolean(config.security.trustModelAccepted &&
|
|
1002
|
+
config.security.trustModelAcceptedAt &&
|
|
1003
|
+
config.security.trustModelVersion === SECURITY_POLICY_VERSION);
|
|
527
1004
|
}
|
|
528
1005
|
export function acceptSecurityTrustModel(params) {
|
|
529
1006
|
const acceptedAt = normalizeString(params?.acceptedAt, new Date().toISOString(), { allowEmpty: false });
|
|
530
|
-
const acceptedBy = normalizeString(params?.acceptedBy ?? '', '', {
|
|
1007
|
+
const acceptedBy = normalizeString(params?.acceptedBy ?? '', '', {
|
|
1008
|
+
allowEmpty: true,
|
|
1009
|
+
});
|
|
531
1010
|
const policyVersion = normalizeString(params?.policyVersion, SECURITY_POLICY_VERSION, { allowEmpty: false });
|
|
532
1011
|
return updateRuntimeConfig((draft) => {
|
|
533
1012
|
draft.security.trustModelAccepted = true;
|