@nordbyte/nordrelay 0.8.0 → 0.8.2
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/.env.example +9 -0
- package/README.md +81 -1197
- package/dist/{access-control.js → access/access-control.js} +1 -1
- package/dist/{audit-log.js → access/audit-log.js} +2 -2
- package/dist/{session-locks.js → access/session-locks.js} +1 -1
- package/dist/{user-management.js → access/user-management.js} +1 -1
- package/dist/{claude-code-cli.js → agents/claude-code/claude-code-cli.js} +2 -2
- package/dist/{claude-code-session.js → agents/claude-code/claude-code-session.js} +1 -1
- package/dist/{codex-cli.js → agents/codex/codex-cli.js} +14 -5
- package/dist/{codex-session.js → agents/codex/codex-session.js} +2 -4
- package/dist/{hermes-cli.js → agents/hermes/hermes-cli.js} +2 -2
- package/dist/{hermes-launch.js → agents/hermes/hermes-launch.js} +1 -1
- package/dist/{hermes-session.js → agents/hermes/hermes-session.js} +1 -1
- package/dist/{openclaw-cli.js → agents/openclaw/openclaw-cli.js} +2 -2
- package/dist/{openclaw-launch.js → agents/openclaw/openclaw-launch.js} +1 -1
- package/dist/{openclaw-session.js → agents/openclaw/openclaw-session.js} +1 -1
- package/dist/{pi-cli.js → agents/pi/pi-cli.js} +2 -2
- package/dist/{pi-launch.js → agents/pi/pi-launch.js} +1 -1
- package/dist/{pi-session.js → agents/pi/pi-session.js} +1 -1
- package/dist/{adapter-conformance.js → agents/shared/adapter-conformance.js} +2 -2
- package/dist/{agent-activity.js → agents/shared/agent-activity.js} +5 -5
- package/dist/agents/shared/agent-auth-commands.js +30 -0
- package/dist/{agent-factory.js → agents/shared/agent-factory.js} +5 -5
- package/dist/{agent-feature-matrix.js → agents/shared/agent-feature-matrix.js} +2 -2
- package/dist/{agent-updates.js → agents/shared/agent-updates.js} +7 -7
- package/dist/{discord-artifacts.js → channels/discord/discord-artifacts.js} +4 -4
- package/dist/{discord-bot.js → channels/discord/discord-bot.js} +164 -424
- package/dist/{discord-channel-runtime.js → channels/discord/discord-channel-runtime.js} +2 -2
- package/dist/{discord-command-surface.js → channels/discord/discord-command-surface.js} +3 -3
- package/dist/{bot-rendering.js → channels/shared/bot-rendering.js} +6 -6
- package/dist/{channel-actions.js → channels/shared/channel-actions.js} +4 -4
- package/dist/channels/shared/channel-bridge-controller.js +69 -0
- package/dist/channels/shared/channel-cli-artifacts.js +51 -0
- package/dist/{channel-command-service.js → channels/shared/channel-command-service.js} +51 -28
- package/dist/channels/shared/channel-external-mirror-controller.js +193 -0
- package/dist/channels/shared/channel-external-monitor.js +52 -0
- package/dist/{channel-mirror-registry.js → channels/shared/channel-mirror-registry.js} +14 -6
- package/dist/{channel-peer-prompt.js → channels/shared/channel-peer-prompt.js} +3 -3
- package/dist/{channel-turn-service.js → channels/shared/channel-turn-service.js} +2 -2
- package/dist/{context-key.js → channels/shared/context-key.js} +1 -1
- package/dist/{session-format.js → channels/shared/session-format.js} +2 -2
- package/dist/{slack-artifacts.js → channels/slack/slack-artifacts.js} +4 -4
- package/dist/{slack-bot.js → channels/slack/slack-bot.js} +159 -294
- package/dist/{slack-channel-runtime.js → channels/slack/slack-channel-runtime.js} +2 -2
- package/dist/{slack-command-surface.js → channels/slack/slack-command-surface.js} +2 -2
- package/dist/{slack-diagnostics.js → channels/slack/slack-diagnostics.js} +2 -2
- package/dist/{bot-ui.js → channels/telegram/bot-ui.js} +1 -1
- package/dist/{bot.js → channels/telegram/bot.js} +178 -427
- package/dist/{telegram-access-commands.js → channels/telegram/telegram-access-commands.js} +3 -3
- package/dist/{telegram-access-middleware.js → channels/telegram/telegram-access-middleware.js} +4 -4
- package/dist/{telegram-agent-commands.js → channels/telegram/telegram-agent-commands.js} +9 -9
- package/dist/{telegram-artifact-commands.js → channels/telegram/telegram-artifact-commands.js} +4 -4
- package/dist/{telegram-channel-runtime.js → channels/telegram/telegram-channel-runtime.js} +2 -2
- package/dist/{telegram-command-menu.js → channels/telegram/telegram-command-menu.js} +1 -1
- package/dist/{telegram-diagnostics-command.js → channels/telegram/telegram-diagnostics-command.js} +7 -7
- package/dist/{telegram-general-commands.js → channels/telegram/telegram-general-commands.js} +4 -4
- package/dist/{telegram-operational-commands.js → channels/telegram/telegram-operational-commands.js} +5 -5
- package/dist/{telegram-output.js → channels/telegram/telegram-output.js} +2 -2
- package/dist/{telegram-preference-commands.js → channels/telegram/telegram-preference-commands.js} +3 -3
- package/dist/{telegram-queue-commands.js → channels/telegram/telegram-queue-commands.js} +6 -6
- package/dist/{telegram-support-command.js → channels/telegram/telegram-support-command.js} +4 -4
- package/dist/{telegram-update-commands.js → channels/telegram/telegram-update-commands.js} +5 -5
- package/dist/{config-metadata.js → core/config-metadata.js} +8 -0
- package/dist/{config.js → core/config.js} +11 -3
- package/dist/index.js +27 -23
- package/dist/{peer-client.js → peers/peer-client.js} +57 -1
- package/dist/peers/peer-discovery-jobs.js +206 -0
- package/dist/peers/peer-discovery.js +223 -0
- package/dist/peers/peer-health-monitor.js +49 -0
- package/dist/{peer-identity.js → peers/peer-identity.js} +50 -1
- package/dist/{peer-runtime-service.js → peers/peer-runtime-service.js} +29 -7
- package/dist/{peer-server.js → peers/peer-server.js} +23 -6
- package/dist/{peer-store.js → peers/peer-store.js} +84 -11
- package/dist/{peer-types.js → peers/peer-types.js} +9 -0
- package/dist/peers/peer-web-proxy-contract.js +127 -0
- package/dist/{metrics.js → runtime/metrics.js} +5 -3
- package/dist/{relay-artifact-service.js → runtime/relay-artifact-service.js} +1 -1
- package/dist/runtime/relay-auth-service.js +63 -0
- package/dist/runtime/relay-dashboard-service.js +139 -0
- package/dist/{relay-external-activity-monitor.js → runtime/relay-external-activity-monitor.js} +140 -53
- package/dist/runtime/relay-runtime-active-sessions.js +387 -0
- package/dist/runtime/relay-runtime-dashboard.js +201 -0
- package/dist/runtime/relay-runtime-prompt-queue-artifacts.js +307 -0
- package/dist/runtime/relay-runtime-sessions.js +623 -0
- package/dist/runtime/relay-runtime-types.js +1 -0
- package/dist/runtime/relay-runtime-updates-jobs.js +360 -0
- package/dist/runtime/relay-runtime.js +451 -0
- package/dist/runtime/runtime-cache.js +117 -0
- package/dist/{session-registry.js → state/session-registry.js} +3 -3
- package/dist/{operations.js → support/operations.js} +7 -7
- package/dist/{support-bundle.js → support/support-bundle.js} +1 -1
- package/dist/{web-api-contract.js → web/web-api-contract.js} +17 -3
- package/dist/web/web-api-types.js +1 -0
- package/dist/{web-dashboard-access-routes.js → web/web-dashboard-access-routes.js} +2 -2
- package/dist/{web-dashboard-assets.js → web/web-dashboard-assets.js} +24 -2
- package/dist/{web-dashboard-http.js → web/web-dashboard-http.js} +41 -5
- package/dist/{web-dashboard-pages.js → web/web-dashboard-pages.js} +37 -10
- package/dist/{web-dashboard-peer-routes.js → web/web-dashboard-peer-routes.js} +102 -7
- package/dist/web/web-dashboard-security.js +14 -0
- package/dist/{web-dashboard-session-routes.js → web/web-dashboard-session-routes.js} +12 -1
- package/dist/{web-dashboard.js → web/web-dashboard.js} +132 -48
- package/dist/web/web-performance.js +60 -0
- package/dist/web/web-rate-limit.js +19 -0
- package/dist/{web-state.js → web/web-state.js} +74 -5
- package/dist/webui-assets/dashboard.css +171 -10
- package/dist/webui-assets/dashboard.js +515 -48
- package/dist/webui-assets/favicon.ico +0 -0
- package/dist/webui-assets/favicon.png +0 -0
- package/dist/webui-assets/logo.png +0 -0
- package/package.json +4 -3
- package/plugins/nordrelay/scripts/nordrelay.mjs +17 -5
- package/{launchd/start.sh → scripts/launchd-start.sh} +1 -1
- package/dist/relay-runtime.js +0 -1916
- package/dist/runtime-cache.js +0 -57
- /package/dist/{user-management-crypto.js → access/user-management-crypto.js} +0 -0
- /package/dist/{user-management-normalize.js → access/user-management-normalize.js} +0 -0
- /package/dist/{user-management-types.js → access/user-management-types.js} +0 -0
- /package/dist/{claude-code-auth.js → agents/claude-code/claude-code-auth.js} +0 -0
- /package/dist/{claude-code-launch.js → agents/claude-code/claude-code-launch.js} +0 -0
- /package/dist/{claude-code-state.js → agents/claude-code/claude-code-state.js} +0 -0
- /package/dist/{codex-auth.js → agents/codex/codex-auth.js} +0 -0
- /package/dist/{codex-config.js → agents/codex/codex-config.js} +0 -0
- /package/dist/{codex-launch.js → agents/codex/codex-launch.js} +0 -0
- /package/dist/{codex-state.js → agents/codex/codex-state.js} +0 -0
- /package/dist/{hermes-api.js → agents/hermes/hermes-api.js} +0 -0
- /package/dist/{hermes-auth.js → agents/hermes/hermes-auth.js} +0 -0
- /package/dist/{hermes-state.js → agents/hermes/hermes-state.js} +0 -0
- /package/dist/{openclaw-auth.js → agents/openclaw/openclaw-auth.js} +0 -0
- /package/dist/{openclaw-gateway.js → agents/openclaw/openclaw-gateway.js} +0 -0
- /package/dist/{openclaw-state.js → agents/openclaw/openclaw-state.js} +0 -0
- /package/dist/{pi-auth.js → agents/pi/pi-auth.js} +0 -0
- /package/dist/{pi-rpc.js → agents/pi/pi-rpc.js} +0 -0
- /package/dist/{pi-state.js → agents/pi/pi-state.js} +0 -0
- /package/dist/{agent-adapter.js → agents/shared/agent-adapter.js} +0 -0
- /package/dist/{agent.js → agents/shared/agent.js} +0 -0
- /package/dist/{artifacts.js → artifacts/artifacts.js} +0 -0
- /package/dist/{attachments.js → artifacts/attachments.js} +0 -0
- /package/dist/{voice.js → artifacts/voice.js} +0 -0
- /package/dist/{discord-rate-limit.js → channels/discord/discord-rate-limit.js} +0 -0
- /package/dist/{channel-adapter.js → channels/shared/channel-adapter.js} +0 -0
- /package/dist/{relay-runtime-types.js → channels/shared/channel-bridge-state.js} +0 -0
- /package/dist/{channel-command-catalog.js → channels/shared/channel-command-catalog.js} +0 -0
- /package/dist/{channel-command-core.js → channels/shared/channel-command-core.js} +0 -0
- /package/dist/{channel-prompt-engine.js → channels/shared/channel-prompt-engine.js} +0 -0
- /package/dist/{channel-runtime.js → channels/shared/channel-runtime.js} +0 -0
- /package/dist/{channel-turn-lifecycle.js → channels/shared/channel-turn-lifecycle.js} +0 -0
- /package/dist/{slack-rate-limit.js → channels/slack/slack-rate-limit.js} +0 -0
- /package/dist/{telegram-command-types.js → channels/telegram/telegram-command-types.js} +0 -0
- /package/dist/{telegram-rate-limit.js → channels/telegram/telegram-rate-limit.js} +0 -0
- /package/dist/{activity-events.js → core/activity-events.js} +0 -0
- /package/dist/{error-messages.js → core/error-messages.js} +0 -0
- /package/dist/{format.js → core/format.js} +0 -0
- /package/dist/{logger.js → core/logger.js} +0 -0
- /package/dist/{redaction.js → core/redaction.js} +0 -0
- /package/dist/{settings-service.js → core/settings-service.js} +0 -0
- /package/dist/{settings-wizard-test.js → core/settings-wizard-test.js} +0 -0
- /package/dist/{workspace-policy.js → core/workspace-policy.js} +0 -0
- /package/dist/{peer-auth.js → peers/peer-auth.js} +0 -0
- /package/dist/{peer-context.js → peers/peer-context.js} +0 -0
- /package/dist/{peer-readiness.js → peers/peer-readiness.js} +0 -0
- /package/dist/{relay-queue-service.js → runtime/relay-queue-service.js} +0 -0
- /package/dist/{web-api-types.js → runtime/relay-runtime-delegate.js} +0 -0
- /package/dist/{relay-runtime-helpers.js → runtime/relay-runtime-helpers.js} +0 -0
- /package/dist/{remote-prompt.js → runtime/remote-prompt.js} +0 -0
- /package/dist/{bot-preferences.js → state/bot-preferences.js} +0 -0
- /package/dist/{job-store.js → state/job-store.js} +0 -0
- /package/dist/{persistence.js → state/persistence.js} +0 -0
- /package/dist/{prompt-store.js → state/prompt-store.js} +0 -0
- /package/dist/{state-backend.js → state/state-backend.js} +0 -0
- /package/dist/{zip-writer.js → support/zip-writer.js} +0 -0
- /package/dist/{web-dashboard-artifact-routes.js → web/web-dashboard-artifact-routes.js} +0 -0
- /package/dist/{web-dashboard-runtime-routes.js → web/web-dashboard-runtime-routes.js} +0 -0
- /package/dist/{web-dashboard-ui.js → web/web-dashboard-ui.js} +0 -0
|
@@ -3,36 +3,38 @@ import { readFile, unlink } from "node:fs/promises";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { autoRetry } from "@grammyjs/auto-retry";
|
|
5
5
|
import { Bot, InlineKeyboard, InputFile } from "grammy";
|
|
6
|
-
import { ADMIN_GROUP_ID } from "
|
|
7
|
-
import { buildFileInstructions, outboxPath, stageFile, } from "
|
|
8
|
-
import { collectArtifactReport,
|
|
9
|
-
import { AgentUpdateManager } from "
|
|
10
|
-
import { AuditLogStore } from "
|
|
6
|
+
import { ADMIN_GROUP_ID } from "../../access/access-control.js";
|
|
7
|
+
import { buildFileInstructions, outboxPath, stageFile, } from "../../artifacts/attachments.js";
|
|
8
|
+
import { collectArtifactReport, createArtifactZipBundle, ensureOutDir, formatArtifactSummary, isTelegramImagePreview, pruneConnectorTurnDirs, telegramArtifactFilename, totalArtifactSize, } from "../../artifacts/artifacts.js";
|
|
9
|
+
import { AgentUpdateManager } from "../../agents/shared/agent-updates.js";
|
|
10
|
+
import { AuditLogStore } from "../../access/audit-log.js";
|
|
11
11
|
import { formatSessionLabel } from "./bot-ui.js";
|
|
12
|
-
import { BotPreferencesStore, isQuietNow, } from "
|
|
13
|
-
import { renderAgentUpdateJobAction } from "
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
12
|
+
import { BotPreferencesStore, isQuietNow, } from "../../state/bot-preferences.js";
|
|
13
|
+
import { renderAgentUpdateJobAction } from "../shared/channel-actions.js";
|
|
14
|
+
import { createChannelBusyStore } from "../shared/channel-bridge-controller.js";
|
|
15
|
+
import { ChannelCommandService } from "../shared/channel-command-service.js";
|
|
16
|
+
import { runChannelPeerPrompt } from "../shared/channel-peer-prompt.js";
|
|
17
|
+
import { deliverChannelAction } from "../shared/channel-runtime.js";
|
|
18
|
+
import { deliverChannelCliArtifacts } from "../shared/channel-cli-artifacts.js";
|
|
19
|
+
import { createChannelExternalMirrorController } from "../shared/channel-external-mirror-controller.js";
|
|
20
|
+
import { monitorChannelExternalContexts } from "../shared/channel-external-monitor.js";
|
|
21
|
+
import { createChannelTurnLifecycle, createChannelTypingLoop } from "../shared/channel-turn-lifecycle.js";
|
|
22
|
+
import { agentLabel, agentReasoningLabel, agentReasoningOptions, } from "../../agents/shared/agent.js";
|
|
23
|
+
import { agentIdForAuth as resolveAgentIdForAuth, agentLabelForAuth, hostAgentLoginCommand, hostAgentLogoutCommand, } from "../../agents/shared/agent-auth-commands.js";
|
|
24
|
+
import { getExternalActivityForSession, } from "../../agents/shared/agent-activity.js";
|
|
25
|
+
import { checkAuthStatus, clearAuthCache, startLogin as startCodexLogin, startLogout as startCodexLogout } from "../../agents/codex/codex-auth.js";
|
|
26
|
+
import { formatLaunchProfileBehavior } from "../../agents/codex/codex-launch.js";
|
|
27
|
+
import { contextKeyFromCtx, isTelegramContextKey, isTopicContextKey, parseContextKey } from "../shared/context-key.js";
|
|
28
|
+
import { friendlyErrorText } from "../../core/error-messages.js";
|
|
29
|
+
import { escapeHTML } from "../../core/format.js";
|
|
30
|
+
import { PromptStore, toPromptEnvelope } from "../../state/prompt-store.js";
|
|
31
|
+
import { RemoteRelayClient } from "../../peers/peer-client.js";
|
|
32
|
+
import { RelayAuthService } from "../../runtime/relay-auth-service.js";
|
|
33
|
+
import { configureRedaction, redactText } from "../../core/redaction.js";
|
|
34
|
+
import { canWriteWithLock, SessionLockStore } from "../../access/session-locks.js";
|
|
35
|
+
import { renderSessionInfoHTML, renderSessionInfoPlain, } from "../shared/session-format.js";
|
|
36
|
+
import { SessionRegistry } from "../../state/session-registry.js";
|
|
37
|
+
import { transcribeAudio } from "../../artifacts/voice.js";
|
|
36
38
|
import { telegramRateLimiter } from "./telegram-rate-limit.js";
|
|
37
39
|
import { chatBucket, downloadTelegramFile, isMessageNotModifiedError, renderMarkdownChunkWithinLimit, safeEditMessage, safeEditReplyMarkup, safeReply, sendChatActionSafe, sendTextMessage, splitMarkdownForTelegram, } from "./telegram-output.js";
|
|
38
40
|
import { NOOP_PAGE_CALLBACK_DATA, TelegramBotChannelRuntime, paginateKeyboard, telegramChannelContextFromCtx, } from "./telegram-channel-runtime.js";
|
|
@@ -47,11 +49,11 @@ import { registerTelegramPreferenceCommands } from "./telegram-preference-comman
|
|
|
47
49
|
import { createQueuedPromptCancelKeyboard, registerTelegramQueueCommands, } from "./telegram-queue-commands.js";
|
|
48
50
|
import { registerTelegramSupportCommands } from "./telegram-support-command.js";
|
|
49
51
|
import { registerTelegramUpdateCommands } from "./telegram-update-commands.js";
|
|
50
|
-
import { appendWithCap, authHelpText, buildStreamingPreview, capabilitiesOf, filterSessions, formatAgentLaunchProfileLabel, formatAgentSettingScope, formatDurationSeconds, formatError, formatLocalDateTime, formatLockOwner, formatModelButtonLabel, formatRelativeTime, formatTelegramName, formatToolSummaryLine, formatTurnUsageLine, getWorkspaceShortName, idOf, isEmptyArtifactReport, isPromptEnvelopeLike, isQueuedPromptLike, labelOf, orderPinnedSessions, parseFastModeArgument,
|
|
51
|
-
import { UserStore } from "
|
|
52
|
-
import { WebActivityStore } from "
|
|
53
|
-
import { evaluateWorkspacePolicy, filterAllowedWorkspaces, renderWorkspacePolicyLine, } from "
|
|
54
|
-
export { formatToolSummaryLine, formatTurnUsageLine, summarizeToolName } from "
|
|
52
|
+
import { appendWithCap, authHelpText, buildStreamingPreview, capabilitiesOf, filterSessions, formatAgentLaunchProfileLabel, formatAgentSettingScope, formatDurationSeconds, formatError, formatLocalDateTime, formatLockOwner, formatModelButtonLabel, formatRelativeTime, formatTelegramName, formatToolSummaryLine, formatTurnUsageLine, getWorkspaceShortName, idOf, isEmptyArtifactReport, isPromptEnvelopeLike, isQueuedPromptLike, labelOf, orderPinnedSessions, parseFastModeArgument, renderPromptFailure, renderTodoList, renderToolEndMessage, renderToolStartMessage, requiresTurnApproval, trimLine, } from "../shared/bot-rendering.js";
|
|
53
|
+
import { UserStore } from "../../access/user-management.js";
|
|
54
|
+
import { WebActivityStore } from "../../web/web-state.js";
|
|
55
|
+
import { evaluateWorkspacePolicy, filterAllowedWorkspaces, renderWorkspacePolicyLine, } from "../../core/workspace-policy.js";
|
|
56
|
+
export { formatToolSummaryLine, formatTurnUsageLine, summarizeToolName } from "../shared/bot-rendering.js";
|
|
55
57
|
export { registerCommands } from "./telegram-command-menu.js";
|
|
56
58
|
const EDIT_DEBOUNCE_MS = 1500;
|
|
57
59
|
const TYPING_INTERVAL_MS = 4500;
|
|
@@ -73,7 +75,12 @@ export function createBot(config, registry) {
|
|
|
73
75
|
const bot = new Bot(config.telegramBotToken);
|
|
74
76
|
bot.api.config.use(autoRetry({ maxRetryAttempts: 3, maxDelaySeconds: 10 }));
|
|
75
77
|
const telegramChannelRuntime = new TelegramBotChannelRuntime(bot);
|
|
76
|
-
const contextBusy =
|
|
78
|
+
const contextBusy = createChannelBusyStore(() => ({
|
|
79
|
+
processing: false,
|
|
80
|
+
switching: false,
|
|
81
|
+
transcribing: false,
|
|
82
|
+
approving: false,
|
|
83
|
+
}));
|
|
77
84
|
const pendingApprovals = new Map();
|
|
78
85
|
const pendingSessionPicks = new Map();
|
|
79
86
|
const pendingWorkspacePicks = new Map();
|
|
@@ -93,6 +100,7 @@ export function createBot(config, registry) {
|
|
|
93
100
|
const auditLog = new AuditLogStore(config.workspace, config.stateBackend, config.auditMaxEvents);
|
|
94
101
|
const lockStore = new SessionLockStore(config.workspace, config.stateBackend);
|
|
95
102
|
const userStore = new UserStore();
|
|
103
|
+
const authService = new RelayAuthService(config);
|
|
96
104
|
const contextUsers = new WeakMap();
|
|
97
105
|
const agentUpdateActors = new Map();
|
|
98
106
|
const agentUpdateStates = new Map();
|
|
@@ -155,17 +163,10 @@ export function createBot(config, registry) {
|
|
|
155
163
|
}
|
|
156
164
|
}
|
|
157
165
|
});
|
|
158
|
-
const getBusyState = (contextKey) =>
|
|
159
|
-
let state = contextBusy.get(contextKey);
|
|
160
|
-
if (!state) {
|
|
161
|
-
state = { processing: false, switching: false, transcribing: false, approving: false };
|
|
162
|
-
contextBusy.set(contextKey, state);
|
|
163
|
-
}
|
|
164
|
-
return state;
|
|
165
|
-
};
|
|
166
|
+
const getBusyState = (contextKey) => contextBusy.get(contextKey);
|
|
166
167
|
const getExternalActivity = (session) => getExternalActivityForSession(session, config);
|
|
167
168
|
const getBusyReason = (contextKey) => {
|
|
168
|
-
const state = contextBusy.
|
|
169
|
+
const state = contextBusy.peek(contextKey);
|
|
169
170
|
const session = registry.get(contextKey);
|
|
170
171
|
if (state?.processing || state?.switching || state?.transcribing || state?.approving || session?.isProcessing()) {
|
|
171
172
|
return { busy: true, kind: "connector", state: state ?? getBusyState(contextKey) };
|
|
@@ -191,41 +192,14 @@ export function createBot(config, registry) {
|
|
|
191
192
|
registry.updateMetadata(contextKey, session);
|
|
192
193
|
};
|
|
193
194
|
const checkAgentAuthStatus = async (info) => {
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
197
|
-
if (idOf(info) === "hermes") {
|
|
198
|
-
return checkHermesAuthStatus({
|
|
199
|
-
baseUrl: config.hermesApiBaseUrl,
|
|
200
|
-
apiKey: config.hermesApiKey,
|
|
201
|
-
});
|
|
202
|
-
}
|
|
203
|
-
if (idOf(info) === "openclaw") {
|
|
204
|
-
return checkOpenClawAuthStatus({
|
|
205
|
-
gatewayUrl: config.openClawGatewayUrl,
|
|
206
|
-
token: config.openClawGatewayToken,
|
|
207
|
-
password: config.openClawGatewayPassword,
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
if (idOf(info) === "claude-code") {
|
|
211
|
-
return checkClaudeCodeAuthStatus(config.claudeCodeCliPath);
|
|
212
|
-
}
|
|
213
|
-
return checkAuthStatus(config.codexApiKey);
|
|
195
|
+
const status = await authService.check(info);
|
|
196
|
+
return { ...status, method: status.method ?? "unknown" };
|
|
214
197
|
};
|
|
215
|
-
const agentIdForAuth =
|
|
216
|
-
const labelForAuth =
|
|
198
|
+
const agentIdForAuth = resolveAgentIdForAuth;
|
|
199
|
+
const labelForAuth = agentLabelForAuth;
|
|
217
200
|
const checkLoginAuthStatus = async (info) => {
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
return checkHermesAuthStatus({
|
|
221
|
-
baseUrl: config.hermesApiBaseUrl,
|
|
222
|
-
apiKey: config.hermesApiKey,
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
if (agentId === "claude-code") {
|
|
226
|
-
return checkClaudeCodeAuthStatus(config.claudeCodeCliPath);
|
|
227
|
-
}
|
|
228
|
-
return checkAuthStatus(config.codexApiKey);
|
|
201
|
+
const status = info ? await authService.check(info) : await checkAuthStatus(config.codexApiKey);
|
|
202
|
+
return { ...status, method: status.method ?? "unknown" };
|
|
229
203
|
};
|
|
230
204
|
const replyChannelAction = async (ctx, rendered) => {
|
|
231
205
|
const channelContext = telegramChannelContextFromCtx(ctx);
|
|
@@ -280,44 +254,16 @@ export function createBot(config, registry) {
|
|
|
280
254
|
}
|
|
281
255
|
};
|
|
282
256
|
const startAgentLogin = (info) => {
|
|
283
|
-
|
|
284
|
-
if (agentId === "hermes") {
|
|
285
|
-
return startHermesLogin(config.hermesCliPath);
|
|
286
|
-
}
|
|
287
|
-
if (agentId === "claude-code") {
|
|
288
|
-
return startClaudeCodeLogin(config.claudeCodeCliPath);
|
|
289
|
-
}
|
|
290
|
-
return startCodexLogin();
|
|
257
|
+
return info ? authService.startLogin(info) : startCodexLogin();
|
|
291
258
|
};
|
|
292
259
|
const startAgentLogout = (info) => {
|
|
293
|
-
|
|
294
|
-
if (agentId === "hermes") {
|
|
295
|
-
return startHermesLogout(config.hermesCliPath);
|
|
296
|
-
}
|
|
297
|
-
if (agentId === "claude-code") {
|
|
298
|
-
return startClaudeCodeLogout(config.claudeCodeCliPath);
|
|
299
|
-
}
|
|
300
|
-
return startCodexLogout();
|
|
260
|
+
return info ? authService.startLogout(info) : startCodexLogout();
|
|
301
261
|
};
|
|
302
262
|
const hostLoginCommand = (info) => {
|
|
303
|
-
|
|
304
|
-
if (agentId === "hermes") {
|
|
305
|
-
return `${config.hermesCliPath ?? "hermes"} login --no-browser`;
|
|
306
|
-
}
|
|
307
|
-
if (agentId === "claude-code") {
|
|
308
|
-
return `${config.claudeCodeCliPath ?? "claude"} auth login`;
|
|
309
|
-
}
|
|
310
|
-
return "codex login --device-auth";
|
|
263
|
+
return hostAgentLoginCommand(config, info);
|
|
311
264
|
};
|
|
312
265
|
const hostLogoutCommand = (info) => {
|
|
313
|
-
|
|
314
|
-
if (agentId === "hermes") {
|
|
315
|
-
return `${config.hermesCliPath ?? "hermes"} logout`;
|
|
316
|
-
}
|
|
317
|
-
if (agentId === "claude-code") {
|
|
318
|
-
return `${config.claudeCodeCliPath ?? "claude"} auth logout`;
|
|
319
|
-
}
|
|
320
|
-
return "codex logout";
|
|
266
|
+
return hostAgentLogoutCommand(config, info);
|
|
321
267
|
};
|
|
322
268
|
const isTopicContext = (contextKey) => isTopicContextKey(contextKey);
|
|
323
269
|
const getPreferences = (contextKey) => preferencesStore.get(contextKey);
|
|
@@ -421,276 +367,24 @@ export function createBot(config, registry) {
|
|
|
421
367
|
return item;
|
|
422
368
|
};
|
|
423
369
|
const monitorExternalContexts = async () => {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const session = await registry.getOrCreate(contextKey, { deferThreadStart: true }).catch(() => null);
|
|
440
|
-
if (!session) {
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
const info = session.getInfo();
|
|
444
|
-
if (!capabilitiesOf(info).externalActivity) {
|
|
445
|
-
const parsed = parseContextKey(contextKey);
|
|
446
|
-
const queueLength = promptStore.list(contextKey).length;
|
|
447
|
-
if (queueLength > 0 && !promptStore.isPaused(contextKey) && !session.isProcessing()) {
|
|
448
|
-
await drainQueuedPrompts(createSystemContext(contextKey), contextKey, parsed.chatId, session);
|
|
449
|
-
}
|
|
450
|
-
return;
|
|
451
|
-
}
|
|
452
|
-
const threadId = session.getActiveThreadId();
|
|
453
|
-
const parsed = parseContextKey(contextKey);
|
|
454
|
-
const queueLength = promptStore.list(contextKey).length;
|
|
455
|
-
const paused = promptStore.isPaused(contextKey);
|
|
456
|
-
if (!threadId) {
|
|
457
|
-
if (queueLength > 0 && !paused && !session.isProcessing()) {
|
|
458
|
-
await drainQueuedPrompts(createSystemContext(contextKey), contextKey, parsed.chatId, session);
|
|
459
|
-
}
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
const previous = externalMirrors.get(contextKey);
|
|
463
|
-
const snapshot = getExternalSnapshotForSession(session, config, {
|
|
464
|
-
afterLine: previous?.lastLine ?? Number.MAX_SAFE_INTEGER,
|
|
465
|
-
}) ?? getExternalSnapshotForSession(session, config, {
|
|
466
|
-
maxEvents: 0,
|
|
467
|
-
});
|
|
468
|
-
if (!snapshot) {
|
|
469
|
-
if (queueLength > 0 && !paused && !session.isProcessing()) {
|
|
370
|
+
await monitorChannelExternalContexts({
|
|
371
|
+
config,
|
|
372
|
+
registry,
|
|
373
|
+
promptStore,
|
|
374
|
+
isContextKey: isTelegramContextKey,
|
|
375
|
+
canSendSystemMessages: canSendSystemMessagesToContext,
|
|
376
|
+
contextForKey: channelContextFromTelegramKey,
|
|
377
|
+
previousLastLine: (contextKey) => externalMirrors.get(contextKey)?.lastLine,
|
|
378
|
+
mirrorSnapshot: async (contextKey, _context, session, snapshot) => {
|
|
379
|
+
const parsed = parseContextKey(contextKey);
|
|
380
|
+
await mirrorExternalSnapshot(contextKey, parsed.chatId, session, snapshot);
|
|
381
|
+
},
|
|
382
|
+
updateQueueStatus: (contextKey, _context, text) => updateQueueStatusMessage(contextKey, text),
|
|
383
|
+
drainQueue: async (contextKey, _context, session) => {
|
|
384
|
+
const parsed = parseContextKey(contextKey);
|
|
470
385
|
await drainQueuedPrompts(createSystemContext(contextKey), contextKey, parsed.chatId, session);
|
|
471
|
-
}
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
if (!session.isProcessing()) {
|
|
475
|
-
await mirrorExternalSnapshot(contextKey, parsed.chatId, session, snapshot);
|
|
476
|
-
}
|
|
477
|
-
const activity = snapshot.activity;
|
|
478
|
-
if (activity.active && queueLength > 0) {
|
|
479
|
-
await updateQueueStatusMessage(contextKey, `Waiting for ${info.agentLabel} CLI task... ${queueLength} queued${paused ? " (paused)" : ""}.`);
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
if (!activity.active && queueLength > 0 && !paused && !session.isProcessing()) {
|
|
483
|
-
await updateQueueStatusMessage(contextKey, `CLI task finished, running queued prompt 1/${queueLength}.`);
|
|
484
|
-
await drainQueuedPrompts(createSystemContext(contextKey), contextKey, parsed.chatId, session);
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
const sendExternalMirrorTyping = async (chatId, messageThreadId, state) => {
|
|
488
|
-
const now = Date.now();
|
|
489
|
-
if (state.lastTypingAt && now - state.lastTypingAt < TYPING_INTERVAL_MS) {
|
|
490
|
-
return;
|
|
491
|
-
}
|
|
492
|
-
state.lastTypingAt = now;
|
|
493
|
-
await sendChatActionSafe(bot.api, chatId, "typing", messageThreadId).catch(() => { });
|
|
494
|
-
};
|
|
495
|
-
const sendExternalWorkingNotice = async (chatId, messageThreadId, state, snapshot) => {
|
|
496
|
-
const turnKey = snapshot.activity.turnId ?? snapshot.activity.startedAt?.toISOString() ?? "unknown";
|
|
497
|
-
if (state.workingNoticeTurnKey === turnKey) {
|
|
498
|
-
return;
|
|
499
|
-
}
|
|
500
|
-
const prompt = trimLine(snapshot.latestUserMessage ?? "", 250);
|
|
501
|
-
const fallbackText = prompt ? `Working on ${prompt}` : `Working on external ${snapshot.agentLabel} task...`;
|
|
502
|
-
const html = prompt
|
|
503
|
-
? `<b>Working on</b> ${escapeHTML(prompt)}`
|
|
504
|
-
: `<b>Working on</b> external ${escapeHTML(snapshot.agentLabel)} task...`;
|
|
505
|
-
await sendTextMessage(bot.api, chatId, html, {
|
|
506
|
-
fallbackText,
|
|
507
|
-
messageThreadId,
|
|
386
|
+
},
|
|
508
387
|
});
|
|
509
|
-
state.workingNoticeTurnKey = turnKey;
|
|
510
|
-
};
|
|
511
|
-
const mirrorExternalSnapshot = async (contextKey, chatId, session, snapshot) => {
|
|
512
|
-
const parsed = parseContextKey(contextKey);
|
|
513
|
-
const previous = externalMirrors.get(contextKey);
|
|
514
|
-
let state = previous;
|
|
515
|
-
if (!state || state.threadId !== snapshot.threadId || state.rolloutPath !== snapshot.sourcePath) {
|
|
516
|
-
state = {
|
|
517
|
-
threadId: snapshot.threadId,
|
|
518
|
-
rolloutPath: snapshot.sourcePath,
|
|
519
|
-
lastLine: snapshot.lineCount,
|
|
520
|
-
turnId: snapshot.activity.turnId,
|
|
521
|
-
startedAt: snapshot.activity.startedAt,
|
|
522
|
-
};
|
|
523
|
-
externalMirrors.set(contextKey, state);
|
|
524
|
-
}
|
|
525
|
-
const mirrorMode = getEffectiveMirrorMode(contextKey);
|
|
526
|
-
if (snapshot.activity.active) {
|
|
527
|
-
state.turnId = snapshot.activity.turnId;
|
|
528
|
-
state.startedAt = snapshot.activity.startedAt;
|
|
529
|
-
const turnKey = snapshot.activity.turnId ?? snapshot.activity.startedAt?.toISOString() ?? "unknown";
|
|
530
|
-
if (state.activityStartedTurnKey !== turnKey) {
|
|
531
|
-
const info = session.getInfo();
|
|
532
|
-
appendActivity({
|
|
533
|
-
source: "cli",
|
|
534
|
-
status: "running",
|
|
535
|
-
type: "cli_turn_started",
|
|
536
|
-
contextKey,
|
|
537
|
-
threadId: snapshot.threadId,
|
|
538
|
-
workspace: info.workspace,
|
|
539
|
-
agentId: info.agentId,
|
|
540
|
-
actor: CLI_ACTIVITY_ACTOR,
|
|
541
|
-
prompt: snapshot.latestUserMessage ?? `${snapshot.agentLabel} CLI task`,
|
|
542
|
-
detail: `${snapshot.sourceLabel}: ${snapshot.sourcePath}`,
|
|
543
|
-
});
|
|
544
|
-
state.activityStartedTurnKey = turnKey;
|
|
545
|
-
state.activityFinishedTurnKey = undefined;
|
|
546
|
-
state.activityToolStartLines = [];
|
|
547
|
-
state.activityToolEndLines = [];
|
|
548
|
-
}
|
|
549
|
-
if (mirrorMode !== "off") {
|
|
550
|
-
await sendExternalMirrorTyping(chatId, parsed.messageThreadId, state);
|
|
551
|
-
}
|
|
552
|
-
if (mirrorMode === "final") {
|
|
553
|
-
await sendExternalWorkingNotice(chatId, parsed.messageThreadId, state, snapshot);
|
|
554
|
-
state.lastLine = Math.max(state.lastLine, snapshot.lineCount);
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
if (mirrorMode === "off") {
|
|
558
|
-
state.lastLine = Math.max(state.lastLine, snapshot.lineCount);
|
|
559
|
-
return;
|
|
560
|
-
}
|
|
561
|
-
const status = renderExternalMirrorStatus(snapshot, promptStore.list(contextKey).length);
|
|
562
|
-
const now = Date.now();
|
|
563
|
-
const canUpdateStatus = !state.latestStatusAt || now - state.latestStatusAt >= config.telegramMirrorMinUpdateMs;
|
|
564
|
-
if (!state.statusMessageId) {
|
|
565
|
-
const message = await sendTextMessage(bot.api, chatId, status.html, {
|
|
566
|
-
fallbackText: status.plain,
|
|
567
|
-
messageThreadId: parsed.messageThreadId,
|
|
568
|
-
});
|
|
569
|
-
state.statusMessageId = message.message_id;
|
|
570
|
-
state.latestStatusAt = now;
|
|
571
|
-
}
|
|
572
|
-
else if (state.latestStatus !== status.plain && canUpdateStatus) {
|
|
573
|
-
await safeEditMessage(bot, chatId, state.statusMessageId, status.html, {
|
|
574
|
-
fallbackText: status.plain,
|
|
575
|
-
});
|
|
576
|
-
state.latestStatusAt = now;
|
|
577
|
-
}
|
|
578
|
-
state.latestStatus = status.plain;
|
|
579
|
-
if (mirrorMode === "full") {
|
|
580
|
-
const newEvents = snapshot.events
|
|
581
|
-
.filter((event) => event.lineNumber > (state.latestMirroredEventLine ?? state.lastLine))
|
|
582
|
-
.filter((event) => event.kind === "tool" || event.kind === "task")
|
|
583
|
-
.slice(-4);
|
|
584
|
-
for (const event of newEvents) {
|
|
585
|
-
const rendered = renderExternalMirrorEvent(event);
|
|
586
|
-
if (!rendered) {
|
|
587
|
-
continue;
|
|
588
|
-
}
|
|
589
|
-
await sendTextMessage(bot.api, chatId, rendered.html, {
|
|
590
|
-
fallbackText: rendered.plain,
|
|
591
|
-
messageThreadId: parsed.messageThreadId,
|
|
592
|
-
});
|
|
593
|
-
state.latestMirroredEventLine = event.lineNumber;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
const info = session.getInfo();
|
|
597
|
-
const loggedStartLines = new Set(state.activityToolStartLines ?? []);
|
|
598
|
-
const loggedEndLines = new Set(state.activityToolEndLines ?? []);
|
|
599
|
-
for (const event of snapshot.events.filter((event) => event.lineNumber > state.lastLine && event.kind === "tool")) {
|
|
600
|
-
if (event.status === "started" && !loggedStartLines.has(event.lineNumber)) {
|
|
601
|
-
appendActivity({
|
|
602
|
-
source: "cli",
|
|
603
|
-
status: "running",
|
|
604
|
-
type: "cli_tool_started",
|
|
605
|
-
contextKey,
|
|
606
|
-
threadId: snapshot.threadId,
|
|
607
|
-
workspace: info.workspace,
|
|
608
|
-
agentId: info.agentId,
|
|
609
|
-
actor: CLI_ACTIVITY_ACTOR,
|
|
610
|
-
prompt: snapshot.latestUserMessage ?? undefined,
|
|
611
|
-
detail: event.toolName ?? "tool",
|
|
612
|
-
});
|
|
613
|
-
loggedStartLines.add(event.lineNumber);
|
|
614
|
-
}
|
|
615
|
-
if ((event.status === "finished" || event.status === "failed") && !loggedEndLines.has(event.lineNumber)) {
|
|
616
|
-
appendActivity({
|
|
617
|
-
source: "cli",
|
|
618
|
-
status: event.status === "failed" ? "failed" : "completed",
|
|
619
|
-
type: event.status === "failed" ? "cli_tool_failed" : "cli_tool_completed",
|
|
620
|
-
contextKey,
|
|
621
|
-
threadId: snapshot.threadId,
|
|
622
|
-
workspace: info.workspace,
|
|
623
|
-
agentId: info.agentId,
|
|
624
|
-
actor: CLI_ACTIVITY_ACTOR,
|
|
625
|
-
prompt: snapshot.latestUserMessage ?? undefined,
|
|
626
|
-
detail: event.toolName ?? "tool",
|
|
627
|
-
});
|
|
628
|
-
loggedEndLines.add(event.lineNumber);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
state.activityToolStartLines = [...loggedStartLines].slice(-200);
|
|
632
|
-
state.activityToolEndLines = [...loggedEndLines].slice(-200);
|
|
633
|
-
state.lastLine = Math.max(state.lastLine, snapshot.lineCount);
|
|
634
|
-
return;
|
|
635
|
-
}
|
|
636
|
-
if (!previous) {
|
|
637
|
-
state.lastLine = Math.max(state.lastLine, snapshot.lineCount);
|
|
638
|
-
return;
|
|
639
|
-
}
|
|
640
|
-
const terminalEvent = [...snapshot.events].reverse().find((event) => event.kind === "task" && event.status && event.status !== "started");
|
|
641
|
-
if (terminalEvent) {
|
|
642
|
-
const turnKey = terminalEvent.turnId ?? snapshot.activity.turnId ?? state.startedAt?.toString() ?? "unknown";
|
|
643
|
-
if (state.activityFinishedTurnKey !== turnKey) {
|
|
644
|
-
const info = session.getInfo();
|
|
645
|
-
const startedAt = state.startedAt instanceof Date ? state.startedAt : state.startedAt ? new Date(state.startedAt) : snapshot.activity.startedAt;
|
|
646
|
-
appendActivity({
|
|
647
|
-
source: "cli",
|
|
648
|
-
status: terminalEvent.status === "aborted" ? "aborted" : terminalEvent.status === "failed" ? "failed" : "completed",
|
|
649
|
-
type: "cli_turn_finished",
|
|
650
|
-
contextKey,
|
|
651
|
-
threadId: snapshot.threadId,
|
|
652
|
-
workspace: info.workspace,
|
|
653
|
-
agentId: info.agentId,
|
|
654
|
-
actor: CLI_ACTIVITY_ACTOR,
|
|
655
|
-
prompt: snapshot.latestUserMessage ?? undefined,
|
|
656
|
-
detail: `${snapshot.agentLabel} CLI task ${terminalEvent.status ?? "finished"}.`,
|
|
657
|
-
durationMs: startedAt && terminalEvent.timestamp ? Math.max(0, terminalEvent.timestamp.getTime() - startedAt.getTime()) : undefined,
|
|
658
|
-
});
|
|
659
|
-
state.activityFinishedTurnKey = turnKey;
|
|
660
|
-
}
|
|
661
|
-
if (mirrorMode !== "off") {
|
|
662
|
-
const doneText = `${snapshot.agentLabel} CLI task ${terminalEvent.status}.`;
|
|
663
|
-
if (state.statusMessageId) {
|
|
664
|
-
await safeEditMessage(bot, chatId, state.statusMessageId, escapeHTML(doneText), {
|
|
665
|
-
fallbackText: doneText,
|
|
666
|
-
});
|
|
667
|
-
}
|
|
668
|
-
else if (shouldNotify(contextKey, "minimal")) {
|
|
669
|
-
await sendTextMessage(bot.api, chatId, escapeHTML(doneText), {
|
|
670
|
-
fallbackText: doneText,
|
|
671
|
-
messageThreadId: parsed.messageThreadId,
|
|
672
|
-
});
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
const finalAgent = snapshot.events.filter((event) => event.kind === "agent" && event.text).at(-1);
|
|
676
|
-
if (mirrorMode !== "off" && mirrorMode !== "status" && finalAgent?.text && finalAgent.lineNumber !== state.latestAgentLine) {
|
|
677
|
-
await sendTextMessage(bot.api, chatId, `<b>${escapeHTML(snapshot.agentLabel)} CLI final answer:</b>`, {
|
|
678
|
-
fallbackText: `${snapshot.agentLabel} CLI final answer:`,
|
|
679
|
-
messageThreadId: parsed.messageThreadId,
|
|
680
|
-
});
|
|
681
|
-
for (const chunk of splitMarkdownForTelegram(finalAgent.text)) {
|
|
682
|
-
await sendTextMessage(bot.api, chatId, chunk.text, {
|
|
683
|
-
parseMode: chunk.parseMode,
|
|
684
|
-
fallbackText: chunk.fallbackText,
|
|
685
|
-
messageThreadId: parsed.messageThreadId,
|
|
686
|
-
});
|
|
687
|
-
}
|
|
688
|
-
state.latestAgentLine = finalAgent.lineNumber;
|
|
689
|
-
}
|
|
690
|
-
await deliverCliGeneratedArtifacts(contextKey, chatId, session, state.startedAt, terminalEvent.turnId, parsed.messageThreadId);
|
|
691
|
-
}
|
|
692
|
-
state.workingNoticeTurnKey = undefined;
|
|
693
|
-
state.lastLine = Math.max(state.lastLine, snapshot.lineCount);
|
|
694
388
|
};
|
|
695
389
|
const canSendSystemMessagesToContext = (contextKey) => {
|
|
696
390
|
if (!userStore.hasAdminUser()) {
|
|
@@ -706,59 +400,116 @@ export function createBot(config, registry) {
|
|
|
706
400
|
if (!canSendSystemMessagesToContext(contextKey)) {
|
|
707
401
|
return;
|
|
708
402
|
}
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
}
|
|
712
|
-
const state = externalMirrors.get(contextKey);
|
|
713
|
-
if (state?.artifactsDeliveredForTurnId === turnId) {
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
const workspace = session.getInfo().workspace;
|
|
717
|
-
const report = await collectRecentWorkspaceArtifacts(workspace, {
|
|
718
|
-
since: startedAt,
|
|
719
|
-
until: new Date(),
|
|
720
|
-
maxFileSize: config.maxFileSize,
|
|
721
|
-
limit: 5,
|
|
722
|
-
ignoreDirs: config.artifactIgnoreDirs,
|
|
723
|
-
ignoreGlobs: config.artifactIgnoreGlobs,
|
|
724
|
-
});
|
|
725
|
-
if (isEmptyArtifactReport(report)) {
|
|
726
|
-
if (state)
|
|
727
|
-
state.artifactsDeliveredForTurnId = turnId;
|
|
728
|
-
return;
|
|
729
|
-
}
|
|
730
|
-
const persistedReport = await persistWorkspaceArtifactReport(workspace, turnId, report).catch((error) => {
|
|
731
|
-
console.error("Failed to persist CLI artifact report:", error);
|
|
732
|
-
return null;
|
|
733
|
-
});
|
|
734
|
-
if (!config.telegramAutoSendArtifacts) {
|
|
735
|
-
if (state)
|
|
736
|
-
state.artifactsDeliveredForTurnId = turnId;
|
|
737
|
-
return;
|
|
738
|
-
}
|
|
739
|
-
const summary = formatArtifactSummary(report.artifacts, report.skippedCount, report.omittedCount);
|
|
740
|
-
await sendTextMessage(bot.api, chatId, escapeHTML(summary), {
|
|
741
|
-
fallbackText: summary,
|
|
742
|
-
messageThreadId,
|
|
743
|
-
});
|
|
744
|
-
for (const artifact of (persistedReport?.artifacts ?? report.artifacts)) {
|
|
745
|
-
await sendArtifactFileByApi(bot.api, chatId, artifact, messageThreadId);
|
|
746
|
-
}
|
|
747
|
-
const info = session.getInfo();
|
|
748
|
-
appendActivity({
|
|
749
|
-
source: "cli",
|
|
750
|
-
status: "info",
|
|
751
|
-
type: "artifacts_sent",
|
|
403
|
+
await deliverChannelCliArtifacts({
|
|
404
|
+
config,
|
|
752
405
|
contextKey,
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
406
|
+
session,
|
|
407
|
+
startedAt,
|
|
408
|
+
turnId,
|
|
409
|
+
state: externalMirrors.get(contextKey),
|
|
410
|
+
autoSend: config.telegramAutoSendArtifacts,
|
|
411
|
+
sendSummaryWhenAutoSendDisabled: false,
|
|
412
|
+
logPrefix: "Telegram",
|
|
413
|
+
sendSummary: (summary) => sendTextMessage(bot.api, chatId, escapeHTML(summary), {
|
|
414
|
+
fallbackText: summary,
|
|
415
|
+
messageThreadId,
|
|
416
|
+
}).then(() => { }),
|
|
417
|
+
sendArtifact: (artifact) => sendArtifactFileByApi(bot.api, chatId, artifact, messageThreadId).then(() => { }),
|
|
418
|
+
appendActivity,
|
|
758
419
|
});
|
|
759
|
-
if (state)
|
|
760
|
-
state.artifactsDeliveredForTurnId = turnId;
|
|
761
420
|
};
|
|
421
|
+
const channelContextFromTelegramKey = (contextKey) => {
|
|
422
|
+
const parsed = parseContextKey(contextKey);
|
|
423
|
+
return {
|
|
424
|
+
channelId: "telegram",
|
|
425
|
+
chatId: String(parsed.chatId),
|
|
426
|
+
...(parsed.messageThreadId ? { topicId: String(parsed.messageThreadId) } : {}),
|
|
427
|
+
};
|
|
428
|
+
};
|
|
429
|
+
const externalMirrorController = createChannelExternalMirrorController({
|
|
430
|
+
config,
|
|
431
|
+
states: externalMirrors,
|
|
432
|
+
typingIntervalMs: TYPING_INTERVAL_MS,
|
|
433
|
+
minUpdateMs: () => config.telegramMirrorMinUpdateMs,
|
|
434
|
+
mirrorMode: (contextKey) => getEffectiveMirrorMode(contextKey),
|
|
435
|
+
queueLength: (contextKey) => promptStore.list(contextKey).length,
|
|
436
|
+
activityActor: () => CLI_ACTIVITY_ACTOR,
|
|
437
|
+
appendActivity,
|
|
438
|
+
sendTyping: async (contextKey) => {
|
|
439
|
+
const parsed = parseContextKey(contextKey);
|
|
440
|
+
await sendChatActionSafe(bot.api, parsed.chatId, "typing", parsed.messageThreadId).catch(() => { });
|
|
441
|
+
},
|
|
442
|
+
sendWorkingNotice: async (contextKey, _context, state, snapshot, prompt) => {
|
|
443
|
+
const turnKey = snapshot.activity.turnId ?? snapshot.activity.startedAt?.toISOString() ?? "unknown";
|
|
444
|
+
if (state.workingNoticeTurnKey === turnKey) {
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const parsed = parseContextKey(contextKey);
|
|
448
|
+
const fallbackText = prompt ? `Working on ${prompt}` : `Working on external ${snapshot.agentLabel} task...`;
|
|
449
|
+
const html = prompt
|
|
450
|
+
? `<b>Working on</b> ${escapeHTML(prompt)}`
|
|
451
|
+
: `<b>Working on</b> external ${escapeHTML(snapshot.agentLabel)} task...`;
|
|
452
|
+
await sendTextMessage(bot.api, parsed.chatId, html, {
|
|
453
|
+
fallbackText,
|
|
454
|
+
messageThreadId: parsed.messageThreadId,
|
|
455
|
+
});
|
|
456
|
+
state.workingNoticeTurnKey = turnKey;
|
|
457
|
+
},
|
|
458
|
+
sendStatus: async (contextKey, _context, _state, rendered) => {
|
|
459
|
+
const parsed = parseContextKey(contextKey);
|
|
460
|
+
const message = await sendTextMessage(bot.api, parsed.chatId, rendered.html, {
|
|
461
|
+
fallbackText: rendered.plain,
|
|
462
|
+
messageThreadId: parsed.messageThreadId,
|
|
463
|
+
});
|
|
464
|
+
return message.message_id;
|
|
465
|
+
},
|
|
466
|
+
editStatus: async (contextKey, _context, _state, messageId, rendered) => {
|
|
467
|
+
const parsed = parseContextKey(contextKey);
|
|
468
|
+
await safeEditMessage(bot, parsed.chatId, messageId, rendered.html, {
|
|
469
|
+
fallbackText: rendered.plain,
|
|
470
|
+
});
|
|
471
|
+
},
|
|
472
|
+
sendEvent: async (contextKey, _context, _state, rendered) => {
|
|
473
|
+
const parsed = parseContextKey(contextKey);
|
|
474
|
+
await sendTextMessage(bot.api, parsed.chatId, rendered.html, {
|
|
475
|
+
fallbackText: rendered.plain,
|
|
476
|
+
messageThreadId: parsed.messageThreadId,
|
|
477
|
+
});
|
|
478
|
+
},
|
|
479
|
+
sendDone: async (contextKey, _context, state, text) => {
|
|
480
|
+
const parsed = parseContextKey(contextKey);
|
|
481
|
+
if (state.statusMessageId) {
|
|
482
|
+
await safeEditMessage(bot, parsed.chatId, state.statusMessageId, escapeHTML(text), {
|
|
483
|
+
fallbackText: text,
|
|
484
|
+
});
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
await sendTextMessage(bot.api, parsed.chatId, escapeHTML(text), {
|
|
488
|
+
fallbackText: text,
|
|
489
|
+
messageThreadId: parsed.messageThreadId,
|
|
490
|
+
});
|
|
491
|
+
},
|
|
492
|
+
sendFinalAnswer: async (contextKey, _context, _state, snapshot, text) => {
|
|
493
|
+
const parsed = parseContextKey(contextKey);
|
|
494
|
+
await sendTextMessage(bot.api, parsed.chatId, `<b>${escapeHTML(snapshot.agentLabel)} CLI final answer:</b>`, {
|
|
495
|
+
fallbackText: `${snapshot.agentLabel} CLI final answer:`,
|
|
496
|
+
messageThreadId: parsed.messageThreadId,
|
|
497
|
+
});
|
|
498
|
+
for (const chunk of splitMarkdownForTelegram(text)) {
|
|
499
|
+
await sendTextMessage(bot.api, parsed.chatId, chunk.text, {
|
|
500
|
+
parseMode: chunk.parseMode,
|
|
501
|
+
fallbackText: chunk.fallbackText,
|
|
502
|
+
messageThreadId: parsed.messageThreadId,
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
deliverArtifacts: (contextKey, _context, session, state, turnId) => {
|
|
507
|
+
const parsed = parseContextKey(contextKey);
|
|
508
|
+
return deliverCliGeneratedArtifacts(contextKey, parsed.chatId, session, state.startedAt, turnId, parsed.messageThreadId);
|
|
509
|
+
},
|
|
510
|
+
shouldSendDone: (contextKey) => shouldNotify(contextKey, "minimal"),
|
|
511
|
+
});
|
|
512
|
+
const mirrorExternalSnapshot = (contextKey, _chatId, session, snapshot) => externalMirrorController.mirror(contextKey, channelContextFromTelegramKey(contextKey), session, snapshot);
|
|
762
513
|
const scheduleExternalQueueDrain = (ctx, contextKey, chatId, session) => {
|
|
763
514
|
if (externalQueueTimers.has(contextKey)) {
|
|
764
515
|
return;
|