@poolzin/pool-bot 2026.2.0 → 2026.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +9 -11
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/model-selection.js +21 -0
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +1 -0
- package/dist/agents/pi-embedded-runner/model.js +61 -2
- package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
- package/dist/agents/pi-embedded-runner/run.js +199 -46
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-tools.js +10 -5
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +24 -0
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/envelope.js +50 -72
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +1 -0
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/get-reply-run.js +2 -1
- package/dist/auto-reply/reply/inbound-context.js +5 -1
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +50 -7
- package/dist/browser/config.js +1 -10
- package/dist/browser/extension-relay.js +101 -40
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +5 -5
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/update-cli.js +173 -52
- package/dist/commands/agent.js +1 -0
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.status-command.js +43 -23
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/config/defaults.js +3 -2
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +2 -2
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +171 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-http.js +109 -11
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +5 -1
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/media/fetch.js +81 -58
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +2 -2
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +28 -16
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +154 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/node-host/runner.js +34 -24
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/source-display.js +51 -0
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +1 -2
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +37 -10
- package/dist/telegram/bot-message-dispatch.js +48 -15
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +45 -14
- package/dist/version.js +42 -5
- package/package.json +1 -1
|
@@ -49,6 +49,64 @@ function normalizeAllowFromEntry(params) {
|
|
|
49
49
|
});
|
|
50
50
|
return normalized.filter((entry) => entry.trim().length > 0);
|
|
51
51
|
}
|
|
52
|
+
function resolveOwnerAllowFromList(params) {
|
|
53
|
+
const raw = params.allowFrom ?? params.cfg.commands?.ownerAllowFrom;
|
|
54
|
+
if (!Array.isArray(raw) || raw.length === 0) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const filtered = [];
|
|
58
|
+
for (const entry of raw) {
|
|
59
|
+
const trimmed = String(entry ?? "").trim();
|
|
60
|
+
if (!trimmed)
|
|
61
|
+
continue;
|
|
62
|
+
const separatorIndex = trimmed.indexOf(":");
|
|
63
|
+
if (separatorIndex > 0) {
|
|
64
|
+
const prefix = trimmed.slice(0, separatorIndex);
|
|
65
|
+
const channel = normalizeAnyChannelId(prefix);
|
|
66
|
+
if (channel) {
|
|
67
|
+
if (params.providerId && channel !== params.providerId)
|
|
68
|
+
continue;
|
|
69
|
+
const remainder = trimmed.slice(separatorIndex + 1).trim();
|
|
70
|
+
if (remainder)
|
|
71
|
+
filtered.push(remainder);
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
filtered.push(trimmed);
|
|
76
|
+
}
|
|
77
|
+
return formatAllowFromList({
|
|
78
|
+
dock: params.dock,
|
|
79
|
+
cfg: params.cfg,
|
|
80
|
+
accountId: params.accountId,
|
|
81
|
+
allowFrom: filtered,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolves the commands.allowFrom list for a given provider.
|
|
86
|
+
* Returns the provider-specific list if defined, otherwise the "*" global list.
|
|
87
|
+
* Returns null if commands.allowFrom is not configured at all (fall back to channel allowFrom).
|
|
88
|
+
*/
|
|
89
|
+
function resolveCommandsAllowFromList(params) {
|
|
90
|
+
const { dock, cfg, accountId, providerId } = params;
|
|
91
|
+
const commandsAllowFrom = cfg.commands?.allowFrom;
|
|
92
|
+
if (!commandsAllowFrom || typeof commandsAllowFrom !== "object") {
|
|
93
|
+
return null; // Not configured, fall back to channel allowFrom
|
|
94
|
+
}
|
|
95
|
+
// Check provider-specific list first, then fall back to global "*"
|
|
96
|
+
const providerKey = providerId ?? "";
|
|
97
|
+
const providerList = commandsAllowFrom[providerKey];
|
|
98
|
+
const globalList = commandsAllowFrom["*"];
|
|
99
|
+
const rawList = Array.isArray(providerList) ? providerList : globalList;
|
|
100
|
+
if (!Array.isArray(rawList)) {
|
|
101
|
+
return null; // No applicable list found
|
|
102
|
+
}
|
|
103
|
+
return formatAllowFromList({
|
|
104
|
+
dock,
|
|
105
|
+
cfg,
|
|
106
|
+
accountId,
|
|
107
|
+
allowFrom: rawList,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
52
110
|
function resolveSenderCandidates(params) {
|
|
53
111
|
const { dock, cfg, accountId } = params;
|
|
54
112
|
const candidates = [];
|
|
@@ -83,6 +141,13 @@ export function resolveCommandAuthorization(params) {
|
|
|
83
141
|
const dock = providerId ? getChannelDock(providerId) : undefined;
|
|
84
142
|
const from = (ctx.From ?? "").trim();
|
|
85
143
|
const to = (ctx.To ?? "").trim();
|
|
144
|
+
// Check if commands.allowFrom is configured (separate command authorization)
|
|
145
|
+
const commandsAllowFromList = resolveCommandsAllowFromList({
|
|
146
|
+
dock,
|
|
147
|
+
cfg,
|
|
148
|
+
accountId: ctx.AccountId,
|
|
149
|
+
providerId,
|
|
150
|
+
});
|
|
86
151
|
const allowFromRaw = dock?.config?.resolveAllowFrom
|
|
87
152
|
? dock.config.resolveAllowFrom({ cfg, accountId: ctx.AccountId })
|
|
88
153
|
: [];
|
|
@@ -92,9 +157,23 @@ export function resolveCommandAuthorization(params) {
|
|
|
92
157
|
accountId: ctx.AccountId,
|
|
93
158
|
allowFrom: Array.isArray(allowFromRaw) ? allowFromRaw : [],
|
|
94
159
|
});
|
|
160
|
+
const configOwnerAllowFromList = resolveOwnerAllowFromList({
|
|
161
|
+
dock,
|
|
162
|
+
cfg,
|
|
163
|
+
accountId: ctx.AccountId,
|
|
164
|
+
providerId,
|
|
165
|
+
allowFrom: cfg.commands?.ownerAllowFrom,
|
|
166
|
+
});
|
|
167
|
+
const contextOwnerAllowFromList = resolveOwnerAllowFromList({
|
|
168
|
+
dock,
|
|
169
|
+
cfg,
|
|
170
|
+
accountId: ctx.AccountId,
|
|
171
|
+
providerId,
|
|
172
|
+
allowFrom: ctx.OwnerAllowFrom,
|
|
173
|
+
});
|
|
95
174
|
const allowAll = allowFromList.length === 0 || allowFromList.some((entry) => entry.trim() === "*");
|
|
96
|
-
const
|
|
97
|
-
if (!allowAll &&
|
|
175
|
+
const ownerCandidatesForCommands = allowAll ? [] : allowFromList.filter((entry) => entry !== "*");
|
|
176
|
+
if (!allowAll && ownerCandidatesForCommands.length === 0 && to) {
|
|
98
177
|
const normalizedTo = normalizeAllowFromEntry({
|
|
99
178
|
dock,
|
|
100
179
|
cfg,
|
|
@@ -102,9 +181,18 @@ export function resolveCommandAuthorization(params) {
|
|
|
102
181
|
value: to,
|
|
103
182
|
});
|
|
104
183
|
if (normalizedTo.length > 0)
|
|
105
|
-
|
|
184
|
+
ownerCandidatesForCommands.push(...normalizedTo);
|
|
106
185
|
}
|
|
107
|
-
const
|
|
186
|
+
const ownerAllowAll = configOwnerAllowFromList.some((entry) => entry.trim() === "*");
|
|
187
|
+
const explicitOwners = configOwnerAllowFromList.filter((entry) => entry !== "*");
|
|
188
|
+
const explicitOverrides = contextOwnerAllowFromList.filter((entry) => entry !== "*");
|
|
189
|
+
const ownerList = Array.from(new Set(explicitOwners.length > 0
|
|
190
|
+
? explicitOwners
|
|
191
|
+
: ownerAllowAll
|
|
192
|
+
? []
|
|
193
|
+
: explicitOverrides.length > 0
|
|
194
|
+
? explicitOverrides
|
|
195
|
+
: ownerCandidatesForCommands));
|
|
108
196
|
const senderCandidates = resolveSenderCandidates({
|
|
109
197
|
dock,
|
|
110
198
|
providerId,
|
|
@@ -117,14 +205,41 @@ export function resolveCommandAuthorization(params) {
|
|
|
117
205
|
const matchedSender = ownerList.length
|
|
118
206
|
? senderCandidates.find((candidate) => ownerList.includes(candidate))
|
|
119
207
|
: undefined;
|
|
208
|
+
const matchedCommandOwner = ownerCandidatesForCommands.length
|
|
209
|
+
? senderCandidates.find((candidate) => ownerCandidatesForCommands.includes(candidate))
|
|
210
|
+
: undefined;
|
|
120
211
|
const senderId = matchedSender ?? senderCandidates[0];
|
|
121
212
|
const enforceOwner = Boolean(dock?.commands?.enforceOwnerForCommands);
|
|
122
|
-
const
|
|
123
|
-
const
|
|
213
|
+
const senderIsOwner = Boolean(matchedSender);
|
|
214
|
+
const ownerAllowlistConfigured = ownerAllowAll || explicitOwners.length > 0;
|
|
215
|
+
const requireOwner = enforceOwner || ownerAllowlistConfigured;
|
|
216
|
+
const isOwnerForCommands = !requireOwner
|
|
217
|
+
? true
|
|
218
|
+
: ownerAllowAll
|
|
219
|
+
? true
|
|
220
|
+
: ownerAllowlistConfigured
|
|
221
|
+
? senderIsOwner
|
|
222
|
+
: allowAll || ownerCandidatesForCommands.length === 0 || Boolean(matchedCommandOwner);
|
|
223
|
+
// If commands.allowFrom is configured, use it for command authorization
|
|
224
|
+
// Otherwise, fall back to existing behavior (channel allowFrom + owner checks)
|
|
225
|
+
let isAuthorizedSender;
|
|
226
|
+
if (commandsAllowFromList !== null) {
|
|
227
|
+
// commands.allowFrom is configured - use it for authorization
|
|
228
|
+
const commandsAllowAll = commandsAllowFromList.some((entry) => entry.trim() === "*");
|
|
229
|
+
const matchedCommandsAllowFrom = commandsAllowFromList.length
|
|
230
|
+
? senderCandidates.find((candidate) => commandsAllowFromList.includes(candidate))
|
|
231
|
+
: undefined;
|
|
232
|
+
isAuthorizedSender = commandsAllowAll || Boolean(matchedCommandsAllowFrom);
|
|
233
|
+
}
|
|
234
|
+
else {
|
|
235
|
+
// Fall back to existing behavior
|
|
236
|
+
isAuthorizedSender = commandAuthorized && isOwnerForCommands;
|
|
237
|
+
}
|
|
124
238
|
return {
|
|
125
239
|
providerId,
|
|
126
240
|
ownerList,
|
|
127
241
|
senderId: senderId || undefined,
|
|
242
|
+
senderIsOwner,
|
|
128
243
|
isAuthorizedSender,
|
|
129
244
|
from: from || undefined,
|
|
130
245
|
to: to || undefined,
|
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { resolveUserTimezone } from "../agents/date-time.js";
|
|
2
2
|
import { normalizeChatType } from "../channels/chat-type.js";
|
|
3
3
|
import { resolveSenderLabel } from "../channels/sender-label.js";
|
|
4
|
+
import { resolveTimezone, formatUtcTimestamp, formatZonedTimestamp, } from "../infra/format-time/format-datetime.js";
|
|
5
|
+
import { formatTimeAgo } from "../infra/format-time/format-relative.js";
|
|
6
|
+
function sanitizeEnvelopeHeaderPart(value) {
|
|
7
|
+
return value
|
|
8
|
+
.replace(/\r\n|\r|\n/g, " ")
|
|
9
|
+
.replaceAll("[", "(")
|
|
10
|
+
.replaceAll("]", ")")
|
|
11
|
+
.replace(/\s+/g, " ")
|
|
12
|
+
.trim();
|
|
13
|
+
}
|
|
4
14
|
export function resolveEnvelopeFormatOptions(cfg) {
|
|
5
15
|
const defaults = cfg?.agents?.defaults;
|
|
6
16
|
return {
|
|
@@ -20,15 +30,6 @@ function normalizeEnvelopeOptions(options) {
|
|
|
20
30
|
userTimezone: options?.userTimezone,
|
|
21
31
|
};
|
|
22
32
|
}
|
|
23
|
-
function resolveExplicitTimezone(value) {
|
|
24
|
-
try {
|
|
25
|
-
new Intl.DateTimeFormat("en-US", { timeZone: value }).format(new Date());
|
|
26
|
-
return value;
|
|
27
|
-
}
|
|
28
|
-
catch {
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
33
|
function resolveEnvelopeTimezone(options) {
|
|
33
34
|
const trimmed = options.timezone?.trim();
|
|
34
35
|
if (!trimmed)
|
|
@@ -41,42 +42,9 @@ function resolveEnvelopeTimezone(options) {
|
|
|
41
42
|
if (lowered === "user") {
|
|
42
43
|
return { mode: "iana", timeZone: resolveUserTimezone(options.userTimezone) };
|
|
43
44
|
}
|
|
44
|
-
const explicit =
|
|
45
|
+
const explicit = resolveTimezone(trimmed);
|
|
45
46
|
return explicit ? { mode: "iana", timeZone: explicit } : { mode: "utc" };
|
|
46
47
|
}
|
|
47
|
-
function formatUtcTimestamp(date) {
|
|
48
|
-
const yyyy = String(date.getUTCFullYear()).padStart(4, "0");
|
|
49
|
-
const mm = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
50
|
-
const dd = String(date.getUTCDate()).padStart(2, "0");
|
|
51
|
-
const hh = String(date.getUTCHours()).padStart(2, "0");
|
|
52
|
-
const min = String(date.getUTCMinutes()).padStart(2, "0");
|
|
53
|
-
return `${yyyy}-${mm}-${dd}T${hh}:${min}Z`;
|
|
54
|
-
}
|
|
55
|
-
function formatZonedTimestamp(date, timeZone) {
|
|
56
|
-
const parts = new Intl.DateTimeFormat("en-US", {
|
|
57
|
-
timeZone,
|
|
58
|
-
year: "numeric",
|
|
59
|
-
month: "2-digit",
|
|
60
|
-
day: "2-digit",
|
|
61
|
-
hour: "2-digit",
|
|
62
|
-
minute: "2-digit",
|
|
63
|
-
hourCycle: "h23",
|
|
64
|
-
timeZoneName: "short",
|
|
65
|
-
}).formatToParts(date);
|
|
66
|
-
const pick = (type) => parts.find((part) => part.type === type)?.value;
|
|
67
|
-
const yyyy = pick("year");
|
|
68
|
-
const mm = pick("month");
|
|
69
|
-
const dd = pick("day");
|
|
70
|
-
const hh = pick("hour");
|
|
71
|
-
const min = pick("minute");
|
|
72
|
-
const tz = [...parts]
|
|
73
|
-
.reverse()
|
|
74
|
-
.find((part) => part.type === "timeZoneName")
|
|
75
|
-
?.value?.trim();
|
|
76
|
-
if (!yyyy || !mm || !dd || !hh || !min)
|
|
77
|
-
return undefined;
|
|
78
|
-
return `${yyyy}-${mm}-${dd} ${hh}:${min}${tz ? ` ${tz}` : ""}`;
|
|
79
|
-
}
|
|
80
48
|
function formatTimestamp(ts, options) {
|
|
81
49
|
if (!ts)
|
|
82
50
|
return undefined;
|
|
@@ -87,48 +55,57 @@ function formatTimestamp(ts, options) {
|
|
|
87
55
|
if (Number.isNaN(date.getTime()))
|
|
88
56
|
return undefined;
|
|
89
57
|
const zone = resolveEnvelopeTimezone(resolved);
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
58
|
+
let weekday;
|
|
59
|
+
try {
|
|
60
|
+
weekday = new Intl.DateTimeFormat("en-US", {
|
|
61
|
+
weekday: "short",
|
|
62
|
+
...(zone.mode === "iana" ? { timeZone: zone.timeZone } : {}),
|
|
63
|
+
...(zone.mode === "utc" ? { timeZone: "UTC" } : {}),
|
|
64
|
+
}).format(date);
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
weekday = undefined;
|
|
68
|
+
}
|
|
69
|
+
let formatted;
|
|
70
|
+
if (zone.mode === "utc") {
|
|
71
|
+
formatted = formatUtcTimestamp(date);
|
|
72
|
+
}
|
|
73
|
+
else if (zone.mode === "local") {
|
|
74
|
+
formatted = formatZonedTimestamp(date);
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
formatted = formatZonedTimestamp(date, { timeZone: zone.timeZone });
|
|
78
|
+
}
|
|
79
|
+
if (!formatted)
|
|
99
80
|
return undefined;
|
|
100
|
-
|
|
101
|
-
if (seconds < 60)
|
|
102
|
-
return `${seconds}s`;
|
|
103
|
-
const minutes = Math.floor(seconds / 60);
|
|
104
|
-
if (minutes < 60)
|
|
105
|
-
return `${minutes}m`;
|
|
106
|
-
const hours = Math.floor(minutes / 60);
|
|
107
|
-
if (hours < 24)
|
|
108
|
-
return `${hours}h`;
|
|
109
|
-
const days = Math.floor(hours / 24);
|
|
110
|
-
return `${days}d`;
|
|
81
|
+
return weekday ? `${weekday} ${formatted}` : formatted;
|
|
111
82
|
}
|
|
112
83
|
export function formatAgentEnvelope(params) {
|
|
113
|
-
const channel = params.channel?.trim() || "Channel";
|
|
84
|
+
const channel = sanitizeEnvelopeHeaderPart(params.channel?.trim() || "Channel");
|
|
114
85
|
const parts = [channel];
|
|
115
86
|
const resolved = normalizeEnvelopeOptions(params.envelope);
|
|
116
|
-
|
|
117
|
-
|
|
87
|
+
let elapsed;
|
|
88
|
+
if (resolved.includeElapsed && params.timestamp && params.previousTimestamp) {
|
|
89
|
+
const curMs = params.timestamp instanceof Date ? params.timestamp.getTime() : params.timestamp;
|
|
90
|
+
const prevMs = params.previousTimestamp instanceof Date
|
|
118
91
|
? params.previousTimestamp.getTime()
|
|
119
|
-
: params.previousTimestamp
|
|
120
|
-
|
|
92
|
+
: params.previousTimestamp;
|
|
93
|
+
const elapsedMs = curMs - prevMs;
|
|
94
|
+
if (Number.isFinite(elapsedMs) && elapsedMs >= 0) {
|
|
95
|
+
elapsed = formatTimeAgo(elapsedMs, { suffix: false });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
121
98
|
if (params.from?.trim()) {
|
|
122
|
-
const from = params.from.trim();
|
|
99
|
+
const from = sanitizeEnvelopeHeaderPart(params.from.trim());
|
|
123
100
|
parts.push(elapsed ? `${from} +${elapsed}` : from);
|
|
124
101
|
}
|
|
125
102
|
else if (elapsed) {
|
|
126
103
|
parts.push(`+${elapsed}`);
|
|
127
104
|
}
|
|
128
105
|
if (params.host?.trim())
|
|
129
|
-
parts.push(params.host.trim());
|
|
106
|
+
parts.push(sanitizeEnvelopeHeaderPart(params.host.trim()));
|
|
130
107
|
if (params.ip?.trim())
|
|
131
|
-
parts.push(params.ip.trim());
|
|
108
|
+
parts.push(sanitizeEnvelopeHeaderPart(params.ip.trim()));
|
|
132
109
|
const ts = formatTimestamp(params.timestamp, resolved);
|
|
133
110
|
if (ts)
|
|
134
111
|
parts.push(ts);
|
|
@@ -138,7 +115,8 @@ export function formatAgentEnvelope(params) {
|
|
|
138
115
|
export function formatInboundEnvelope(params) {
|
|
139
116
|
const chatType = normalizeChatType(params.chatType);
|
|
140
117
|
const isDirect = !chatType || chatType === "direct";
|
|
141
|
-
const
|
|
118
|
+
const resolvedSenderRaw = params.senderLabel?.trim() || resolveSenderLabel(params.sender ?? {});
|
|
119
|
+
const resolvedSender = resolvedSenderRaw ? sanitizeEnvelopeHeaderPart(resolvedSenderRaw) : "";
|
|
142
120
|
const body = !isDirect && resolvedSender ? `${resolvedSender}: ${params.body}` : params.body;
|
|
143
121
|
return formatAgentEnvelope({
|
|
144
122
|
channel: params.channel,
|
|
@@ -70,6 +70,7 @@ export const handleCompactCommand = async (params) => {
|
|
|
70
70
|
defaultLevel: "off",
|
|
71
71
|
},
|
|
72
72
|
customInstructions,
|
|
73
|
+
senderIsOwner: params.command.senderIsOwner,
|
|
73
74
|
ownerNumbers: params.command.ownerList.length > 0 ? params.command.ownerList : undefined,
|
|
74
75
|
});
|
|
75
76
|
const compactLabel = result.ok
|
|
@@ -75,6 +75,7 @@ async function resolveContextReport(params) {
|
|
|
75
75
|
groupChannel: params.sessionEntry?.groupChannel ?? undefined,
|
|
76
76
|
groupSpace: params.sessionEntry?.space ?? undefined,
|
|
77
77
|
spawnedBy: params.sessionEntry?.spawnedBy ?? undefined,
|
|
78
|
+
senderIsOwner: params.command.senderIsOwner,
|
|
78
79
|
modelProvider: params.provider,
|
|
79
80
|
modelId: params.model,
|
|
80
81
|
});
|
|
@@ -1,75 +1,28 @@
|
|
|
1
1
|
import { loadModelCatalog } from "../../agents/model-catalog.js";
|
|
2
2
|
import { buildAllowedModelSet, buildModelAliasIndex, normalizeProviderId, resolveConfiguredModelRef, resolveModelRefFromString, } from "../../agents/model-selection.js";
|
|
3
3
|
import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../agents/defaults.js";
|
|
4
|
+
import { buildModelsKeyboard, buildProviderKeyboard, calculateTotalPages, getModelsPageSize, } from "../../telegram/model-buttons.js";
|
|
4
5
|
const PAGE_SIZE_DEFAULT = 20;
|
|
5
6
|
const PAGE_SIZE_MAX = 100;
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
if (!trimmed) {
|
|
12
|
-
return { page: 1, pageSize: PAGE_SIZE_DEFAULT, all: false };
|
|
13
|
-
}
|
|
14
|
-
const tokens = trimmed.split(/\s+/g).filter(Boolean);
|
|
15
|
-
const provider = tokens[0]?.trim();
|
|
16
|
-
let page = 1;
|
|
17
|
-
let all = false;
|
|
18
|
-
for (const token of tokens.slice(1)) {
|
|
19
|
-
const lower = token.toLowerCase();
|
|
20
|
-
if (lower === "all" || lower === "--all") {
|
|
21
|
-
all = true;
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
if (lower.startsWith("page=")) {
|
|
25
|
-
const value = Number.parseInt(lower.slice("page=".length), 10);
|
|
26
|
-
if (Number.isFinite(value) && value > 0)
|
|
27
|
-
page = value;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (/^[0-9]+$/.test(lower)) {
|
|
31
|
-
const value = Number.parseInt(lower, 10);
|
|
32
|
-
if (Number.isFinite(value) && value > 0)
|
|
33
|
-
page = value;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
let pageSize = PAGE_SIZE_DEFAULT;
|
|
37
|
-
for (const token of tokens) {
|
|
38
|
-
const lower = token.toLowerCase();
|
|
39
|
-
if (lower.startsWith("limit=") || lower.startsWith("size=")) {
|
|
40
|
-
const rawValue = lower.slice(lower.indexOf("=") + 1);
|
|
41
|
-
const value = Number.parseInt(rawValue, 10);
|
|
42
|
-
if (Number.isFinite(value) && value > 0)
|
|
43
|
-
pageSize = Math.min(PAGE_SIZE_MAX, value);
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return {
|
|
47
|
-
provider: provider ? normalizeProviderId(provider) : undefined,
|
|
48
|
-
page,
|
|
49
|
-
pageSize,
|
|
50
|
-
all,
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
export async function resolveModelsCommandReply(params) {
|
|
54
|
-
const body = params.commandBodyNormalized.trim();
|
|
55
|
-
if (!body.startsWith("/models"))
|
|
56
|
-
return null;
|
|
57
|
-
const argText = body.replace(/^\/models\b/i, "").trim();
|
|
58
|
-
const { provider, page, pageSize, all } = parseModelsArgs(argText);
|
|
7
|
+
/**
|
|
8
|
+
* Build provider/model data from config and catalog.
|
|
9
|
+
* Exported for reuse by callback handlers.
|
|
10
|
+
*/
|
|
11
|
+
export async function buildModelsProviderData(cfg) {
|
|
59
12
|
const resolvedDefault = resolveConfiguredModelRef({
|
|
60
|
-
cfg
|
|
13
|
+
cfg,
|
|
61
14
|
defaultProvider: DEFAULT_PROVIDER,
|
|
62
15
|
defaultModel: DEFAULT_MODEL,
|
|
63
16
|
});
|
|
64
|
-
const catalog = await loadModelCatalog({ config:
|
|
17
|
+
const catalog = await loadModelCatalog({ config: cfg });
|
|
65
18
|
const allowed = buildAllowedModelSet({
|
|
66
|
-
cfg
|
|
19
|
+
cfg,
|
|
67
20
|
catalog,
|
|
68
21
|
defaultProvider: resolvedDefault.provider,
|
|
69
22
|
defaultModel: resolvedDefault.model,
|
|
70
23
|
});
|
|
71
24
|
const aliasIndex = buildModelAliasIndex({
|
|
72
|
-
cfg
|
|
25
|
+
cfg,
|
|
73
26
|
defaultProvider: resolvedDefault.provider,
|
|
74
27
|
});
|
|
75
28
|
const byProvider = new Map();
|
|
@@ -93,7 +46,7 @@ export async function resolveModelsCommandReply(params) {
|
|
|
93
46
|
add(resolved.ref.provider, resolved.ref.model);
|
|
94
47
|
};
|
|
95
48
|
const addModelConfigEntries = () => {
|
|
96
|
-
const modelConfig =
|
|
49
|
+
const modelConfig = cfg.agents?.defaults?.model;
|
|
97
50
|
if (typeof modelConfig === "string") {
|
|
98
51
|
addRawModelRef(modelConfig);
|
|
99
52
|
}
|
|
@@ -103,7 +56,7 @@ export async function resolveModelsCommandReply(params) {
|
|
|
103
56
|
addRawModelRef(fallback);
|
|
104
57
|
}
|
|
105
58
|
}
|
|
106
|
-
const imageConfig =
|
|
59
|
+
const imageConfig = cfg.agents?.defaults?.imageModel;
|
|
107
60
|
if (typeof imageConfig === "string") {
|
|
108
61
|
addRawModelRef(imageConfig);
|
|
109
62
|
}
|
|
@@ -118,7 +71,7 @@ export async function resolveModelsCommandReply(params) {
|
|
|
118
71
|
add(entry.provider, entry.id);
|
|
119
72
|
}
|
|
120
73
|
// Include config-only allowlist keys that aren't in the curated catalog.
|
|
121
|
-
for (const raw of Object.keys(
|
|
74
|
+
for (const raw of Object.keys(cfg.agents?.defaults?.models ?? {})) {
|
|
122
75
|
addRawModelRef(raw);
|
|
123
76
|
}
|
|
124
77
|
// Ensure configured defaults/fallbacks/image models show up even when the
|
|
@@ -126,7 +79,79 @@ export async function resolveModelsCommandReply(params) {
|
|
|
126
79
|
add(resolvedDefault.provider, resolvedDefault.model);
|
|
127
80
|
addModelConfigEntries();
|
|
128
81
|
const providers = [...byProvider.keys()].sort();
|
|
82
|
+
return { byProvider, providers, resolvedDefault };
|
|
83
|
+
}
|
|
84
|
+
function formatProviderLine(params) {
|
|
85
|
+
return `- ${params.provider} (${params.count})`;
|
|
86
|
+
}
|
|
87
|
+
function parseModelsArgs(raw) {
|
|
88
|
+
const trimmed = raw.trim();
|
|
89
|
+
if (!trimmed) {
|
|
90
|
+
return { page: 1, pageSize: PAGE_SIZE_DEFAULT, all: false };
|
|
91
|
+
}
|
|
92
|
+
const tokens = trimmed.split(/\s+/g).filter(Boolean);
|
|
93
|
+
const provider = tokens[0]?.trim();
|
|
94
|
+
let page = 1;
|
|
95
|
+
let all = false;
|
|
96
|
+
for (const token of tokens.slice(1)) {
|
|
97
|
+
const lower = token.toLowerCase();
|
|
98
|
+
if (lower === "all" || lower === "--all") {
|
|
99
|
+
all = true;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (lower.startsWith("page=")) {
|
|
103
|
+
const value = Number.parseInt(lower.slice("page=".length), 10);
|
|
104
|
+
if (Number.isFinite(value) && value > 0)
|
|
105
|
+
page = value;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (/^[0-9]+$/.test(lower)) {
|
|
109
|
+
const value = Number.parseInt(lower, 10);
|
|
110
|
+
if (Number.isFinite(value) && value > 0)
|
|
111
|
+
page = value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
let pageSize = PAGE_SIZE_DEFAULT;
|
|
115
|
+
for (const token of tokens) {
|
|
116
|
+
const lower = token.toLowerCase();
|
|
117
|
+
if (lower.startsWith("limit=") || lower.startsWith("size=")) {
|
|
118
|
+
const rawValue = lower.slice(lower.indexOf("=") + 1);
|
|
119
|
+
const value = Number.parseInt(rawValue, 10);
|
|
120
|
+
if (Number.isFinite(value) && value > 0)
|
|
121
|
+
pageSize = Math.min(PAGE_SIZE_MAX, value);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
provider: provider ? normalizeProviderId(provider) : undefined,
|
|
126
|
+
page,
|
|
127
|
+
pageSize,
|
|
128
|
+
all,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export async function resolveModelsCommandReply(params) {
|
|
132
|
+
const body = params.commandBodyNormalized.trim();
|
|
133
|
+
if (!body.startsWith("/models"))
|
|
134
|
+
return null;
|
|
135
|
+
const argText = body.replace(/^\/models\b/i, "").trim();
|
|
136
|
+
const { provider, page, pageSize, all } = parseModelsArgs(argText);
|
|
137
|
+
const { byProvider, providers } = await buildModelsProviderData(params.cfg);
|
|
138
|
+
const isTelegram = params.surface === "telegram";
|
|
139
|
+
// Provider list (no provider specified)
|
|
129
140
|
if (!provider) {
|
|
141
|
+
// For Telegram: show buttons if there are providers
|
|
142
|
+
if (isTelegram && providers.length > 0) {
|
|
143
|
+
const providerInfos = providers.map((p) => ({
|
|
144
|
+
id: p,
|
|
145
|
+
count: byProvider.get(p)?.size ?? 0,
|
|
146
|
+
}));
|
|
147
|
+
const buttons = buildProviderKeyboard(providerInfos);
|
|
148
|
+
const text = "Select a provider:";
|
|
149
|
+
return {
|
|
150
|
+
text,
|
|
151
|
+
channelData: { telegram: { buttons } },
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// Text fallback for non-Telegram surfaces
|
|
130
155
|
const lines = [
|
|
131
156
|
"Providers:",
|
|
132
157
|
...providers.map((p) => formatProviderLine({ provider: p, count: byProvider.get(p)?.size ?? 0 })),
|
|
@@ -158,6 +183,26 @@ export async function resolveModelsCommandReply(params) {
|
|
|
158
183
|
];
|
|
159
184
|
return { text: lines.join("\n") };
|
|
160
185
|
}
|
|
186
|
+
// For Telegram: use button-based model list with inline keyboard pagination
|
|
187
|
+
if (isTelegram) {
|
|
188
|
+
const telegramPageSize = getModelsPageSize();
|
|
189
|
+
const totalPages = calculateTotalPages(total, telegramPageSize);
|
|
190
|
+
const safePage = Math.max(1, Math.min(page, totalPages));
|
|
191
|
+
const buttons = buildModelsKeyboard({
|
|
192
|
+
provider,
|
|
193
|
+
models,
|
|
194
|
+
currentModel: params.currentModel,
|
|
195
|
+
currentPage: safePage,
|
|
196
|
+
totalPages,
|
|
197
|
+
pageSize: telegramPageSize,
|
|
198
|
+
});
|
|
199
|
+
const text = `Models (${provider}) — ${total} available`;
|
|
200
|
+
return {
|
|
201
|
+
text,
|
|
202
|
+
channelData: { telegram: { buttons } },
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
// Text fallback for non-Telegram surfaces
|
|
161
206
|
const effectivePageSize = all ? total : pageSize;
|
|
162
207
|
const pageCount = effectivePageSize > 0 ? Math.ceil(total / effectivePageSize) : 1;
|
|
163
208
|
const safePage = all ? 1 : Math.max(1, Math.min(page, pageCount));
|
|
@@ -194,6 +239,8 @@ export const handleModelsCommand = async (params, allowTextCommands) => {
|
|
|
194
239
|
const reply = await resolveModelsCommandReply({
|
|
195
240
|
cfg: params.cfg,
|
|
196
241
|
commandBodyNormalized: params.command.commandBodyNormalized,
|
|
242
|
+
surface: params.ctx.Surface,
|
|
243
|
+
currentModel: params.model ? `${params.provider}/${params.model}` : undefined,
|
|
197
244
|
});
|
|
198
245
|
if (!reply)
|
|
199
246
|
return null;
|