@poolzin/pool-bot 2026.3.25 → 2026.3.27
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/model-fallback.js +5 -4
- package/dist/agents/tools/common.js +16 -201
- package/dist/auto-reply/auto-reply/reply/agent-runner-execution.js +502 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-helpers.js +65 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-memory.js +160 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-payloads.js +85 -0
- package/dist/auto-reply/auto-reply/reply/agent-runner-utils.js +101 -0
- package/dist/auto-reply/auto-reply/reply/bash-command.js +338 -0
- package/dist/auto-reply/auto-reply/reply/block-streaming.js +91 -0
- package/dist/auto-reply/auto-reply/reply/commands-approve.js +88 -0
- package/dist/auto-reply/auto-reply/reply/commands-bash.js +26 -0
- package/dist/auto-reply/auto-reply/reply/commands-compact.js +107 -0
- package/dist/auto-reply/auto-reply/reply/commands-config.js +241 -0
- package/dist/auto-reply/auto-reply/reply/commands-context-report.js +295 -0
- package/dist/auto-reply/auto-reply/reply/commands-context.js +30 -0
- package/dist/auto-reply/auto-reply/reply/commands-core.js +151 -0
- package/dist/auto-reply/auto-reply/reply/commands-export-session.js +163 -0
- package/dist/auto-reply/auto-reply/reply/commands-info.js +184 -0
- package/dist/auto-reply/auto-reply/reply/commands-models.js +299 -0
- package/dist/auto-reply/auto-reply/reply/commands-plugin.js +35 -0
- package/dist/auto-reply/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/auto-reply/auto-reply/reply/commands-setunset.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-slash-parse.js +31 -0
- package/dist/auto-reply/auto-reply/reply/commands-status.js +178 -0
- package/dist/auto-reply/auto-reply/reply/commands-subagents.js +73 -0
- package/dist/auto-reply/auto-reply/reply/commands-system-prompt.js +117 -0
- package/dist/auto-reply/auto-reply/reply/commands-tts.js +231 -0
- package/dist/auto-reply/auto-reply/reply/directive-handling.impl.js +380 -0
- package/dist/auto-reply/auto-reply/reply/followup-runner.js +227 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-apply.js +201 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives-utils.js +54 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-directives.js +332 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-inline-actions.js +258 -0
- package/dist/auto-reply/auto-reply/reply/get-reply-run.js +297 -0
- package/dist/auto-reply/auto-reply/reply/groups.js +102 -0
- package/dist/auto-reply/auto-reply/reply/mentions.js +129 -0
- package/dist/auto-reply/auto-reply/reply/reply-delivery.js +92 -0
- package/dist/auto-reply/auto-reply/reply/reply-directives.js +30 -0
- package/dist/auto-reply/auto-reply/reply/reply-dispatcher.js +152 -0
- package/dist/auto-reply/auto-reply/reply/reply-elevated.js +166 -0
- package/dist/auto-reply/auto-reply/reply/reply-inline.js +28 -0
- package/dist/auto-reply/auto-reply/reply/reply-payloads.js +114 -0
- package/dist/auto-reply/auto-reply/reply/reply-reference.js +36 -0
- package/dist/auto-reply/auto-reply/reply/reply-tags.js +13 -0
- package/dist/auto-reply/auto-reply/reply/reply-threading.js +41 -0
- package/dist/auto-reply/auto-reply/reply/session-updates.js +233 -0
- package/dist/auto-reply/auto-reply/reply/stage-sandbox-media.js +146 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/canvas-host/a2ui/a2ui.bundle.js +2 -17772
- package/dist/canvas-host/a2ui/index.html +1 -307
- package/dist/channels/channels/directory-config.js +185 -0
- package/dist/channels/channels/discord/handle-action.guild-admin.js +332 -0
- package/dist/channels/channels/discord/handle-action.js +165 -0
- package/dist/channels/channels/discord.js +413 -0
- package/dist/channels/channels/dock.js +436 -0
- package/dist/channels/channels/index.js +51 -0
- package/dist/channels/channels/plugins/outbound/discord.js +101 -0
- package/dist/channels/channels/whatsapp.js +17 -0
- package/dist/channels/plugins/types.js +1 -1
- package/dist/channels/run-state-machine.js +7 -0
- package/dist/commands/models/auth.js +47 -1
- package/dist/commands-subagents/action-agents.js +44 -0
- package/dist/commands-subagents/action-focus.js +64 -0
- package/dist/commands-subagents/action-help.js +4 -0
- package/dist/commands-subagents/action-info.js +45 -0
- package/dist/commands-subagents/action-kill.js +60 -0
- package/dist/commands-subagents/action-list.js +44 -0
- package/dist/commands-subagents/action-log.js +29 -0
- package/dist/commands-subagents/action-send.js +119 -0
- package/dist/commands-subagents/action-spawn.js +52 -0
- package/dist/commands-subagents/action-unfocus.js +30 -0
- package/dist/commands-subagents/shared.js +303 -0
- package/dist/config/config.js +1 -8
- package/dist/config/types.secrets.js +61 -0
- package/dist/control-ui/assets/{index-D7shnQwQ.js → index-umCsvrWy.js} +884 -741
- package/dist/control-ui/assets/index-umCsvrWy.js.map +1 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js +2 -0
- package/dist/control-ui/assets/pt-BR-DedEVAvY.js.map +1 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js +2 -0
- package/dist/control-ui/assets/zh-CN-CDzeklK-.js.map +1 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js +2 -0
- package/dist/control-ui/assets/zh-TW-BJCRYNWH.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/method-scopes.js +9 -1
- package/dist/gateway/node-pending-work.js +142 -0
- package/dist/gateway/protocol/index.js +5 -1
- package/dist/gateway/protocol/schema/nodes.js +18 -0
- package/dist/gateway/server-methods/nodes-pending.js +96 -0
- package/dist/gateway/server-methods-list.js +4 -0
- package/dist/gateway/server-methods.js +2 -0
- package/dist/imessage/channel.js +253 -0
- package/dist/imessage/monitor/echo-cache.js +70 -0
- package/dist/imessage/monitor/loop-rate-limiter.js +51 -0
- package/dist/imessage/monitor/reflection-guard.js +50 -0
- package/dist/imessage/monitor/sanitize-outbound.js +25 -0
- package/dist/imessage/monitor/self-chat-cache.js +75 -0
- package/dist/imessage/runtime.js +3 -0
- package/dist/infra/exec-approval-reply.js +7 -0
- package/dist/infra/tmp-openclaw-dir.js +84 -0
- package/dist/pairing/pairing-challenge.js +15 -0
- package/dist/plugin-sdk/account-id.d.ts +1 -0
- package/dist/plugin-sdk/agent-media-payload.d.ts +12 -0
- package/dist/plugin-sdk/allow-from.d.ts +27 -0
- package/dist/plugin-sdk/command-auth.d.ts +25 -0
- package/dist/plugin-sdk/command-auth.js +3 -1
- package/dist/plugin-sdk/config-paths.d.ts +6 -0
- package/dist/plugin-sdk/file-lock.d.ts +16 -0
- package/dist/plugin-sdk/index.d.ts +428 -0
- package/dist/plugin-sdk/index.js +237 -103
- package/dist/plugin-sdk/json-store.d.ts +5 -0
- package/dist/plugin-sdk/keyed-async-queue.d.ts +12 -0
- package/dist/plugin-sdk/onboarding.d.ts +11 -0
- package/dist/plugin-sdk/provider-auth-result.d.ts +14 -0
- package/dist/plugin-sdk/slack-message-actions.d.ts +11 -0
- package/dist/plugin-sdk/status-helpers.d.ts +25 -0
- package/dist/plugin-sdk/temp-path.d.ts +12 -0
- package/dist/plugin-sdk/text-chunking.d.ts +1 -0
- package/dist/plugin-sdk/tool-send.d.ts +4 -0
- package/dist/plugin-sdk/webhook-path.d.ts +6 -0
- package/dist/plugin-sdk/webhook-targets.d.ts +23 -0
- package/dist/plugin-sdk/windows-spawn.d.ts +39 -0
- package/dist/plugin-sdk-internal/accounts.js +6 -0
- package/dist/plugin-sdk-internal/discord.js +23 -0
- package/dist/plugin-sdk-internal/imessage.js +13 -0
- package/dist/plugin-sdk-internal/setup.js +9 -0
- package/dist/plugin-sdk-internal/signal.js +13 -0
- package/dist/plugin-sdk-internal/slack.js +22 -0
- package/dist/plugin-sdk-internal/telegram.js +32 -0
- package/dist/plugin-sdk-internal/whatsapp.js +29 -0
- package/dist/routing/session-key.js +4 -185
- package/dist/shared/pid-alive.js +2 -61
- package/dist/shared/process-scoped-map.js +5 -7
- package/dist/signal/channel.js +264 -0
- package/dist/signal/monitor/access-policy.js +60 -0
- package/dist/signal/runtime.js +3 -0
- package/dist/slack/account-inspect.js +135 -0
- package/dist/slack/blocks-input.js +7 -38
- package/dist/slack/channel.js +394 -0
- package/dist/slack/interactive-replies.js +28 -0
- package/dist/slack/monitor/channel-type.js +31 -0
- package/dist/slack/monitor/dm-auth.js +49 -0
- package/dist/slack/monitor/events/interactions.modal.js +137 -0
- package/dist/slack/monitor/events/message-subtype-handlers.js +68 -0
- package/dist/slack/monitor/events/system-event-context.js +29 -0
- package/dist/slack/monitor/events/system-event-test-harness.js +41 -0
- package/dist/slack/monitor/external-arg-menu-store.js +46 -0
- package/dist/slack/monitor/message-handler/prepare-content.js +69 -0
- package/dist/slack/monitor/message-handler/prepare-thread-context.js +91 -0
- package/dist/slack/monitor/message-handler/prepare.test-helpers.js +55 -0
- package/dist/slack/monitor/reconnect-policy.js +78 -0
- package/dist/slack/monitor/slash-commands.runtime.js +1 -0
- package/dist/slack/monitor/slash-dispatch.runtime.js +9 -0
- package/dist/slack/monitor/slash-skill-commands.runtime.js +1 -0
- package/dist/slack/resolve-allowlist-common.js +36 -0
- package/dist/slack/runtime.js +3 -0
- package/dist/slack/sent-thread-cache.js +61 -0
- package/dist/slack/truncate.js +10 -0
- package/dist/telegram/account-inspect.js +175 -0
- package/dist/telegram/allow-from.js +10 -0
- package/dist/telegram/api-fetch.js +18 -0
- package/dist/telegram/approval-buttons.js +30 -0
- package/dist/telegram/audit-membership-runtime.js +61 -0
- package/dist/telegram/bot/delivery.replies.js +508 -0
- package/dist/telegram/bot/delivery.resolve-media.js +227 -0
- package/dist/telegram/bot/delivery.send.js +132 -0
- package/dist/telegram/bot/reply-threading.js +46 -0
- package/dist/telegram/bot-message-context.body.js +186 -0
- package/dist/telegram/bot-message-context.session.js +207 -0
- package/dist/telegram/bot-message-context.types.js +1 -0
- package/dist/telegram/bot-native-commands.test-helpers.js +117 -0
- package/dist/telegram/bot.media.e2e-harness.js +81 -0
- package/dist/telegram/bot.media.test-utils.js +81 -0
- package/dist/telegram/channel-actions.js +225 -0
- package/dist/telegram/channel.js +515 -0
- package/dist/telegram/conversation-route.js +107 -0
- package/dist/telegram/delivery.js +2 -0
- package/dist/telegram/delivery.replies.js +508 -0
- package/dist/telegram/dm-access.js +86 -0
- package/dist/telegram/draft-stream.test-helpers.js +62 -0
- package/dist/telegram/exec-approvals-handler.js +281 -0
- package/dist/telegram/exec-approvals.js +62 -0
- package/dist/telegram/forum-service-message.js +22 -0
- package/dist/telegram/group-config-helpers.js +10 -0
- package/dist/telegram/lane-delivery-state.js +19 -0
- package/dist/telegram/lane-delivery-text-deliverer.js +357 -0
- package/dist/telegram/lane-delivery.js +2 -0
- package/dist/telegram/normalize.js +37 -0
- package/dist/telegram/onboarding.js +192 -0
- package/dist/telegram/outbound-adapter.js +100 -0
- package/dist/telegram/polling-session.js +275 -0
- package/dist/telegram/runtime.js +3 -0
- package/dist/telegram/sendchataction-401-backoff.js +71 -0
- package/dist/telegram/sequential-key.js +46 -0
- package/dist/telegram/status-issues.js +105 -0
- package/dist/telegram/target-writeback.js +165 -0
- package/dist/telegram/thread-bindings.js +560 -0
- package/dist/utils.js +32 -257
- package/dist/wizard/prompts.js +5 -5
- package/extensions/feishu/src/policy.ts +1 -1
- package/extensions/firecrawl/index.test.ts +82 -0
- package/extensions/firecrawl/index.ts +20 -0
- package/extensions/firecrawl/openclaw.plugin.json +8 -0
- package/extensions/firecrawl/package.json +12 -0
- package/extensions/firecrawl/src/config.ts +159 -0
- package/extensions/firecrawl/src/firecrawl-client.ts +446 -0
- package/extensions/firecrawl/src/firecrawl-scrape-tool.ts +89 -0
- package/extensions/firecrawl/src/firecrawl-search-provider.ts +63 -0
- package/extensions/firecrawl/src/firecrawl-search-tool.ts +76 -0
- package/package.json +1 -1
- package/dist/.buildstamp +0 -1
- package/dist/acp/bindings-store.js +0 -209
- package/dist/acp/control-plane/runtime-cache.js +0 -54
- package/dist/acp/control-plane/runtime-options.js +0 -215
- package/dist/acp/control-plane/session-actor-queue.js +0 -36
- package/dist/acp/index.js +0 -2
- package/dist/acp/runtime/errors.js +0 -47
- package/dist/acp/runtime/registry.js +0 -86
- package/dist/acp/secret-file.js +0 -22
- package/dist/agents/auth-profiles.resolve-auth-profile-order.fixtures.js +0 -23
- package/dist/agents/bash-process-registry.test-helpers.js +0 -29
- package/dist/agents/bash-tools.exec-approval-request.js +0 -20
- package/dist/agents/bash-tools.exec-host-gateway.js +0 -240
- package/dist/agents/bash-tools.exec-host-node.js +0 -235
- package/dist/agents/checkpoint-manager.js +0 -290
- package/dist/agents/claude-cli-runner.js +0 -3
- package/dist/agents/error-classifier.js +0 -251
- package/dist/agents/live-model-filter.js +0 -84
- package/dist/agents/nvidia-models.js +0 -228
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +0 -34
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.mocks.shared.js +0 -156
- package/dist/agents/pi-embedded-subscribe.handlers.tools.media.test-helpers.js +0 -30
- package/dist/agents/provider/config-loader.js +0 -76
- package/dist/agents/provider/index.js +0 -15
- package/dist/agents/provider/models-dev.js +0 -129
- package/dist/agents/provider/session-binding.js +0 -376
- package/dist/agents/queued-file-writer.js +0 -22
- package/dist/agents/skills/bundled-context.js +0 -23
- package/dist/agents/skills/security.js +0 -211
- package/dist/agents/skills/tools-dir.js +0 -9
- package/dist/agents/skills-install-download.js +0 -290
- package/dist/agents/skills-install-output.js +0 -30
- package/dist/agents/skills-install.download-test-utils.js +0 -36
- package/dist/agents/skills.test-helpers.js +0 -13
- package/dist/agents/subagent-announce-reliability.js +0 -160
- package/dist/agents/subagent-registry.mocks.shared.js +0 -12
- package/dist/agents/test-helpers/assistant-message-fixtures.js +0 -29
- package/dist/agents/test-helpers/fast-coding-tools.js +0 -1
- package/dist/agents/test-helpers/fast-core-tools.js +0 -8
- package/dist/agents/test-helpers/fast-tool-stubs.js +0 -18
- package/dist/agents/test-helpers/host-sandbox-fs-bridge.js +0 -74
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +0 -27
- package/dist/agents/tool-display-common.js +0 -915
- package/dist/agents/tool-policy-shared.js +0 -108
- package/dist/agents/tool-policy.conformance.js +0 -14
- package/dist/agents/tool-result-truncation.js +0 -299
- package/dist/agents/tools/cron-tool.test-helpers.js +0 -12
- package/dist/agents/tools/discord-actions-moderation-shared.js +0 -27
- package/dist/agents/tools/discord-actions-presence.js +0 -78
- package/dist/control-ui/assets/index-D7shnQwQ.js.map +0 -1
- package/dist/discord/discord-improvements.js +0 -167
- package/dist/discord/index.js +0 -2
- package/dist/hooks/bundled/boot-md/HOOK.md +0 -19
- package/dist/hooks/bundled/command-logger/HOOK.md +0 -122
- package/dist/hooks/bundled/session-memory/HOOK.md +0 -86
- package/dist/hooks/bundled/soul-evil/HOOK.md +0 -71
- package/dist/whatsapp/normalize.js +0 -66
- package/dist/whatsapp/resolve-outbound-target.js +0 -42
- /package/dist/{acp/runtime/types.js → auto-reply/auto-reply/reply/commands-types.js} +0 -0
- /package/dist/{agents/pi-embedded-payloads.js → slack/account-surface-fields.js} +0 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { resolveSessionAgentIds } from "../../agents/agent-scope.js";
|
|
2
|
+
import { resolveBootstrapMaxChars } from "../../agents/pi-embedded-helpers.js";
|
|
3
|
+
import { createPoolbotCodingTools } from "../../agents/pi-tools.js";
|
|
4
|
+
import { resolveSandboxRuntimeStatus } from "../../agents/sandbox.js";
|
|
5
|
+
import { buildWorkspaceSkillSnapshot } from "../../agents/skills.js";
|
|
6
|
+
import { getSkillsSnapshotVersion } from "../../agents/skills/refresh.js";
|
|
7
|
+
import { buildAgentSystemPrompt } from "../../agents/system-prompt.js";
|
|
8
|
+
import { buildSystemPromptReport } from "../../agents/system-prompt-report.js";
|
|
9
|
+
import { buildSystemPromptParams } from "../../agents/system-prompt-params.js";
|
|
10
|
+
import { resolveDefaultModelForAgent } from "../../agents/model-selection.js";
|
|
11
|
+
import { buildToolSummaryMap } from "../../agents/tool-summaries.js";
|
|
12
|
+
import { resolveBootstrapContextForRun } from "../../agents/bootstrap-files.js";
|
|
13
|
+
import { getRemoteSkillEligibility } from "../../infra/skills-remote.js";
|
|
14
|
+
import { buildTtsSystemPromptHint } from "../../tts/tts.js";
|
|
15
|
+
function estimateTokensFromChars(chars) {
|
|
16
|
+
return Math.ceil(Math.max(0, chars) / 4);
|
|
17
|
+
}
|
|
18
|
+
function formatInt(n) {
|
|
19
|
+
return new Intl.NumberFormat("en-US").format(n);
|
|
20
|
+
}
|
|
21
|
+
function formatCharsAndTokens(chars) {
|
|
22
|
+
return `${formatInt(chars)} chars (~${formatInt(estimateTokensFromChars(chars))} tok)`;
|
|
23
|
+
}
|
|
24
|
+
function parseContextArgs(commandBodyNormalized) {
|
|
25
|
+
if (commandBodyNormalized === "/context")
|
|
26
|
+
return "";
|
|
27
|
+
if (commandBodyNormalized.startsWith("/context "))
|
|
28
|
+
return commandBodyNormalized.slice(8).trim();
|
|
29
|
+
return "";
|
|
30
|
+
}
|
|
31
|
+
function formatListTop(entries, cap) {
|
|
32
|
+
const sorted = [...entries].sort((a, b) => b.value - a.value);
|
|
33
|
+
const top = sorted.slice(0, cap);
|
|
34
|
+
const omitted = Math.max(0, sorted.length - top.length);
|
|
35
|
+
const lines = top.map((e) => `- ${e.name}: ${formatCharsAndTokens(e.value)}`);
|
|
36
|
+
return { lines, omitted };
|
|
37
|
+
}
|
|
38
|
+
async function resolveContextReport(params) {
|
|
39
|
+
const existing = params.sessionEntry?.systemPromptReport;
|
|
40
|
+
if (existing && existing.source === "run")
|
|
41
|
+
return existing;
|
|
42
|
+
const workspaceDir = params.workspaceDir;
|
|
43
|
+
const bootstrapMaxChars = resolveBootstrapMaxChars(params.cfg);
|
|
44
|
+
const { bootstrapFiles, contextFiles: injectedFiles } = await resolveBootstrapContextForRun({
|
|
45
|
+
workspaceDir,
|
|
46
|
+
config: params.cfg,
|
|
47
|
+
sessionKey: params.sessionKey,
|
|
48
|
+
sessionId: params.sessionEntry?.sessionId,
|
|
49
|
+
});
|
|
50
|
+
const skillsSnapshot = (() => {
|
|
51
|
+
try {
|
|
52
|
+
return buildWorkspaceSkillSnapshot(workspaceDir, {
|
|
53
|
+
config: params.cfg,
|
|
54
|
+
eligibility: { remote: getRemoteSkillEligibility() },
|
|
55
|
+
snapshotVersion: getSkillsSnapshotVersion(workspaceDir),
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return { prompt: "", skills: [], resolvedSkills: [] };
|
|
60
|
+
}
|
|
61
|
+
})();
|
|
62
|
+
const skillsPrompt = skillsSnapshot.prompt ?? "";
|
|
63
|
+
const sandboxRuntime = resolveSandboxRuntimeStatus({
|
|
64
|
+
cfg: params.cfg,
|
|
65
|
+
sessionKey: params.ctx.SessionKey ?? params.sessionKey,
|
|
66
|
+
});
|
|
67
|
+
const tools = (() => {
|
|
68
|
+
try {
|
|
69
|
+
return createPoolbotCodingTools({
|
|
70
|
+
config: params.cfg,
|
|
71
|
+
workspaceDir,
|
|
72
|
+
sessionKey: params.sessionKey,
|
|
73
|
+
messageProvider: params.command.channel,
|
|
74
|
+
groupId: params.sessionEntry?.groupId ?? undefined,
|
|
75
|
+
groupChannel: params.sessionEntry?.groupChannel ?? undefined,
|
|
76
|
+
groupSpace: params.sessionEntry?.space ?? undefined,
|
|
77
|
+
spawnedBy: params.sessionEntry?.spawnedBy ?? undefined,
|
|
78
|
+
senderIsOwner: params.command.senderIsOwner,
|
|
79
|
+
modelProvider: params.provider,
|
|
80
|
+
modelId: params.model,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
})();
|
|
87
|
+
const toolSummaries = buildToolSummaryMap(tools);
|
|
88
|
+
const toolNames = tools.map((t) => t.name);
|
|
89
|
+
const { sessionAgentId } = resolveSessionAgentIds({
|
|
90
|
+
sessionKey: params.sessionKey,
|
|
91
|
+
config: params.cfg,
|
|
92
|
+
});
|
|
93
|
+
const defaultModelRef = resolveDefaultModelForAgent({
|
|
94
|
+
cfg: params.cfg,
|
|
95
|
+
agentId: sessionAgentId,
|
|
96
|
+
});
|
|
97
|
+
const defaultModelLabel = `${defaultModelRef.provider}/${defaultModelRef.model}`;
|
|
98
|
+
const { runtimeInfo, userTimezone, userTime, userTimeFormat } = buildSystemPromptParams({
|
|
99
|
+
config: params.cfg,
|
|
100
|
+
agentId: sessionAgentId,
|
|
101
|
+
workspaceDir,
|
|
102
|
+
cwd: process.cwd(),
|
|
103
|
+
runtime: {
|
|
104
|
+
host: "unknown",
|
|
105
|
+
os: "unknown",
|
|
106
|
+
arch: "unknown",
|
|
107
|
+
node: process.version,
|
|
108
|
+
model: `${params.provider}/${params.model}`,
|
|
109
|
+
defaultModel: defaultModelLabel,
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
const sandboxInfo = sandboxRuntime.sandboxed
|
|
113
|
+
? {
|
|
114
|
+
enabled: true,
|
|
115
|
+
workspaceDir,
|
|
116
|
+
workspaceAccess: "rw",
|
|
117
|
+
elevated: {
|
|
118
|
+
allowed: params.elevated.allowed,
|
|
119
|
+
defaultLevel: (params.resolvedElevatedLevel ?? "off"),
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
: { enabled: false };
|
|
123
|
+
const ttsHint = params.cfg ? buildTtsSystemPromptHint(params.cfg) : undefined;
|
|
124
|
+
const systemPrompt = buildAgentSystemPrompt({
|
|
125
|
+
workspaceDir,
|
|
126
|
+
defaultThinkLevel: params.resolvedThinkLevel,
|
|
127
|
+
reasoningLevel: params.resolvedReasoningLevel,
|
|
128
|
+
extraSystemPrompt: undefined,
|
|
129
|
+
ownerNumbers: undefined,
|
|
130
|
+
reasoningTagHint: false,
|
|
131
|
+
toolNames,
|
|
132
|
+
toolSummaries,
|
|
133
|
+
modelAliasLines: [],
|
|
134
|
+
userTimezone,
|
|
135
|
+
userTime,
|
|
136
|
+
userTimeFormat,
|
|
137
|
+
contextFiles: injectedFiles,
|
|
138
|
+
skillsPrompt,
|
|
139
|
+
heartbeatPrompt: undefined,
|
|
140
|
+
ttsHint,
|
|
141
|
+
runtimeInfo,
|
|
142
|
+
sandboxInfo,
|
|
143
|
+
});
|
|
144
|
+
return buildSystemPromptReport({
|
|
145
|
+
source: "estimate",
|
|
146
|
+
generatedAt: Date.now(),
|
|
147
|
+
sessionId: params.sessionEntry?.sessionId,
|
|
148
|
+
sessionKey: params.sessionKey,
|
|
149
|
+
provider: params.provider,
|
|
150
|
+
model: params.model,
|
|
151
|
+
workspaceDir,
|
|
152
|
+
bootstrapMaxChars,
|
|
153
|
+
sandbox: { mode: sandboxRuntime.mode, sandboxed: sandboxRuntime.sandboxed },
|
|
154
|
+
systemPrompt,
|
|
155
|
+
bootstrapFiles,
|
|
156
|
+
injectedFiles,
|
|
157
|
+
skillsPrompt,
|
|
158
|
+
tools,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export async function buildContextReply(params) {
|
|
162
|
+
const args = parseContextArgs(params.command.commandBodyNormalized);
|
|
163
|
+
const sub = args.split(/\s+/).filter(Boolean)[0]?.toLowerCase() ?? "";
|
|
164
|
+
if (!sub || sub === "help") {
|
|
165
|
+
return {
|
|
166
|
+
text: [
|
|
167
|
+
"🧠 /context",
|
|
168
|
+
"",
|
|
169
|
+
"What counts as context (high-level), plus a breakdown mode.",
|
|
170
|
+
"",
|
|
171
|
+
"Try:",
|
|
172
|
+
"- /context list (short breakdown)",
|
|
173
|
+
"- /context detail (per-file + per-tool + per-skill + system prompt size)",
|
|
174
|
+
"- /context json (same, machine-readable)",
|
|
175
|
+
"",
|
|
176
|
+
"Inline shortcut = a command token inside a normal message (e.g. “hey /status”). It runs immediately (allowlisted senders only) and is stripped before the model sees the remaining text.",
|
|
177
|
+
].join("\n"),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
const report = await resolveContextReport(params);
|
|
181
|
+
const session = {
|
|
182
|
+
totalTokens: params.sessionEntry?.totalTokens ?? null,
|
|
183
|
+
inputTokens: params.sessionEntry?.inputTokens ?? null,
|
|
184
|
+
outputTokens: params.sessionEntry?.outputTokens ?? null,
|
|
185
|
+
contextTokens: params.contextTokens ?? null,
|
|
186
|
+
};
|
|
187
|
+
if (sub === "json") {
|
|
188
|
+
return { text: JSON.stringify({ report, session }, null, 2) };
|
|
189
|
+
}
|
|
190
|
+
if (sub !== "list" && sub !== "show" && sub !== "detail" && sub !== "deep") {
|
|
191
|
+
return {
|
|
192
|
+
text: [
|
|
193
|
+
"Unknown /context mode.",
|
|
194
|
+
"Use: /context, /context list, /context detail, or /context json",
|
|
195
|
+
].join("\n"),
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
const fileLines = report.injectedWorkspaceFiles.map((f) => {
|
|
199
|
+
const status = f.missing ? "MISSING" : f.truncated ? "TRUNCATED" : "OK";
|
|
200
|
+
const raw = f.missing ? "0" : formatCharsAndTokens(f.rawChars);
|
|
201
|
+
const injected = f.missing ? "0" : formatCharsAndTokens(f.injectedChars);
|
|
202
|
+
return `- ${f.name}: ${status} | raw ${raw} | injected ${injected}`;
|
|
203
|
+
});
|
|
204
|
+
const sandboxLine = `Sandbox: mode=${report.sandbox?.mode ?? "unknown"} sandboxed=${report.sandbox?.sandboxed ?? false}`;
|
|
205
|
+
const toolSchemaLine = `Tool schemas (JSON): ${formatCharsAndTokens(report.tools.schemaChars)} (counts toward context; not shown as text)`;
|
|
206
|
+
const toolListLine = `Tool list (system prompt text): ${formatCharsAndTokens(report.tools.listChars)}`;
|
|
207
|
+
const skillNameSet = new Set(report.skills.entries.map((s) => s.name));
|
|
208
|
+
const skillNames = Array.from(skillNameSet);
|
|
209
|
+
const toolNames = report.tools.entries.map((t) => t.name);
|
|
210
|
+
const formatNameList = (names, cap) => names.length <= cap
|
|
211
|
+
? names.join(", ")
|
|
212
|
+
: `${names.slice(0, cap).join(", ")}, … (+${names.length - cap} more)`;
|
|
213
|
+
const skillsLine = `Skills list (system prompt text): ${formatCharsAndTokens(report.skills.promptChars)} (${skillNameSet.size} skills)`;
|
|
214
|
+
const skillsNamesLine = skillNameSet.size
|
|
215
|
+
? `Skills: ${formatNameList(skillNames, 20)}`
|
|
216
|
+
: "Skills: (none)";
|
|
217
|
+
const toolsNamesLine = toolNames.length
|
|
218
|
+
? `Tools: ${formatNameList(toolNames, 30)}`
|
|
219
|
+
: "Tools: (none)";
|
|
220
|
+
const systemPromptLine = `System prompt (${report.source}): ${formatCharsAndTokens(report.systemPrompt.chars)} (Project Context ${formatCharsAndTokens(report.systemPrompt.projectContextChars)})`;
|
|
221
|
+
const workspaceLabel = report.workspaceDir ?? params.workspaceDir;
|
|
222
|
+
const bootstrapMaxLabel = typeof report.bootstrapMaxChars === "number"
|
|
223
|
+
? `${formatInt(report.bootstrapMaxChars)} chars`
|
|
224
|
+
: "? chars";
|
|
225
|
+
const totalsLine = session.totalTokens != null
|
|
226
|
+
? `Session tokens (cached): ${formatInt(session.totalTokens)} total / ctx=${session.contextTokens ?? "?"}`
|
|
227
|
+
: `Session tokens (cached): unknown / ctx=${session.contextTokens ?? "?"}`;
|
|
228
|
+
if (sub === "detail" || sub === "deep") {
|
|
229
|
+
const perSkill = formatListTop(report.skills.entries.map((s) => ({ name: s.name, value: s.blockChars })), 30);
|
|
230
|
+
const perToolSchema = formatListTop(report.tools.entries.map((t) => ({ name: t.name, value: t.schemaChars })), 30);
|
|
231
|
+
const perToolSummary = formatListTop(report.tools.entries.map((t) => ({ name: t.name, value: t.summaryChars })), 30);
|
|
232
|
+
const toolPropsLines = report.tools.entries
|
|
233
|
+
.filter((t) => t.propertiesCount != null)
|
|
234
|
+
.sort((a, b) => (b.propertiesCount ?? 0) - (a.propertiesCount ?? 0))
|
|
235
|
+
.slice(0, 30)
|
|
236
|
+
.map((t) => `- ${t.name}: ${t.propertiesCount} params`);
|
|
237
|
+
return {
|
|
238
|
+
text: [
|
|
239
|
+
"🧠 Context breakdown (detailed)",
|
|
240
|
+
`Workspace: ${workspaceLabel}`,
|
|
241
|
+
`Bootstrap max/file: ${bootstrapMaxLabel}`,
|
|
242
|
+
sandboxLine,
|
|
243
|
+
systemPromptLine,
|
|
244
|
+
"",
|
|
245
|
+
"Injected workspace files:",
|
|
246
|
+
...fileLines,
|
|
247
|
+
"",
|
|
248
|
+
skillsLine,
|
|
249
|
+
skillsNamesLine,
|
|
250
|
+
...(perSkill.lines.length ? ["Top skills (prompt entry size):", ...perSkill.lines] : []),
|
|
251
|
+
...(perSkill.omitted ? [`… (+${perSkill.omitted} more skills)`] : []),
|
|
252
|
+
"",
|
|
253
|
+
toolListLine,
|
|
254
|
+
toolSchemaLine,
|
|
255
|
+
toolsNamesLine,
|
|
256
|
+
"Top tools (schema size):",
|
|
257
|
+
...perToolSchema.lines,
|
|
258
|
+
...(perToolSchema.omitted ? [`… (+${perToolSchema.omitted} more tools)`] : []),
|
|
259
|
+
"",
|
|
260
|
+
"Top tools (summary text size):",
|
|
261
|
+
...perToolSummary.lines,
|
|
262
|
+
...(perToolSummary.omitted ? [`… (+${perToolSummary.omitted} more tools)`] : []),
|
|
263
|
+
...(toolPropsLines.length ? ["", "Tools (param count):", ...toolPropsLines] : []),
|
|
264
|
+
"",
|
|
265
|
+
totalsLine,
|
|
266
|
+
"",
|
|
267
|
+
"Inline shortcut: a command token inside normal text (e.g. “hey /status”) that runs immediately (allowlisted senders only) and is stripped before the model sees the remaining message.",
|
|
268
|
+
]
|
|
269
|
+
.filter(Boolean)
|
|
270
|
+
.join("\n"),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
return {
|
|
274
|
+
text: [
|
|
275
|
+
"🧠 Context breakdown",
|
|
276
|
+
`Workspace: ${workspaceLabel}`,
|
|
277
|
+
`Bootstrap max/file: ${bootstrapMaxLabel}`,
|
|
278
|
+
sandboxLine,
|
|
279
|
+
systemPromptLine,
|
|
280
|
+
"",
|
|
281
|
+
"Injected workspace files:",
|
|
282
|
+
...fileLines,
|
|
283
|
+
"",
|
|
284
|
+
skillsLine,
|
|
285
|
+
skillsNamesLine,
|
|
286
|
+
toolListLine,
|
|
287
|
+
toolSchemaLine,
|
|
288
|
+
toolsNamesLine,
|
|
289
|
+
"",
|
|
290
|
+
totalsLine,
|
|
291
|
+
"",
|
|
292
|
+
"Inline shortcut: a command token inside normal text (e.g. “hey /status”) that runs immediately (allowlisted senders only) and is stripped before the model sees the remaining message.",
|
|
293
|
+
].join("\n"),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { resolveCommandAuthorization } from "../command-auth.js";
|
|
2
|
+
import { normalizeCommandBody } from "../commands-registry.js";
|
|
3
|
+
import { stripMentions } from "./mentions.js";
|
|
4
|
+
export function buildCommandContext(params) {
|
|
5
|
+
const { ctx, cfg, agentId, sessionKey, isGroup, triggerBodyNormalized } = params;
|
|
6
|
+
const auth = resolveCommandAuthorization({
|
|
7
|
+
ctx,
|
|
8
|
+
cfg,
|
|
9
|
+
commandAuthorized: params.commandAuthorized,
|
|
10
|
+
});
|
|
11
|
+
const surface = (ctx.Surface ?? ctx.Provider ?? "").trim().toLowerCase();
|
|
12
|
+
const channel = (ctx.Provider ?? surface).trim().toLowerCase();
|
|
13
|
+
const abortKey = sessionKey ?? (auth.from || undefined) ?? (auth.to || undefined);
|
|
14
|
+
const rawBodyNormalized = triggerBodyNormalized;
|
|
15
|
+
const commandBodyNormalized = normalizeCommandBody(isGroup ? stripMentions(rawBodyNormalized, ctx, cfg, agentId) : rawBodyNormalized);
|
|
16
|
+
return {
|
|
17
|
+
surface,
|
|
18
|
+
channel,
|
|
19
|
+
channelId: auth.providerId,
|
|
20
|
+
ownerList: auth.ownerList,
|
|
21
|
+
senderIsOwner: auth.senderIsOwner,
|
|
22
|
+
isAuthorizedSender: auth.isAuthorizedSender,
|
|
23
|
+
senderId: auth.senderId,
|
|
24
|
+
abortKey,
|
|
25
|
+
rawBodyNormalized,
|
|
26
|
+
commandBodyNormalized,
|
|
27
|
+
from: auth.from,
|
|
28
|
+
to: auth.to,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import { logVerbose } from "../../globals.js";
|
|
3
|
+
import { createInternalHookEvent, triggerInternalHook } from "../../hooks/internal-hooks.js";
|
|
4
|
+
import { getGlobalHookRunner } from "../../plugins/hook-runner-global.js";
|
|
5
|
+
import { resolveSendPolicy } from "../../sessions/send-policy.js";
|
|
6
|
+
import { shouldHandleTextCommands } from "../commands-registry.js";
|
|
7
|
+
import { handleAllowlistCommand } from "./commands-allowlist.js";
|
|
8
|
+
import { handleApproveCommand } from "./commands-approve.js";
|
|
9
|
+
import { handleBashCommand } from "./commands-bash.js";
|
|
10
|
+
import { handleCompactCommand } from "./commands-compact.js";
|
|
11
|
+
import { handleConfigCommand, handleDebugCommand } from "./commands-config.js";
|
|
12
|
+
import { handleCommandsListCommand, handleContextCommand, handleExportSessionCommand, handleHelpCommand, handleStatusCommand, handleWhoamiCommand, } from "./commands-info.js";
|
|
13
|
+
import { handleModelsCommand } from "./commands-models.js";
|
|
14
|
+
import { handlePluginCommand } from "./commands-plugin.js";
|
|
15
|
+
import { handleAbortTrigger, handleActivationCommand, handleRestartCommand, handleSessionCommand, handleSendPolicyCommand, handleStopCommand, handleUsageCommand, } from "./commands-session.js";
|
|
16
|
+
import { handleSubagentsCommand } from "./commands-subagents.js";
|
|
17
|
+
import { handleTtsCommands } from "./commands-tts.js";
|
|
18
|
+
import { routeReply } from "./route-reply.js";
|
|
19
|
+
let HANDLERS = null;
|
|
20
|
+
export async function handleCommands(params) {
|
|
21
|
+
if (HANDLERS === null) {
|
|
22
|
+
HANDLERS = [
|
|
23
|
+
// Plugin commands are processed first, before built-in commands
|
|
24
|
+
handlePluginCommand,
|
|
25
|
+
handleBashCommand,
|
|
26
|
+
handleActivationCommand,
|
|
27
|
+
handleSendPolicyCommand,
|
|
28
|
+
handleUsageCommand,
|
|
29
|
+
handleSessionCommand,
|
|
30
|
+
handleRestartCommand,
|
|
31
|
+
handleTtsCommands,
|
|
32
|
+
handleHelpCommand,
|
|
33
|
+
handleCommandsListCommand,
|
|
34
|
+
handleStatusCommand,
|
|
35
|
+
handleAllowlistCommand,
|
|
36
|
+
handleApproveCommand,
|
|
37
|
+
handleContextCommand,
|
|
38
|
+
handleExportSessionCommand,
|
|
39
|
+
handleWhoamiCommand,
|
|
40
|
+
handleSubagentsCommand,
|
|
41
|
+
handleConfigCommand,
|
|
42
|
+
handleDebugCommand,
|
|
43
|
+
handleModelsCommand,
|
|
44
|
+
handleStopCommand,
|
|
45
|
+
handleCompactCommand,
|
|
46
|
+
handleAbortTrigger,
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
const resetMatch = params.command.commandBodyNormalized.match(/^\/(new|reset)(?:\s|$)/);
|
|
50
|
+
const resetRequested = Boolean(resetMatch);
|
|
51
|
+
if (resetRequested && !params.command.isAuthorizedSender) {
|
|
52
|
+
logVerbose(`Ignoring /reset from unauthorized sender: ${params.command.senderId || "<unknown>"}`);
|
|
53
|
+
return { shouldContinue: false };
|
|
54
|
+
}
|
|
55
|
+
// Trigger internal hook for reset/new commands
|
|
56
|
+
if (resetRequested && params.command.isAuthorizedSender) {
|
|
57
|
+
const commandAction = resetMatch?.[1] ?? "new";
|
|
58
|
+
const hookEvent = createInternalHookEvent("command", commandAction, params.sessionKey ?? "", {
|
|
59
|
+
sessionEntry: params.sessionEntry,
|
|
60
|
+
previousSessionEntry: params.previousSessionEntry,
|
|
61
|
+
commandSource: params.command.surface,
|
|
62
|
+
senderId: params.command.senderId,
|
|
63
|
+
cfg: params.cfg, // Pass config for LLM slug generation
|
|
64
|
+
});
|
|
65
|
+
await triggerInternalHook(hookEvent);
|
|
66
|
+
// Send hook messages immediately if present
|
|
67
|
+
if (hookEvent.messages.length > 0) {
|
|
68
|
+
// Use OriginatingChannel/To if available, otherwise fall back to command channel/from
|
|
69
|
+
// oxlint-disable-next-line typescript/no-explicit-any
|
|
70
|
+
const channel = params.ctx.OriginatingChannel || params.command.channel;
|
|
71
|
+
// For replies, use 'from' (the sender) not 'to' (which might be the bot itself)
|
|
72
|
+
const to = params.ctx.OriginatingTo || params.command.from || params.command.to;
|
|
73
|
+
if (channel && to) {
|
|
74
|
+
const hookReply = { text: hookEvent.messages.join("\n\n") };
|
|
75
|
+
await routeReply({
|
|
76
|
+
payload: hookReply,
|
|
77
|
+
channel: channel,
|
|
78
|
+
to: to,
|
|
79
|
+
sessionKey: params.sessionKey,
|
|
80
|
+
accountId: params.ctx.AccountId,
|
|
81
|
+
threadId: params.ctx.MessageThreadId,
|
|
82
|
+
cfg: params.cfg,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
// Fire before_reset plugin hook — extract memories before session history is lost
|
|
87
|
+
const hookRunner = getGlobalHookRunner();
|
|
88
|
+
if (hookRunner?.hasHooks("before_reset")) {
|
|
89
|
+
const prevEntry = params.previousSessionEntry;
|
|
90
|
+
const sessionFile = prevEntry?.sessionFile;
|
|
91
|
+
// Fire-and-forget: read old session messages and run hook
|
|
92
|
+
void (async () => {
|
|
93
|
+
try {
|
|
94
|
+
const messages = [];
|
|
95
|
+
if (sessionFile) {
|
|
96
|
+
const content = await fs.readFile(sessionFile, "utf-8");
|
|
97
|
+
for (const line of content.split("\n")) {
|
|
98
|
+
if (!line.trim()) {
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const entry = JSON.parse(line);
|
|
103
|
+
if (entry.type === "message" && entry.message) {
|
|
104
|
+
messages.push(entry.message);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// skip malformed lines
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
logVerbose("before_reset: no session file available, firing hook with empty messages");
|
|
114
|
+
}
|
|
115
|
+
await hookRunner.runBeforeReset({ sessionFile, messages, reason: commandAction }, {
|
|
116
|
+
agentId: params.sessionKey?.split(":")[0] ?? "main",
|
|
117
|
+
sessionKey: params.sessionKey,
|
|
118
|
+
sessionId: prevEntry?.sessionId,
|
|
119
|
+
workspaceDir: params.workspaceDir,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
catch (err) {
|
|
123
|
+
logVerbose(`before_reset hook failed: ${String(err)}`);
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const allowTextCommands = shouldHandleTextCommands({
|
|
129
|
+
cfg: params.cfg,
|
|
130
|
+
surface: params.command.surface,
|
|
131
|
+
commandSource: params.ctx.CommandSource,
|
|
132
|
+
});
|
|
133
|
+
for (const handler of HANDLERS) {
|
|
134
|
+
const result = await handler(params, allowTextCommands);
|
|
135
|
+
if (result) {
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const sendPolicy = resolveSendPolicy({
|
|
140
|
+
cfg: params.cfg,
|
|
141
|
+
entry: params.sessionEntry,
|
|
142
|
+
sessionKey: params.sessionKey,
|
|
143
|
+
channel: params.sessionEntry?.channel ?? params.command.channel,
|
|
144
|
+
chatType: params.sessionEntry?.chatType,
|
|
145
|
+
});
|
|
146
|
+
if (sendPolicy === "deny") {
|
|
147
|
+
logVerbose(`Send blocked by policy for session ${params.sessionKey ?? "unknown"}`);
|
|
148
|
+
return { shouldContinue: false };
|
|
149
|
+
}
|
|
150
|
+
return { shouldContinue: true };
|
|
151
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { SessionManager } from "@mariozechner/pi-coding-agent";
|
|
5
|
+
import { resolveDefaultSessionStorePath, resolveSessionFilePath, } from "../../config/sessions/paths.js";
|
|
6
|
+
import { loadSessionStore } from "../../config/sessions/store.js";
|
|
7
|
+
import { resolveCommandsSystemPromptBundle } from "./commands-system-prompt.js";
|
|
8
|
+
// Export HTML templates are bundled with this module
|
|
9
|
+
const EXPORT_HTML_DIR = path.join(path.dirname(fileURLToPath(import.meta.url)), "export-html");
|
|
10
|
+
function loadTemplate(fileName) {
|
|
11
|
+
return fs.readFileSync(path.join(EXPORT_HTML_DIR, fileName), "utf-8");
|
|
12
|
+
}
|
|
13
|
+
function generateHtml(sessionData) {
|
|
14
|
+
const template = loadTemplate("template.html");
|
|
15
|
+
const templateCss = loadTemplate("template.css");
|
|
16
|
+
const templateJs = loadTemplate("template.js");
|
|
17
|
+
const markedJs = loadTemplate(path.join("vendor", "marked.min.js"));
|
|
18
|
+
const hljsJs = loadTemplate(path.join("vendor", "highlight.min.js"));
|
|
19
|
+
// Use pi-mono dark theme colors (matching their theme/dark.json)
|
|
20
|
+
const themeVars = `
|
|
21
|
+
--cyan: #00d7ff;
|
|
22
|
+
--blue: #5f87ff;
|
|
23
|
+
--green: #b5bd68;
|
|
24
|
+
--red: #cc6666;
|
|
25
|
+
--yellow: #ffff00;
|
|
26
|
+
--gray: #808080;
|
|
27
|
+
--dimGray: #666666;
|
|
28
|
+
--darkGray: #505050;
|
|
29
|
+
--accent: #8abeb7;
|
|
30
|
+
--selectedBg: #3a3a4a;
|
|
31
|
+
--userMsgBg: #343541;
|
|
32
|
+
--toolPendingBg: #282832;
|
|
33
|
+
--toolSuccessBg: #283228;
|
|
34
|
+
--toolErrorBg: #3c2828;
|
|
35
|
+
--customMsgBg: #2d2838;
|
|
36
|
+
--text: #e0e0e0;
|
|
37
|
+
--dim: #666666;
|
|
38
|
+
--muted: #808080;
|
|
39
|
+
--border: #5f87ff;
|
|
40
|
+
--borderAccent: #00d7ff;
|
|
41
|
+
--borderMuted: #505050;
|
|
42
|
+
--success: #b5bd68;
|
|
43
|
+
--error: #cc6666;
|
|
44
|
+
--warning: #ffff00;
|
|
45
|
+
--thinkingText: #808080;
|
|
46
|
+
--userMessageBg: #343541;
|
|
47
|
+
--userMessageText: #e0e0e0;
|
|
48
|
+
--customMessageBg: #2d2838;
|
|
49
|
+
--customMessageText: #e0e0e0;
|
|
50
|
+
--customMessageLabel: #9575cd;
|
|
51
|
+
--toolTitle: #e0e0e0;
|
|
52
|
+
--toolOutput: #808080;
|
|
53
|
+
--mdHeading: #f0c674;
|
|
54
|
+
--mdLink: #81a2be;
|
|
55
|
+
--mdLinkUrl: #666666;
|
|
56
|
+
--mdCode: #8abeb7;
|
|
57
|
+
--mdCodeBlock: #b5bd68;
|
|
58
|
+
`;
|
|
59
|
+
const bodyBg = "#1e1e28";
|
|
60
|
+
const containerBg = "#282832";
|
|
61
|
+
const infoBg = "#343541";
|
|
62
|
+
// Base64 encode session data
|
|
63
|
+
const sessionDataBase64 = Buffer.from(JSON.stringify(sessionData)).toString("base64");
|
|
64
|
+
// Build CSS with theme variables
|
|
65
|
+
const css = templateCss
|
|
66
|
+
.replace("/* {{THEME_VARS}} */", themeVars.trim())
|
|
67
|
+
.replace("/* {{BODY_BG_DECL}} */", `--body-bg: ${bodyBg};`)
|
|
68
|
+
.replace("/* {{CONTAINER_BG_DECL}} */", `--container-bg: ${containerBg};`)
|
|
69
|
+
.replace("/* {{INFO_BG_DECL}} */", `--info-bg: ${infoBg};`);
|
|
70
|
+
return template
|
|
71
|
+
.replace("{{CSS}}", css)
|
|
72
|
+
.replace("{{JS}}", templateJs)
|
|
73
|
+
.replace("{{SESSION_DATA}}", sessionDataBase64)
|
|
74
|
+
.replace("{{MARKED_JS}}", markedJs)
|
|
75
|
+
.replace("{{HIGHLIGHT_JS}}", hljsJs);
|
|
76
|
+
}
|
|
77
|
+
function parseExportArgs(commandBodyNormalized) {
|
|
78
|
+
const normalized = commandBodyNormalized.trim();
|
|
79
|
+
if (normalized === "/export-session" || normalized === "/export") {
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
const args = normalized.replace(/^\/(export-session|export)\s*/, "").trim();
|
|
83
|
+
// First non-flag argument is the output path
|
|
84
|
+
const outputPath = args.split(/\s+/).find((part) => !part.startsWith("-"));
|
|
85
|
+
return { outputPath };
|
|
86
|
+
}
|
|
87
|
+
export async function buildExportSessionReply(params) {
|
|
88
|
+
const args = parseExportArgs(params.command.commandBodyNormalized);
|
|
89
|
+
// 1. Resolve session file
|
|
90
|
+
const sessionEntry = params.sessionEntry;
|
|
91
|
+
if (!sessionEntry?.sessionId) {
|
|
92
|
+
return { text: "❌ No active session found." };
|
|
93
|
+
}
|
|
94
|
+
const storePath = resolveDefaultSessionStorePath(params.agentId);
|
|
95
|
+
const store = loadSessionStore(storePath, { skipCache: true });
|
|
96
|
+
const entry = store[params.sessionKey];
|
|
97
|
+
if (!entry?.sessionId) {
|
|
98
|
+
return { text: `❌ Session not found: ${params.sessionKey}` };
|
|
99
|
+
}
|
|
100
|
+
let sessionFile;
|
|
101
|
+
try {
|
|
102
|
+
sessionFile = resolveSessionFilePath(entry.sessionId, entry, {
|
|
103
|
+
agentId: params.agentId,
|
|
104
|
+
sessionsDir: path.dirname(storePath),
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
text: `❌ Failed to resolve session file: ${err instanceof Error ? err.message : String(err)}`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (!fs.existsSync(sessionFile)) {
|
|
113
|
+
return { text: `❌ Session file not found: ${sessionFile}` };
|
|
114
|
+
}
|
|
115
|
+
// 2. Load session entries
|
|
116
|
+
const sessionManager = SessionManager.open(sessionFile);
|
|
117
|
+
const entries = sessionManager.getEntries();
|
|
118
|
+
const header = sessionManager.getHeader();
|
|
119
|
+
const leafId = sessionManager.getLeafId();
|
|
120
|
+
// 3. Build full system prompt
|
|
121
|
+
const { systemPrompt, tools } = await resolveCommandsSystemPromptBundle(params);
|
|
122
|
+
// 4. Prepare session data
|
|
123
|
+
const sessionData = {
|
|
124
|
+
header,
|
|
125
|
+
entries,
|
|
126
|
+
leafId,
|
|
127
|
+
systemPrompt,
|
|
128
|
+
tools: tools.map((t) => ({
|
|
129
|
+
name: t.name,
|
|
130
|
+
description: t.description,
|
|
131
|
+
parameters: t.parameters,
|
|
132
|
+
})),
|
|
133
|
+
};
|
|
134
|
+
// 5. Generate HTML
|
|
135
|
+
const html = generateHtml(sessionData);
|
|
136
|
+
// 6. Determine output path
|
|
137
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
138
|
+
const defaultFileName = `poolbot-session-${entry.sessionId.slice(0, 8)}-${timestamp}.html`;
|
|
139
|
+
const outputPath = args.outputPath
|
|
140
|
+
? path.resolve(args.outputPath.startsWith("~")
|
|
141
|
+
? args.outputPath.replace("~", process.env.HOME ?? "")
|
|
142
|
+
: args.outputPath)
|
|
143
|
+
: path.join(params.workspaceDir, defaultFileName);
|
|
144
|
+
// Ensure directory exists
|
|
145
|
+
const outputDir = path.dirname(outputPath);
|
|
146
|
+
if (!fs.existsSync(outputDir)) {
|
|
147
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
148
|
+
}
|
|
149
|
+
// 7. Write file
|
|
150
|
+
fs.writeFileSync(outputPath, html, "utf-8");
|
|
151
|
+
const relativePath = path.relative(params.workspaceDir, outputPath);
|
|
152
|
+
const displayPath = relativePath.startsWith("..") ? outputPath : relativePath;
|
|
153
|
+
return {
|
|
154
|
+
text: [
|
|
155
|
+
"✅ Session exported!",
|
|
156
|
+
"",
|
|
157
|
+
`📄 File: ${displayPath}`,
|
|
158
|
+
`📊 Entries: ${entries.length}`,
|
|
159
|
+
`🧠 System prompt: ${systemPrompt.length.toLocaleString()} chars`,
|
|
160
|
+
`🔧 Tools: ${tools.length}`,
|
|
161
|
+
].join("\n"),
|
|
162
|
+
};
|
|
163
|
+
}
|