@poolzin/pool-bot 2026.2.23 → 2026.2.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +29 -0
- package/dist/acp/client.js +207 -18
- package/dist/acp/secret-file.js +22 -0
- package/dist/agents/agent-scope.js +10 -0
- package/dist/agents/bash-process-registry.test-helpers.js +29 -0
- package/dist/agents/bash-tools.exec-approval-request.js +20 -0
- package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
- package/dist/agents/bash-tools.exec-host-node.js +235 -0
- package/dist/agents/bash-tools.exec-types.js +1 -0
- package/dist/agents/bash-tools.process.js +224 -218
- package/dist/agents/content-blocks.js +16 -0
- package/dist/agents/model-fallback.js +96 -101
- package/dist/agents/models-config.providers.js +299 -182
- package/dist/agents/pi-embedded-payloads.js +1 -0
- package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
- package/dist/agents/skills.test-helpers.js +13 -0
- package/dist/agents/stable-stringify.js +12 -0
- package/dist/agents/subagent-registry.mocks.shared.js +12 -0
- package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
- package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
- package/dist/agents/tool-policy-shared.js +108 -0
- package/dist/agents/tools/browser-tool.js +160 -54
- package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
- package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
- package/dist/agents/tools/image-tool.js +214 -99
- package/dist/agents/tools/sessions-history-tool.js +140 -108
- package/dist/agents/workspace.js +222 -46
- package/dist/auto-reply/commands-registry.js +15 -18
- package/dist/auto-reply/fallback-state.js +114 -0
- package/dist/auto-reply/model-runtime.js +68 -0
- package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
- package/dist/auto-reply/reply/agent-runner.js +165 -39
- package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
- package/dist/browser/config.js +26 -0
- package/dist/browser/navigation-guard.js +31 -0
- package/dist/browser/routes/agent.act.js +431 -424
- package/dist/browser/routes/agent.shared.js +47 -3
- package/dist/browser/routes/agent.snapshot.js +122 -116
- package/dist/browser/routes/agent.storage.js +303 -297
- package/dist/browser/routes/tabs.js +154 -100
- package/dist/browser/server-lifecycle.js +37 -0
- package/dist/build-info.json +3 -3
- package/dist/channels/allow-from.js +25 -0
- package/dist/channels/plugins/account-action-gate.js +13 -0
- package/dist/channels/plugins/message-actions.js +10 -0
- package/dist/channels/telegram/api.js +18 -0
- package/dist/cli/argv.js +84 -21
- package/dist/cli/banner.js +2 -1
- package/dist/cli/exec-approvals-cli.js +92 -124
- package/dist/cli/memory-cli.js +158 -61
- package/dist/cli/nodes-cli/register.push.js +63 -0
- package/dist/cli/nodes-media-utils.js +21 -0
- package/dist/cli/plugins-cli.js +245 -61
- package/dist/cli/program/build-program.js +3 -1
- package/dist/cli/program/command-registry.js +223 -136
- package/dist/cli/program/help.js +43 -12
- package/dist/cli/route.js +1 -1
- package/dist/cli/test-runtime-capture.js +24 -0
- package/dist/commands/agent.js +163 -87
- package/dist/commands/channels.mock-harness.js +23 -0
- package/dist/commands/daemon-install-runtime-warning.js +11 -0
- package/dist/commands/onboard-helpers.js +4 -4
- package/dist/commands/sessions.test-helpers.js +61 -0
- package/dist/compat/legacy-names.js +2 -2
- package/dist/config/commands.js +3 -0
- package/dist/config/config.js +1 -1
- package/dist/config/env-substitution.js +62 -34
- package/dist/config/env-vars.js +9 -0
- package/dist/config/io.js +571 -171
- package/dist/config/merge-patch.js +50 -4
- package/dist/config/redact-snapshot.js +404 -76
- package/dist/config/schema.js +58 -570
- package/dist/config/validation.js +140 -85
- package/dist/config/zod-schema.hooks.js +40 -11
- package/dist/config/zod-schema.installs.js +20 -0
- package/dist/config/zod-schema.js +8 -7
- package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
- package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
- package/dist/control-ui/index.html +1 -1
- package/dist/daemon/cmd-argv.js +21 -0
- package/dist/daemon/cmd-set.js +58 -0
- package/dist/daemon/service-types.js +1 -0
- package/dist/discord/monitor/exec-approvals.js +357 -162
- package/dist/gateway/auth.js +38 -3
- package/dist/gateway/call.js +149 -68
- package/dist/gateway/canvas-capability.js +75 -0
- package/dist/gateway/control-plane-audit.js +28 -0
- package/dist/gateway/control-plane-rate-limit.js +53 -0
- package/dist/gateway/events.js +1 -0
- package/dist/gateway/hooks.js +109 -54
- package/dist/gateway/http-common.js +22 -0
- package/dist/gateway/method-scopes.js +169 -0
- package/dist/gateway/net.js +23 -0
- package/dist/gateway/openresponses-http.js +120 -110
- package/dist/gateway/probe-auth.js +2 -0
- package/dist/gateway/protocol/index.js +3 -2
- package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
- package/dist/gateway/protocol/schema/push.js +18 -0
- package/dist/gateway/protocol/schema.js +1 -0
- package/dist/gateway/server-http.js +236 -52
- package/dist/gateway/server-methods/agent.js +162 -24
- package/dist/gateway/server-methods/chat.js +461 -130
- package/dist/gateway/server-methods/config.js +193 -150
- package/dist/gateway/server-methods/nodes.helpers.js +12 -0
- package/dist/gateway/server-methods/nodes.js +251 -69
- package/dist/gateway/server-methods/push.js +53 -0
- package/dist/gateway/server-reload-handlers.js +2 -3
- package/dist/gateway/server-runtime-config.js +5 -0
- package/dist/gateway/server-runtime-state.js +2 -0
- package/dist/gateway/server-ws-runtime.js +1 -0
- package/dist/gateway/server.impl.js +296 -139
- package/dist/gateway/session-preview.test-helpers.js +11 -0
- package/dist/gateway/startup-auth.js +126 -0
- package/dist/gateway/test-helpers.agent-results.js +15 -0
- package/dist/gateway/test-helpers.mocks.js +37 -14
- package/dist/gateway/test-helpers.server.js +161 -77
- package/dist/hooks/bundled/session-memory/handler.js +165 -34
- package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
- package/dist/infra/archive-path.js +49 -0
- package/dist/infra/device-pairing.js +148 -167
- package/dist/infra/exec-approvals-allowlist.js +19 -70
- package/dist/infra/exec-approvals-analysis.js +44 -17
- package/dist/infra/exec-safe-bin-policy.js +269 -0
- package/dist/infra/fixed-window-rate-limit.js +33 -0
- package/dist/infra/git-root.js +61 -0
- package/dist/infra/heartbeat-active-hours.js +2 -2
- package/dist/infra/heartbeat-reason.js +40 -0
- package/dist/infra/heartbeat-runner.js +72 -32
- package/dist/infra/install-source-utils.js +91 -7
- package/dist/infra/node-pairing.js +50 -105
- package/dist/infra/npm-integrity.js +45 -0
- package/dist/infra/npm-pack-install.js +40 -0
- package/dist/infra/outbound/channel-adapters.js +20 -7
- package/dist/infra/outbound/message-action-runner.js +107 -327
- package/dist/infra/outbound/message.js +59 -36
- package/dist/infra/outbound/outbound-policy.js +52 -25
- package/dist/infra/outbound/outbound-send-service.js +58 -71
- package/dist/infra/pairing-files.js +10 -0
- package/dist/infra/plain-object.js +9 -0
- package/dist/infra/push-apns.js +365 -0
- package/dist/infra/restart-sentinel.js +16 -1
- package/dist/infra/restart.js +229 -26
- package/dist/infra/scp-host.js +54 -0
- package/dist/infra/update-startup.js +86 -9
- package/dist/media/inbound-path-policy.js +114 -0
- package/dist/media/input-files.js +16 -0
- package/dist/memory/test-manager.js +8 -0
- package/dist/plugin-sdk/temp-path.js +47 -0
- package/dist/plugins/discovery.js +217 -23
- package/dist/plugins/hook-runner-global.js +16 -0
- package/dist/plugins/loader.js +192 -26
- package/dist/plugins/logger.js +8 -0
- package/dist/plugins/manifest-registry.js +3 -0
- package/dist/plugins/path-safety.js +34 -0
- package/dist/plugins/registry.js +5 -2
- package/dist/plugins/runtime/index.js +271 -206
- package/dist/providers/github-copilot-models.js +4 -1
- package/dist/security/audit-channel.js +8 -19
- package/dist/security/audit-extra.async.js +354 -182
- package/dist/security/audit-extra.js +11 -1
- package/dist/security/audit-extra.sync.js +340 -33
- package/dist/security/audit-fs.js +31 -13
- package/dist/security/audit.js +145 -371
- package/dist/security/dm-policy-shared.js +24 -0
- package/dist/security/external-content.js +20 -8
- package/dist/security/fix.js +49 -85
- package/dist/security/scan-paths.js +20 -0
- package/dist/security/secret-equal.js +3 -7
- package/dist/security/windows-acl.js +30 -15
- package/dist/shared/node-list-parse.js +13 -0
- package/dist/shared/operator-scope-compat.js +37 -0
- package/dist/shared/text-chunking.js +29 -0
- package/dist/slack/blocks.test-helpers.js +31 -0
- package/dist/slack/monitor/mrkdwn.js +8 -0
- package/dist/telegram/bot-message-dispatch.js +366 -164
- package/dist/telegram/draft-stream.js +30 -7
- package/dist/telegram/reasoning-lane-coordinator.js +128 -0
- package/dist/terminal/prompt-select-styled.js +9 -0
- package/dist/test-utils/command-runner.js +6 -0
- package/dist/test-utils/internal-hook-event-payload.js +10 -0
- package/dist/test-utils/model-auth-mock.js +12 -0
- package/dist/test-utils/provider-usage-fetch.js +14 -0
- package/dist/test-utils/temp-home.js +33 -0
- package/dist/tui/components/chat-log.js +9 -0
- package/dist/tui/tui-command-handlers.js +36 -27
- package/dist/tui/tui-event-handlers.js +122 -32
- package/dist/tui/tui.js +181 -45
- package/dist/utils/mask-api-key.js +10 -0
- package/dist/utils/run-with-concurrency.js +39 -0
- package/dist/web/media.js +4 -0
- package/docs/tools/slash-commands.md +5 -1
- package/extensions/bluebubbles/package.json +1 -1
- package/extensions/copilot-proxy/package.json +1 -1
- package/extensions/diagnostics-otel/package.json +1 -1
- package/extensions/discord/package.json +1 -1
- package/extensions/feishu/package.json +1 -1
- package/extensions/feishu/src/external-keys.ts +19 -0
- package/extensions/google-antigravity-auth/package.json +1 -1
- package/extensions/google-gemini-cli-auth/package.json +1 -1
- package/extensions/googlechat/package.json +1 -1
- package/extensions/imessage/package.json +1 -1
- package/extensions/irc/package.json +1 -1
- package/extensions/line/package.json +1 -1
- package/extensions/llm-task/package.json +1 -1
- package/extensions/lobster/package.json +1 -1
- package/extensions/lobster/src/windows-spawn.ts +193 -0
- package/extensions/matrix/CHANGELOG.md +5 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
- package/extensions/mattermost/package.json +1 -1
- package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
- package/extensions/memory-core/package.json +1 -1
- package/extensions/memory-lancedb/package.json +1 -1
- package/extensions/minimax-portal-auth/package.json +1 -1
- package/extensions/msteams/CHANGELOG.md +5 -0
- package/extensions/msteams/package.json +1 -1
- package/extensions/nextcloud-talk/package.json +1 -1
- package/extensions/nostr/CHANGELOG.md +5 -0
- package/extensions/nostr/package.json +1 -1
- package/extensions/open-prose/package.json +1 -1
- package/extensions/openai-codex-auth/package.json +1 -1
- package/extensions/signal/package.json +1 -1
- package/extensions/slack/package.json +1 -1
- package/extensions/telegram/package.json +1 -1
- package/extensions/tlon/package.json +1 -1
- package/extensions/twitch/CHANGELOG.md +5 -0
- package/extensions/twitch/package.json +1 -1
- package/extensions/voice-call/CHANGELOG.md +5 -0
- package/extensions/voice-call/package.json +1 -1
- package/extensions/whatsapp/package.json +1 -1
- package/extensions/zalo/CHANGELOG.md +5 -0
- package/extensions/zalo/package.json +1 -1
- package/extensions/zalouser/CHANGELOG.md +5 -0
- package/extensions/zalouser/package.json +1 -1
- package/package.json +1 -1
|
@@ -13,6 +13,7 @@ export function createTelegramDraftStream(params) {
|
|
|
13
13
|
: threadParams;
|
|
14
14
|
let streamMessageId;
|
|
15
15
|
let lastSentText = "";
|
|
16
|
+
let lastSentParseMode;
|
|
16
17
|
let stopped = false;
|
|
17
18
|
let isFinal = false;
|
|
18
19
|
const sendOrEditStreamMessage = async (text) => {
|
|
@@ -24,29 +25,49 @@ export function createTelegramDraftStream(params) {
|
|
|
24
25
|
if (!trimmed) {
|
|
25
26
|
return false;
|
|
26
27
|
}
|
|
27
|
-
|
|
28
|
+
const rendered = params.renderText?.(trimmed) ?? { text: trimmed };
|
|
29
|
+
const renderedText = rendered.text.trimEnd();
|
|
30
|
+
const renderedParseMode = rendered.parseMode;
|
|
31
|
+
if (!renderedText) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (renderedText.length > maxChars) {
|
|
28
35
|
// Telegram text messages/edits cap at 4096 chars.
|
|
29
36
|
// Stop streaming once we exceed the cap to avoid repeated API failures.
|
|
30
37
|
stopped = true;
|
|
31
|
-
params.warn?.(`telegram stream preview stopped (text length ${
|
|
38
|
+
params.warn?.(`telegram stream preview stopped (text length ${renderedText.length} > ${maxChars})`);
|
|
32
39
|
return false;
|
|
33
40
|
}
|
|
34
|
-
if (
|
|
41
|
+
if (renderedText === lastSentText && renderedParseMode === lastSentParseMode) {
|
|
35
42
|
return true;
|
|
36
43
|
}
|
|
37
44
|
// Debounce first preview send for better push notification quality.
|
|
38
45
|
if (typeof streamMessageId !== "number" && minInitialChars != null && !isFinal) {
|
|
39
|
-
if (
|
|
46
|
+
if (renderedText.length < minInitialChars) {
|
|
40
47
|
return false;
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
|
-
lastSentText =
|
|
50
|
+
lastSentText = renderedText;
|
|
51
|
+
lastSentParseMode = renderedParseMode;
|
|
44
52
|
try {
|
|
45
53
|
if (typeof streamMessageId === "number") {
|
|
46
|
-
|
|
54
|
+
if (renderedParseMode) {
|
|
55
|
+
await params.api.editMessageText(chatId, streamMessageId, renderedText, {
|
|
56
|
+
parse_mode: renderedParseMode,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
await params.api.editMessageText(chatId, streamMessageId, renderedText);
|
|
61
|
+
}
|
|
47
62
|
return true;
|
|
48
63
|
}
|
|
49
|
-
const
|
|
64
|
+
const sendParams = renderedParseMode
|
|
65
|
+
? {
|
|
66
|
+
...replyParams,
|
|
67
|
+
parse_mode: renderedParseMode,
|
|
68
|
+
}
|
|
69
|
+
: replyParams;
|
|
70
|
+
const sent = await params.api.sendMessage(chatId, renderedText, sendParams);
|
|
50
71
|
const sentMessageId = sent?.message_id;
|
|
51
72
|
if (typeof sentMessageId !== "number" || !Number.isFinite(sentMessageId)) {
|
|
52
73
|
stopped = true;
|
|
@@ -88,6 +109,7 @@ export function createTelegramDraftStream(params) {
|
|
|
88
109
|
}
|
|
89
110
|
try {
|
|
90
111
|
await params.api.deleteMessage(chatId, messageId);
|
|
112
|
+
params.log?.(`telegram stream preview deleted (chat=${chatId}, message=${messageId})`);
|
|
91
113
|
}
|
|
92
114
|
catch (err) {
|
|
93
115
|
params.warn?.(`telegram stream preview cleanup failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -96,6 +118,7 @@ export function createTelegramDraftStream(params) {
|
|
|
96
118
|
const forceNewMessage = () => {
|
|
97
119
|
streamMessageId = undefined;
|
|
98
120
|
lastSentText = "";
|
|
121
|
+
lastSentParseMode = undefined;
|
|
99
122
|
loop.resetPending();
|
|
100
123
|
};
|
|
101
124
|
params.log?.(`telegram stream preview ready (maxChars=${maxChars}, throttleMs=${throttleMs})`);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { formatReasoningMessage } from "../agents/pi-embedded-utils.js";
|
|
2
|
+
import { stripReasoningTagsFromText } from "../shared/text/reasoning-tags.js";
|
|
3
|
+
const REASONING_MESSAGE_PREFIX = "Reasoning:\n";
|
|
4
|
+
const REASONING_TAG_PREFIXES = [
|
|
5
|
+
"<think",
|
|
6
|
+
"<thinking",
|
|
7
|
+
"<thought",
|
|
8
|
+
"<antthinking",
|
|
9
|
+
"</think",
|
|
10
|
+
"</thinking",
|
|
11
|
+
"</thought",
|
|
12
|
+
"</antthinking",
|
|
13
|
+
];
|
|
14
|
+
const THINKING_TAG_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\b[^<>]*>/gi;
|
|
15
|
+
function findCodeRegions(text) {
|
|
16
|
+
const regions = [];
|
|
17
|
+
const fencedRe = /(^|\n)(```|~~~)[^\n]*\n[\s\S]*?(?:\n\2(?:\n|$)|$)/g;
|
|
18
|
+
for (const match of text.matchAll(fencedRe)) {
|
|
19
|
+
const start = (match.index ?? 0) + match[1].length;
|
|
20
|
+
regions.push({ start, end: start + match[0].length - match[1].length });
|
|
21
|
+
}
|
|
22
|
+
const inlineRe = /`+[^`]+`+/g;
|
|
23
|
+
for (const match of text.matchAll(inlineRe)) {
|
|
24
|
+
const start = match.index ?? 0;
|
|
25
|
+
const end = start + match[0].length;
|
|
26
|
+
const insideFenced = regions.some((r) => start >= r.start && end <= r.end);
|
|
27
|
+
if (!insideFenced) {
|
|
28
|
+
regions.push({ start, end });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
regions.sort((a, b) => a.start - b.start);
|
|
32
|
+
return regions;
|
|
33
|
+
}
|
|
34
|
+
function isInsideCode(pos, regions) {
|
|
35
|
+
return regions.some((r) => pos >= r.start && pos < r.end);
|
|
36
|
+
}
|
|
37
|
+
function extractThinkingFromTaggedStreamOutsideCode(text) {
|
|
38
|
+
if (!text) {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
const codeRegions = findCodeRegions(text);
|
|
42
|
+
let result = "";
|
|
43
|
+
let lastIndex = 0;
|
|
44
|
+
let inThinking = false;
|
|
45
|
+
THINKING_TAG_RE.lastIndex = 0;
|
|
46
|
+
for (const match of text.matchAll(THINKING_TAG_RE)) {
|
|
47
|
+
const idx = match.index ?? 0;
|
|
48
|
+
if (isInsideCode(idx, codeRegions)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (inThinking) {
|
|
52
|
+
result += text.slice(lastIndex, idx);
|
|
53
|
+
}
|
|
54
|
+
const isClose = match[1] === "/";
|
|
55
|
+
inThinking = !isClose;
|
|
56
|
+
lastIndex = idx + match[0].length;
|
|
57
|
+
}
|
|
58
|
+
if (inThinking) {
|
|
59
|
+
result += text.slice(lastIndex);
|
|
60
|
+
}
|
|
61
|
+
return result.trim();
|
|
62
|
+
}
|
|
63
|
+
function isPartialReasoningTagPrefix(text) {
|
|
64
|
+
const trimmed = text.trimStart().toLowerCase();
|
|
65
|
+
if (!trimmed.startsWith("<")) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
if (trimmed.includes(">")) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
return REASONING_TAG_PREFIXES.some((prefix) => prefix.startsWith(trimmed));
|
|
72
|
+
}
|
|
73
|
+
export function splitTelegramReasoningText(text) {
|
|
74
|
+
if (typeof text !== "string") {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
const trimmed = text.trim();
|
|
78
|
+
if (isPartialReasoningTagPrefix(trimmed)) {
|
|
79
|
+
return {};
|
|
80
|
+
}
|
|
81
|
+
if (trimmed.startsWith(REASONING_MESSAGE_PREFIX) &&
|
|
82
|
+
trimmed.length > REASONING_MESSAGE_PREFIX.length) {
|
|
83
|
+
return { reasoningText: trimmed };
|
|
84
|
+
}
|
|
85
|
+
const taggedReasoning = extractThinkingFromTaggedStreamOutsideCode(text);
|
|
86
|
+
const strippedAnswer = stripReasoningTagsFromText(text, { mode: "strict", trim: "both" });
|
|
87
|
+
if (!taggedReasoning && strippedAnswer === text) {
|
|
88
|
+
return { answerText: text };
|
|
89
|
+
}
|
|
90
|
+
const reasoningText = taggedReasoning ? formatReasoningMessage(taggedReasoning) : undefined;
|
|
91
|
+
const answerText = strippedAnswer || undefined;
|
|
92
|
+
return { reasoningText, answerText };
|
|
93
|
+
}
|
|
94
|
+
export function createTelegramReasoningStepState() {
|
|
95
|
+
let reasoningStatus = "none";
|
|
96
|
+
let bufferedFinalAnswer;
|
|
97
|
+
const noteReasoningHint = () => {
|
|
98
|
+
if (reasoningStatus === "none") {
|
|
99
|
+
reasoningStatus = "hinted";
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const noteReasoningDelivered = () => {
|
|
103
|
+
reasoningStatus = "delivered";
|
|
104
|
+
};
|
|
105
|
+
const shouldBufferFinalAnswer = () => {
|
|
106
|
+
return reasoningStatus === "hinted" && !bufferedFinalAnswer;
|
|
107
|
+
};
|
|
108
|
+
const bufferFinalAnswer = (value) => {
|
|
109
|
+
bufferedFinalAnswer = value;
|
|
110
|
+
};
|
|
111
|
+
const takeBufferedFinalAnswer = () => {
|
|
112
|
+
const value = bufferedFinalAnswer;
|
|
113
|
+
bufferedFinalAnswer = undefined;
|
|
114
|
+
return value;
|
|
115
|
+
};
|
|
116
|
+
const resetForNextStep = () => {
|
|
117
|
+
reasoningStatus = "none";
|
|
118
|
+
bufferedFinalAnswer = undefined;
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
noteReasoningHint,
|
|
122
|
+
noteReasoningDelivered,
|
|
123
|
+
shouldBufferFinalAnswer,
|
|
124
|
+
bufferFinalAnswer,
|
|
125
|
+
takeBufferedFinalAnswer,
|
|
126
|
+
resetForNextStep,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { select } from "@clack/prompts";
|
|
2
|
+
import { stylePromptHint, stylePromptMessage } from "./prompt-style.js";
|
|
3
|
+
export function selectStyled(params) {
|
|
4
|
+
return select({
|
|
5
|
+
...params,
|
|
6
|
+
message: stylePromptMessage(params.message),
|
|
7
|
+
options: params.options.map((opt) => opt.hint === undefined ? opt : { ...opt, hint: stylePromptHint(opt.hint) }),
|
|
8
|
+
});
|
|
9
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { vi } from "vitest";
|
|
2
|
+
export function createModelAuthMockModule() {
|
|
3
|
+
return {
|
|
4
|
+
resolveApiKeyForProvider: vi.fn(),
|
|
5
|
+
requireApiKey: (auth, provider) => {
|
|
6
|
+
if (auth?.apiKey) {
|
|
7
|
+
return auth.apiKey;
|
|
8
|
+
}
|
|
9
|
+
throw new Error(`No API key resolved for provider "${provider}" (auth mode: ${auth?.mode}).`);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { vi } from "vitest";
|
|
2
|
+
import { withFetchPreconnect } from "./fetch-mock.js";
|
|
3
|
+
export function makeResponse(status, body) {
|
|
4
|
+
const payload = typeof body === "string" ? body : JSON.stringify(body);
|
|
5
|
+
const headers = typeof body === "string" ? undefined : { "Content-Type": "application/json" };
|
|
6
|
+
return new Response(payload, { status, headers });
|
|
7
|
+
}
|
|
8
|
+
export function toRequestUrl(input) {
|
|
9
|
+
return typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
10
|
+
}
|
|
11
|
+
export function createProviderUsageFetch(handler) {
|
|
12
|
+
const mockFetch = vi.fn(async (input, init) => handler(toRequestUrl(input), init));
|
|
13
|
+
return withFetchPreconnect(mockFetch);
|
|
14
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { captureEnv } from "./env.js";
|
|
5
|
+
const HOME_ENV_KEYS = [
|
|
6
|
+
"HOME",
|
|
7
|
+
"USERPROFILE",
|
|
8
|
+
"HOMEDRIVE",
|
|
9
|
+
"HOMEPATH",
|
|
10
|
+
"CLAWDBOT_STATE_DIR",
|
|
11
|
+
];
|
|
12
|
+
export async function createTempHomeEnv(prefix) {
|
|
13
|
+
const home = await fs.mkdtemp(path.join(os.tmpdir(), prefix));
|
|
14
|
+
await fs.mkdir(path.join(home, ".poolbot"), { recursive: true });
|
|
15
|
+
const snapshot = captureEnv([...HOME_ENV_KEYS]);
|
|
16
|
+
process.env.HOME = home;
|
|
17
|
+
process.env.USERPROFILE = home;
|
|
18
|
+
process.env.CLAWDBOT_STATE_DIR = path.join(home, ".poolbot");
|
|
19
|
+
if (process.platform === "win32") {
|
|
20
|
+
const match = home.match(/^([A-Za-z]:)(.*)$/);
|
|
21
|
+
if (match) {
|
|
22
|
+
process.env.HOMEDRIVE = match[1];
|
|
23
|
+
process.env.HOMEPATH = match[2] || "\\";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
home,
|
|
28
|
+
restore: async () => {
|
|
29
|
+
snapshot.restore();
|
|
30
|
+
await fs.rm(home, { recursive: true, force: true });
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
@@ -47,6 +47,15 @@ export class ChatLog extends Container {
|
|
|
47
47
|
}
|
|
48
48
|
this.addChild(new AssistantMessageComponent(text));
|
|
49
49
|
}
|
|
50
|
+
dropAssistant(runId) {
|
|
51
|
+
const effectiveRunId = this.resolveRunId(runId);
|
|
52
|
+
const existing = this.streamingRuns.get(effectiveRunId);
|
|
53
|
+
if (!existing) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
this.removeChild(existing);
|
|
57
|
+
this.streamingRuns.delete(effectiveRunId);
|
|
58
|
+
}
|
|
50
59
|
startTool(toolCallId, toolName, args) {
|
|
51
60
|
const existing = this.toolById.get(toolCallId);
|
|
52
61
|
if (existing) {
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
import { truncateToWidth } from "@mariozechner/pi-tui";
|
|
2
1
|
import { formatThinkingLevels, normalizeUsageDisplay, resolveResponseUsageMode, } from "../auto-reply/thinking.js";
|
|
2
|
+
import { formatRelativeTimestamp } from "../infra/format-time/format-relative.js";
|
|
3
3
|
import { normalizeAgentId } from "../routing/session-key.js";
|
|
4
|
-
import { formatRelativeTime } from "../utils/time-format.js";
|
|
5
4
|
import { helpText, parseCommand } from "./commands.js";
|
|
6
5
|
import { createFilterableSelectList, createSearchableSelectList, createSettingsList, } from "./components/selectors.js";
|
|
7
6
|
import { formatStatusSummary } from "./tui-status-summary.js";
|
|
8
7
|
export function createCommandHandlers(context) {
|
|
9
|
-
const { client, chatLog, tui, opts, state, deliverDefault, openOverlay, closeOverlay, refreshSessionInfo, loadHistory, setSession, refreshAgents, abortActive, setActivityStatus, formatSessionKey, } = context;
|
|
8
|
+
const { client, chatLog, tui, opts, state, deliverDefault, openOverlay, closeOverlay, refreshSessionInfo, loadHistory, setSession, refreshAgents, abortActive, setActivityStatus, formatSessionKey, applySessionInfoFromPatch, noteLocalRunId, forgetLocalRunId, } = context;
|
|
10
9
|
const setAgent = async (id) => {
|
|
11
10
|
state.currentAgentId = normalizeAgentId(id);
|
|
12
11
|
await setSession("");
|
|
13
12
|
};
|
|
14
|
-
// Maximum visible width for overlay item strings. The overlay has its own
|
|
15
|
-
// padding/chrome (~4 chars). We subtract a generous margin so that even
|
|
16
|
-
// after ANSI formatting, lines stay within terminal bounds.
|
|
17
|
-
const overlayItemWidth = () => Math.max(30, (tui.terminal.columns ?? 80) - 6);
|
|
18
13
|
const openModelSelector = async () => {
|
|
19
14
|
try {
|
|
20
15
|
const models = await client.listModels();
|
|
@@ -23,21 +18,21 @@ export function createCommandHandlers(context) {
|
|
|
23
18
|
tui.requestRender();
|
|
24
19
|
return;
|
|
25
20
|
}
|
|
26
|
-
const maxW = overlayItemWidth();
|
|
27
21
|
const items = models.map((model) => ({
|
|
28
22
|
value: `${model.provider}/${model.id}`,
|
|
29
|
-
label:
|
|
30
|
-
description: model.name && model.name !== model.id ?
|
|
23
|
+
label: `${model.provider}/${model.id}`,
|
|
24
|
+
description: model.name && model.name !== model.id ? model.name : "",
|
|
31
25
|
}));
|
|
32
26
|
const selector = createSearchableSelectList(items, 9);
|
|
33
27
|
selector.onSelect = (item) => {
|
|
34
28
|
void (async () => {
|
|
35
29
|
try {
|
|
36
|
-
await client.patchSession({
|
|
30
|
+
const result = await client.patchSession({
|
|
37
31
|
key: state.currentSessionKey,
|
|
38
32
|
model: item.value,
|
|
39
33
|
});
|
|
40
34
|
chatLog.addSystem(`model set to ${item.value}`);
|
|
35
|
+
applySessionInfoFromPatch(result);
|
|
41
36
|
await refreshSessionInfo();
|
|
42
37
|
}
|
|
43
38
|
catch (err) {
|
|
@@ -66,10 +61,9 @@ export function createCommandHandlers(context) {
|
|
|
66
61
|
tui.requestRender();
|
|
67
62
|
return;
|
|
68
63
|
}
|
|
69
|
-
const maxW = overlayItemWidth();
|
|
70
64
|
const items = state.agents.map((agent) => ({
|
|
71
65
|
value: agent.id,
|
|
72
|
-
label:
|
|
66
|
+
label: agent.name ? `${agent.id} (${agent.name})` : agent.id,
|
|
73
67
|
description: agent.id === state.agentDefaultId ? "default" : "",
|
|
74
68
|
}));
|
|
75
69
|
const selector = createSearchableSelectList(items, 9);
|
|
@@ -96,20 +90,21 @@ export function createCommandHandlers(context) {
|
|
|
96
90
|
includeLastMessage: true,
|
|
97
91
|
agentId: state.currentAgentId,
|
|
98
92
|
});
|
|
99
|
-
const maxW = overlayItemWidth();
|
|
100
93
|
const items = result.sessions.map((session) => {
|
|
101
94
|
const title = session.derivedTitle ?? session.displayName;
|
|
102
95
|
const formattedKey = formatSessionKey(session.key);
|
|
103
96
|
// Avoid redundant "title (key)" when title matches key
|
|
104
97
|
const label = title && title !== formattedKey ? `${title} (${formattedKey})` : formattedKey;
|
|
105
98
|
// Build description: time + message preview
|
|
106
|
-
const timePart = session.updatedAt
|
|
99
|
+
const timePart = session.updatedAt
|
|
100
|
+
? formatRelativeTimestamp(session.updatedAt, { dateFallback: true, fallback: "" })
|
|
101
|
+
: "";
|
|
107
102
|
const preview = session.lastMessagePreview?.replace(/\s+/g, " ").trim();
|
|
108
103
|
const description = timePart && preview ? `${timePart} · ${preview}` : (preview ?? timePart);
|
|
109
104
|
return {
|
|
110
105
|
value: session.key,
|
|
111
|
-
label
|
|
112
|
-
description
|
|
106
|
+
label,
|
|
107
|
+
description,
|
|
113
108
|
searchText: [
|
|
114
109
|
session.displayName,
|
|
115
110
|
session.label,
|
|
@@ -176,8 +171,9 @@ export function createCommandHandlers(context) {
|
|
|
176
171
|
};
|
|
177
172
|
const handleCommand = async (raw) => {
|
|
178
173
|
const { name, args } = parseCommand(raw);
|
|
179
|
-
if (!name)
|
|
174
|
+
if (!name) {
|
|
180
175
|
return;
|
|
176
|
+
}
|
|
181
177
|
switch (name) {
|
|
182
178
|
case "help":
|
|
183
179
|
chatLog.addSystem(helpText({
|
|
@@ -194,8 +190,9 @@ export function createCommandHandlers(context) {
|
|
|
194
190
|
}
|
|
195
191
|
if (status && typeof status === "object") {
|
|
196
192
|
const lines = formatStatusSummary(status);
|
|
197
|
-
for (const line of lines)
|
|
193
|
+
for (const line of lines) {
|
|
198
194
|
chatLog.addSystem(line);
|
|
195
|
+
}
|
|
199
196
|
break;
|
|
200
197
|
}
|
|
201
198
|
chatLog.addSystem("status: unknown response");
|
|
@@ -232,11 +229,12 @@ export function createCommandHandlers(context) {
|
|
|
232
229
|
}
|
|
233
230
|
else {
|
|
234
231
|
try {
|
|
235
|
-
await client.patchSession({
|
|
232
|
+
const result = await client.patchSession({
|
|
236
233
|
key: state.currentSessionKey,
|
|
237
234
|
model: args,
|
|
238
235
|
});
|
|
239
236
|
chatLog.addSystem(`model set to ${args}`);
|
|
237
|
+
applySessionInfoFromPatch(result);
|
|
240
238
|
await refreshSessionInfo();
|
|
241
239
|
}
|
|
242
240
|
catch (err) {
|
|
@@ -254,11 +252,12 @@ export function createCommandHandlers(context) {
|
|
|
254
252
|
break;
|
|
255
253
|
}
|
|
256
254
|
try {
|
|
257
|
-
await client.patchSession({
|
|
255
|
+
const result = await client.patchSession({
|
|
258
256
|
key: state.currentSessionKey,
|
|
259
257
|
thinkingLevel: args,
|
|
260
258
|
});
|
|
261
259
|
chatLog.addSystem(`thinking set to ${args}`);
|
|
260
|
+
applySessionInfoFromPatch(result);
|
|
262
261
|
await refreshSessionInfo();
|
|
263
262
|
}
|
|
264
263
|
catch (err) {
|
|
@@ -271,12 +270,13 @@ export function createCommandHandlers(context) {
|
|
|
271
270
|
break;
|
|
272
271
|
}
|
|
273
272
|
try {
|
|
274
|
-
await client.patchSession({
|
|
273
|
+
const result = await client.patchSession({
|
|
275
274
|
key: state.currentSessionKey,
|
|
276
275
|
verboseLevel: args,
|
|
277
276
|
});
|
|
278
277
|
chatLog.addSystem(`verbose set to ${args}`);
|
|
279
|
-
|
|
278
|
+
applySessionInfoFromPatch(result);
|
|
279
|
+
await loadHistory();
|
|
280
280
|
}
|
|
281
281
|
catch (err) {
|
|
282
282
|
chatLog.addSystem(`verbose failed: ${String(err)}`);
|
|
@@ -288,11 +288,12 @@ export function createCommandHandlers(context) {
|
|
|
288
288
|
break;
|
|
289
289
|
}
|
|
290
290
|
try {
|
|
291
|
-
await client.patchSession({
|
|
291
|
+
const result = await client.patchSession({
|
|
292
292
|
key: state.currentSessionKey,
|
|
293
293
|
reasoningLevel: args,
|
|
294
294
|
});
|
|
295
295
|
chatLog.addSystem(`reasoning set to ${args}`);
|
|
296
|
+
applySessionInfoFromPatch(result);
|
|
296
297
|
await refreshSessionInfo();
|
|
297
298
|
}
|
|
298
299
|
catch (err) {
|
|
@@ -309,11 +310,12 @@ export function createCommandHandlers(context) {
|
|
|
309
310
|
const current = resolveResponseUsageMode(currentRaw);
|
|
310
311
|
const next = normalized ?? (current === "off" ? "tokens" : current === "tokens" ? "full" : "off");
|
|
311
312
|
try {
|
|
312
|
-
await client.patchSession({
|
|
313
|
+
const result = await client.patchSession({
|
|
313
314
|
key: state.currentSessionKey,
|
|
314
315
|
responseUsage: next === "off" ? null : next,
|
|
315
316
|
});
|
|
316
317
|
chatLog.addSystem(`usage footer: ${next}`);
|
|
318
|
+
applySessionInfoFromPatch(result);
|
|
317
319
|
await refreshSessionInfo();
|
|
318
320
|
}
|
|
319
321
|
catch (err) {
|
|
@@ -331,11 +333,12 @@ export function createCommandHandlers(context) {
|
|
|
331
333
|
break;
|
|
332
334
|
}
|
|
333
335
|
try {
|
|
334
|
-
await client.patchSession({
|
|
336
|
+
const result = await client.patchSession({
|
|
335
337
|
key: state.currentSessionKey,
|
|
336
338
|
elevatedLevel: args,
|
|
337
339
|
});
|
|
338
340
|
chatLog.addSystem(`elevated set to ${args}`);
|
|
341
|
+
applySessionInfoFromPatch(result);
|
|
339
342
|
await refreshSessionInfo();
|
|
340
343
|
}
|
|
341
344
|
catch (err) {
|
|
@@ -348,11 +351,12 @@ export function createCommandHandlers(context) {
|
|
|
348
351
|
break;
|
|
349
352
|
}
|
|
350
353
|
try {
|
|
351
|
-
await client.patchSession({
|
|
354
|
+
const result = await client.patchSession({
|
|
352
355
|
key: state.currentSessionKey,
|
|
353
356
|
groupActivation: args === "always" ? "always" : "mention",
|
|
354
357
|
});
|
|
355
358
|
chatLog.addSystem(`activation set to ${args}`);
|
|
359
|
+
applySessionInfoFromPatch(result);
|
|
356
360
|
await refreshSessionInfo();
|
|
357
361
|
}
|
|
358
362
|
catch (err) {
|
|
@@ -405,10 +409,15 @@ export function createCommandHandlers(context) {
|
|
|
405
409
|
deliver: deliverDefault,
|
|
406
410
|
timeoutMs: opts.timeoutMs,
|
|
407
411
|
});
|
|
412
|
+
noteLocalRunId(runId);
|
|
408
413
|
state.activeChatRunId = runId;
|
|
409
414
|
setActivityStatus("waiting");
|
|
410
415
|
}
|
|
411
416
|
catch (err) {
|
|
417
|
+
if (state.activeChatRunId) {
|
|
418
|
+
forgetLocalRunId?.(state.activeChatRunId);
|
|
419
|
+
}
|
|
420
|
+
state.activeChatRunId = null;
|
|
412
421
|
chatLog.addSystem(`send failed: ${String(err)}`);
|
|
413
422
|
setActivityStatus("error");
|
|
414
423
|
}
|