@poolzin/pool-bot 2026.2.0 → 2026.2.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/CHANGELOG.md +118 -0
- package/README-header.png +0 -0
- package/dist/agents/bash-tools.exec.js +76 -25
- package/dist/agents/cli-runner/helpers.js +9 -11
- package/dist/agents/context.js +1 -1
- package/dist/agents/identity.js +47 -7
- package/dist/agents/memory-search.js +25 -8
- package/dist/agents/model-catalog.js +1 -1
- package/dist/agents/model-selection.js +21 -0
- package/dist/agents/pi-embedded-block-chunker.js +117 -42
- package/dist/agents/pi-embedded-helpers/errors.js +183 -78
- package/dist/agents/pi-embedded-helpers.js +1 -1
- package/dist/agents/pi-embedded-runner/compact.js +8 -10
- package/dist/agents/pi-embedded-runner/model.js +62 -3
- package/dist/agents/pi-embedded-runner/run/attempt.js +21 -11
- package/dist/agents/pi-embedded-runner/run.js +199 -46
- package/dist/agents/pi-embedded-runner/system-prompt.js +10 -2
- package/dist/agents/pi-embedded-subscribe.js +118 -29
- package/dist/agents/pi-tools.js +10 -5
- package/dist/agents/poolbot-tools.js +15 -10
- package/dist/agents/sandbox-paths.js +31 -0
- package/dist/agents/session-tool-result-guard.js +94 -15
- package/dist/agents/shell-utils.js +51 -0
- package/dist/agents/skills/bundled-context.js +23 -0
- package/dist/agents/skills/bundled-dir.js +41 -7
- package/dist/agents/skills-install.js +60 -23
- package/dist/agents/subagent-announce.js +79 -34
- package/dist/agents/tool-policy.conformance.js +14 -0
- package/dist/agents/tool-policy.js +24 -0
- package/dist/agents/tools/cron-tool.js +166 -19
- package/dist/agents/tools/discord-actions-presence.js +78 -0
- package/dist/agents/tools/image-tool.js +1 -1
- package/dist/agents/tools/message-tool.js +56 -2
- package/dist/agents/tools/sessions-history-tool.js +69 -1
- package/dist/agents/tools/web-search.js +211 -42
- package/dist/agents/usage.js +23 -1
- package/dist/agents/workspace-run.js +67 -0
- package/dist/agents/workspace-templates.js +44 -0
- package/dist/auto-reply/command-auth.js +121 -6
- package/dist/auto-reply/envelope.js +74 -82
- package/dist/auto-reply/reply/commands-compact.js +1 -0
- package/dist/auto-reply/reply/commands-context-report.js +1 -0
- package/dist/auto-reply/reply/commands-context.js +1 -0
- package/dist/auto-reply/reply/commands-models.js +107 -60
- package/dist/auto-reply/reply/commands-ptt.js +171 -0
- package/dist/auto-reply/reply/get-reply-run.js +2 -1
- package/dist/auto-reply/reply/inbound-context.js +5 -1
- package/dist/auto-reply/reply/mentions.js +1 -1
- package/dist/auto-reply/reply/model-selection.js +3 -3
- package/dist/auto-reply/thinking.js +88 -43
- package/dist/browser/bridge-server.js +13 -0
- package/dist/browser/cdp.helpers.js +38 -24
- package/dist/browser/client-fetch.js +50 -7
- package/dist/browser/config.js +1 -10
- package/dist/browser/extension-relay.js +101 -40
- package/dist/browser/pw-ai.js +1 -1
- package/dist/browser/pw-session.js +143 -8
- package/dist/browser/pw-tools-core.interactions.js +125 -27
- package/dist/browser/pw-tools-core.responses.js +1 -1
- package/dist/browser/pw-tools-core.state.js +1 -1
- package/dist/browser/routes/agent.act.js +86 -41
- package/dist/browser/routes/dispatcher.js +4 -4
- package/dist/browser/screenshot.js +1 -1
- package/dist/browser/server.js +13 -0
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/index.html +28 -28
- package/dist/channels/reply-prefix.js +8 -1
- package/dist/cli/cron-cli/register.cron-add.js +61 -40
- package/dist/cli/cron-cli/register.cron-edit.js +60 -34
- package/dist/cli/cron-cli/shared.js +56 -41
- package/dist/cli/dns-cli.js +26 -14
- package/dist/cli/gateway-cli/register.js +37 -19
- package/dist/cli/memory-cli.js +5 -5
- package/dist/cli/parse-bytes.js +37 -0
- package/dist/cli/update-cli.js +173 -52
- package/dist/commands/agent.js +1 -0
- package/dist/commands/auth-choice.apply.oauth.js +1 -1
- package/dist/commands/doctor-config-flow.js +61 -5
- package/dist/commands/doctor-state-migrations.js +1 -1
- package/dist/commands/health.js +1 -1
- package/dist/commands/model-allowlist.js +29 -0
- package/dist/commands/model-picker.js +2 -1
- package/dist/commands/models/list.registry.js +1 -1
- package/dist/commands/models/list.status-command.js +43 -23
- package/dist/commands/models/shared.js +15 -0
- package/dist/commands/onboard-custom.js +384 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice-inference.js +35 -0
- package/dist/commands/onboard-non-interactive/local/auth-choice.js +6 -3
- package/dist/commands/onboard-skills.js +63 -38
- package/dist/commands/openai-model-default.js +41 -0
- package/dist/compat/legacy-names.js +2 -0
- package/dist/config/defaults.js +3 -2
- package/dist/config/paths.js +136 -35
- package/dist/config/plugin-auto-enable.js +21 -5
- package/dist/config/redact-snapshot.js +153 -0
- package/dist/config/schema.field-metadata.js +590 -0
- package/dist/config/schema.js +2 -2
- package/dist/config/sessions/store.js +291 -23
- package/dist/config/zod-schema.agent-defaults.js +3 -0
- package/dist/config/zod-schema.agent-runtime.js +13 -2
- package/dist/config/zod-schema.providers-core.js +142 -0
- package/dist/config/zod-schema.session.js +3 -0
- package/dist/control-ui/assets/{index-CIRDm-Lu.css → index-CSfXd2LO.css} +1 -1
- package/dist/control-ui/assets/{index-CmNMuoem.js → index-HRr1grwl.js} +446 -413
- package/dist/control-ui/assets/index-HRr1grwl.js.map +1 -0
- package/dist/control-ui/index.html +4 -4
- package/dist/cron/delivery.js +57 -0
- package/dist/cron/isolated-agent/delivery-target.js +18 -3
- package/dist/cron/isolated-agent/helpers.js +22 -5
- package/dist/cron/isolated-agent/run.js +172 -63
- package/dist/cron/isolated-agent/session.js +2 -0
- package/dist/cron/normalize.js +356 -28
- package/dist/cron/parse.js +10 -5
- package/dist/cron/run-log.js +35 -10
- package/dist/cron/schedule.js +41 -6
- package/dist/cron/service/jobs.js +208 -35
- package/dist/cron/service/ops.js +72 -16
- package/dist/cron/service/state.js +2 -0
- package/dist/cron/service/store.js +386 -14
- package/dist/cron/service/timer.js +390 -147
- package/dist/cron/session-reaper.js +86 -0
- package/dist/cron/store.js +23 -8
- package/dist/cron/validate-timestamp.js +43 -0
- package/dist/discord/monitor/agent-components.js +438 -0
- package/dist/discord/monitor/allow-list.js +28 -5
- package/dist/discord/monitor/gateway-registry.js +29 -0
- package/dist/discord/monitor/native-command.js +44 -23
- package/dist/discord/monitor/sender-identity.js +45 -0
- package/dist/discord/pluralkit.js +27 -0
- package/dist/discord/send.outbound.js +92 -5
- package/dist/discord/send.shared.js +60 -23
- package/dist/discord/targets.js +84 -1
- package/dist/entry.js +15 -9
- package/dist/extensionAPI.js +8 -0
- package/dist/gateway/control-ui.js +8 -1
- package/dist/gateway/hooks-mapping.js +3 -0
- package/dist/gateway/hooks.js +65 -0
- package/dist/gateway/net.js +96 -31
- package/dist/gateway/node-command-policy.js +50 -15
- package/dist/gateway/origin-check.js +56 -0
- package/dist/gateway/protocol/client-info.js +9 -0
- package/dist/gateway/protocol/index.js +9 -2
- package/dist/gateway/protocol/schema/agents-models-skills.js +71 -1
- package/dist/gateway/protocol/schema/cron.js +22 -10
- package/dist/gateway/protocol/schema/protocol-schemas.js +16 -2
- package/dist/gateway/protocol/schema/sessions.js +12 -0
- package/dist/gateway/server/hooks.js +1 -1
- package/dist/gateway/server-broadcast.js +26 -9
- package/dist/gateway/server-chat.js +112 -23
- package/dist/gateway/server-discovery-runtime.js +10 -2
- package/dist/gateway/server-http.js +109 -11
- package/dist/gateway/server-methods/agent-timestamp.js +60 -0
- package/dist/gateway/server-methods/agents.js +321 -2
- package/dist/gateway/server-methods/usage.js +559 -16
- package/dist/gateway/server-runtime-state.js +22 -8
- package/dist/gateway/server-startup-memory.js +16 -0
- package/dist/gateway/server.impl.js +5 -1
- package/dist/gateway/session-utils.fs.js +23 -25
- package/dist/gateway/session-utils.js +20 -10
- package/dist/gateway/sessions-patch.js +7 -22
- package/dist/gateway/test-helpers.mocks.js +11 -7
- package/dist/gateway/test-helpers.server.js +35 -2
- package/dist/imessage/constants.js +2 -0
- package/dist/imessage/monitor/deliver.js +4 -1
- package/dist/imessage/monitor/monitor-provider.js +51 -1
- package/dist/infra/bonjour-discovery.js +131 -70
- package/dist/infra/control-ui-assets.js +134 -12
- package/dist/infra/errors.js +12 -0
- package/dist/infra/exec-approvals.js +266 -57
- package/dist/infra/format-time/format-datetime.js +79 -0
- package/dist/infra/format-time/format-duration.js +81 -0
- package/dist/infra/format-time/format-relative.js +80 -0
- package/dist/infra/heartbeat-runner.js +140 -49
- package/dist/infra/home-dir.js +54 -0
- package/dist/infra/net/fetch-guard.js +122 -0
- package/dist/infra/net/ssrf.js +65 -29
- package/dist/infra/outbound/abort.js +14 -0
- package/dist/infra/outbound/message-action-runner.js +77 -13
- package/dist/infra/outbound/outbound-session.js +143 -37
- package/dist/infra/poolbot-root.js +43 -1
- package/dist/infra/session-cost-usage.js +631 -41
- package/dist/infra/state-migrations.js +317 -47
- package/dist/infra/update-global.js +35 -0
- package/dist/infra/update-runner.js +149 -43
- package/dist/infra/warning-filter.js +65 -0
- package/dist/infra/widearea-dns.js +30 -9
- package/dist/logging/redact-identifier.js +12 -0
- package/dist/media/fetch.js +81 -58
- package/dist/media/store.js +2 -0
- package/dist/media-understanding/apply.js +403 -3
- package/dist/media-understanding/attachments.js +38 -27
- package/dist/media-understanding/defaults.js +16 -0
- package/dist/media-understanding/providers/deepgram/audio.js +22 -14
- package/dist/media-understanding/providers/google/audio.js +24 -17
- package/dist/media-understanding/providers/google/video.js +24 -17
- package/dist/media-understanding/providers/image.js +3 -3
- package/dist/media-understanding/providers/index.js +4 -1
- package/dist/media-understanding/providers/openai/audio.js +22 -14
- package/dist/media-understanding/providers/shared.js +16 -11
- package/dist/media-understanding/providers/zai/index.js +6 -0
- package/dist/media-understanding/runner.js +158 -90
- package/dist/memory/batch-voyage.js +277 -0
- package/dist/memory/embeddings-voyage.js +75 -0
- package/dist/memory/embeddings.js +28 -16
- package/dist/memory/internal.js +101 -18
- package/dist/memory/manager.js +154 -48
- package/dist/memory/search-manager.js +173 -0
- package/dist/memory/session-files.js +9 -3
- package/dist/node-host/runner.js +34 -24
- package/dist/node-host/with-timeout.js +27 -0
- package/dist/plugins/commands.js +5 -1
- package/dist/plugins/config-state.js +86 -7
- package/dist/plugins/source-display.js +51 -0
- package/dist/process/exec.js +20 -2
- package/dist/routing/resolve-route.js +12 -0
- package/dist/routing/session-key.js +15 -0
- package/dist/runtime.js +2 -0
- package/dist/security/audit-extra.async.js +601 -0
- package/dist/security/audit-extra.js +2 -830
- package/dist/security/audit-extra.sync.js +505 -0
- package/dist/security/channel-metadata.js +34 -0
- package/dist/security/external-content.js +88 -6
- package/dist/security/skill-scanner.js +330 -0
- package/dist/sessions/session-key-utils.js +7 -0
- package/dist/signal/monitor/event-handler.js +80 -1
- package/dist/slack/monitor/media.js +85 -15
- package/dist/tailscale/detect.js +1 -2
- package/dist/telegram/bot/helpers.js +109 -28
- package/dist/telegram/bot-handlers.js +144 -3
- package/dist/telegram/bot-message-context.js +37 -10
- package/dist/telegram/bot-message-dispatch.js +54 -17
- package/dist/telegram/bot-native-commands.js +86 -29
- package/dist/telegram/bot.js +30 -29
- package/dist/telegram/model-buttons.js +163 -0
- package/dist/telegram/monitor.js +110 -85
- package/dist/telegram/send.js +129 -47
- package/dist/terminal/restore.js +45 -0
- package/dist/test-helpers/state-dir-env.js +16 -0
- package/dist/tts/tts.js +12 -6
- package/dist/tui/tui-session-actions.js +166 -54
- package/dist/utils/fetch-timeout.js +20 -0
- package/dist/utils/normalize-secret-input.js +19 -0
- package/dist/utils/transcript-tools.js +58 -0
- package/dist/utils.js +45 -14
- package/dist/version.js +42 -5
- package/dist/wizard/clack-prompter.js +9 -6
- package/extensions/googlechat/node_modules/.bin/poolbot +21 -0
- package/extensions/googlechat/package.json +2 -2
- package/extensions/line/node_modules/.bin/poolbot +21 -0
- package/extensions/line/package.json +1 -1
- package/extensions/matrix/node_modules/.bin/poolbot +21 -0
- package/extensions/matrix/package.json +1 -1
- package/extensions/memory-core/node_modules/.bin/poolbot +21 -0
- package/extensions/memory-core/package.json +4 -1
- package/extensions/twitch/node_modules/.bin/poolbot +21 -0
- package/extensions/twitch/package.json +1 -1
- package/package.json +183 -24
- package/dist/control-ui/assets/index-CmNMuoem.js.map +0 -1
package/dist/memory/manager.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
|
+
import fsSync from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import chokidar from "chokidar";
|
|
5
6
|
import { resolveAgentDir, resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
|
|
@@ -11,9 +12,11 @@ import { resolveUserPath } from "../utils.js";
|
|
|
11
12
|
import { createEmbeddingProvider, } from "./embeddings.js";
|
|
12
13
|
import { DEFAULT_GEMINI_EMBEDDING_MODEL } from "./embeddings-gemini.js";
|
|
13
14
|
import { DEFAULT_OPENAI_EMBEDDING_MODEL } from "./embeddings-openai.js";
|
|
15
|
+
import { DEFAULT_VOYAGE_EMBEDDING_MODEL } from "./embeddings-voyage.js";
|
|
14
16
|
import { OPENAI_BATCH_ENDPOINT, runOpenAiEmbeddingBatches, } from "./batch-openai.js";
|
|
15
17
|
import { runGeminiEmbeddingBatches } from "./batch-gemini.js";
|
|
16
|
-
import {
|
|
18
|
+
import { runVoyageEmbeddingBatches } from "./batch-voyage.js";
|
|
19
|
+
import { buildFileEntry, chunkMarkdown, ensureDir, hashText, isMemoryPath, listMemoryFiles, normalizeExtraMemoryPaths, parseEmbedding, remapChunkLines, runWithConcurrency, } from "./internal.js";
|
|
17
20
|
import { bm25RankToScore, buildFtsQuery, mergeHybridResults } from "./hybrid.js";
|
|
18
21
|
import { searchKeyword, searchVector } from "./manager-search.js";
|
|
19
22
|
import { ensureMemoryIndexSchema } from "./memory-schema.js";
|
|
@@ -53,6 +56,7 @@ export class MemoryIndexManager {
|
|
|
53
56
|
fallbackReason;
|
|
54
57
|
openAi;
|
|
55
58
|
gemini;
|
|
59
|
+
voyage;
|
|
56
60
|
batch;
|
|
57
61
|
batchFailureCount = 0;
|
|
58
62
|
batchFailureLastError;
|
|
@@ -120,6 +124,7 @@ export class MemoryIndexManager {
|
|
|
120
124
|
this.fallbackReason = params.providerResult.fallbackReason;
|
|
121
125
|
this.openAi = params.providerResult.openAi;
|
|
122
126
|
this.gemini = params.providerResult.gemini;
|
|
127
|
+
this.voyage = params.providerResult.voyage;
|
|
123
128
|
this.sources = new Set(params.settings.sources);
|
|
124
129
|
this.db = this.openDatabase();
|
|
125
130
|
this.providerKey = this.computeProviderKey();
|
|
@@ -257,13 +262,51 @@ export class MemoryIndexManager {
|
|
|
257
262
|
return this.syncing;
|
|
258
263
|
}
|
|
259
264
|
async readFile(params) {
|
|
260
|
-
const
|
|
261
|
-
if (!
|
|
265
|
+
const rawPath = params.relPath.trim();
|
|
266
|
+
if (!rawPath) {
|
|
262
267
|
throw new Error("path required");
|
|
263
268
|
}
|
|
264
|
-
const absPath = path.
|
|
265
|
-
|
|
266
|
-
|
|
269
|
+
const absPath = path.isAbsolute(rawPath)
|
|
270
|
+
? path.resolve(rawPath)
|
|
271
|
+
: path.resolve(this.workspaceDir, rawPath);
|
|
272
|
+
const relPath = path.relative(this.workspaceDir, absPath).replace(/\\/g, "/");
|
|
273
|
+
const inWorkspace = relPath.length > 0 && !relPath.startsWith("..") && !path.isAbsolute(relPath);
|
|
274
|
+
const allowedWorkspace = inWorkspace && isMemoryPath(relPath);
|
|
275
|
+
let allowedAdditional = false;
|
|
276
|
+
if (!allowedWorkspace && this.settings.extraPaths.length > 0) {
|
|
277
|
+
const additionalPaths = normalizeExtraMemoryPaths(this.workspaceDir, this.settings.extraPaths);
|
|
278
|
+
for (const additionalPath of additionalPaths) {
|
|
279
|
+
try {
|
|
280
|
+
const stat = await fs.lstat(additionalPath);
|
|
281
|
+
if (stat.isSymbolicLink()) {
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
if (stat.isDirectory()) {
|
|
285
|
+
if (absPath === additionalPath || absPath.startsWith(`${additionalPath}${path.sep}`)) {
|
|
286
|
+
allowedAdditional = true;
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
if (stat.isFile()) {
|
|
292
|
+
if (absPath === additionalPath && absPath.endsWith(".md")) {
|
|
293
|
+
allowedAdditional = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch { }
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (!allowedWorkspace && !allowedAdditional) {
|
|
302
|
+
throw new Error("path required");
|
|
303
|
+
}
|
|
304
|
+
if (!absPath.endsWith(".md")) {
|
|
305
|
+
throw new Error("path required");
|
|
306
|
+
}
|
|
307
|
+
const stat = await fs.lstat(absPath);
|
|
308
|
+
if (stat.isSymbolicLink() || !stat.isFile()) {
|
|
309
|
+
throw new Error("path required");
|
|
267
310
|
}
|
|
268
311
|
const content = await fs.readFile(absPath, "utf-8");
|
|
269
312
|
if (!params.from && !params.lines) {
|
|
@@ -307,19 +350,20 @@ export class MemoryIndexManager {
|
|
|
307
350
|
entry.chunks = row.c ?? 0;
|
|
308
351
|
bySource.set(row.source, entry);
|
|
309
352
|
}
|
|
310
|
-
return sources.map((source) => ({ source,
|
|
353
|
+
return sources.map((source) => Object.assign({ source }, bySource.get(source)));
|
|
311
354
|
})();
|
|
312
355
|
return {
|
|
313
356
|
backend: "builtin",
|
|
314
357
|
files: files?.c ?? 0,
|
|
315
358
|
chunks: chunks?.c ?? 0,
|
|
316
|
-
dirty: this.dirty,
|
|
359
|
+
dirty: this.dirty || this.sessionsDirty,
|
|
317
360
|
workspaceDir: this.workspaceDir,
|
|
318
361
|
dbPath: this.settings.store.path,
|
|
319
362
|
provider: this.provider.id,
|
|
320
363
|
model: this.provider.model,
|
|
321
364
|
requestedProvider: this.requestedProvider,
|
|
322
365
|
sources: Array.from(this.sources),
|
|
366
|
+
extraPaths: this.settings.extraPaths,
|
|
323
367
|
sourceCounts,
|
|
324
368
|
cache: this.cache.enabled
|
|
325
369
|
? {
|
|
@@ -560,13 +604,27 @@ export class MemoryIndexManager {
|
|
|
560
604
|
}
|
|
561
605
|
}
|
|
562
606
|
ensureWatcher() {
|
|
563
|
-
if (!this.sources.has("memory") || !this.settings.sync.watch || this.watcher)
|
|
607
|
+
if (!this.sources.has("memory") || !this.settings.sync.watch || this.watcher) {
|
|
564
608
|
return;
|
|
565
|
-
|
|
609
|
+
}
|
|
610
|
+
const additionalPaths = normalizeExtraMemoryPaths(this.workspaceDir, this.settings.extraPaths)
|
|
611
|
+
.map((entry) => {
|
|
612
|
+
try {
|
|
613
|
+
const stat = fsSync.lstatSync(entry);
|
|
614
|
+
return stat.isSymbolicLink() ? null : entry;
|
|
615
|
+
}
|
|
616
|
+
catch {
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
})
|
|
620
|
+
.filter((entry) => Boolean(entry));
|
|
621
|
+
const watchPaths = new Set([
|
|
566
622
|
path.join(this.workspaceDir, "MEMORY.md"),
|
|
623
|
+
path.join(this.workspaceDir, "memory.md"),
|
|
567
624
|
path.join(this.workspaceDir, "memory"),
|
|
568
|
-
|
|
569
|
-
|
|
625
|
+
...additionalPaths,
|
|
626
|
+
]);
|
|
627
|
+
this.watcher = chokidar.watch(Array.from(watchPaths), {
|
|
570
628
|
ignoreInitial: true,
|
|
571
629
|
awaitWriteFinish: {
|
|
572
630
|
stabilityThreshold: this.settings.sync.watchDebounceMs,
|
|
@@ -765,7 +823,7 @@ export class MemoryIndexManager {
|
|
|
765
823
|
return this.sessionsDirty && this.sessionsDirtyFiles.size > 0;
|
|
766
824
|
}
|
|
767
825
|
async syncMemoryFiles(params) {
|
|
768
|
-
const files = await listMemoryFiles(this.workspaceDir);
|
|
826
|
+
const files = await listMemoryFiles(this.workspaceDir, this.settings.extraPaths);
|
|
769
827
|
const fileEntries = await Promise.all(files.map(async (file) => buildFileEntry(file, this.workspaceDir)));
|
|
770
828
|
log.debug("memory sync: indexing memory files", {
|
|
771
829
|
files: fileEntries.length,
|
|
@@ -805,7 +863,7 @@ export class MemoryIndexManager {
|
|
|
805
863
|
});
|
|
806
864
|
}
|
|
807
865
|
});
|
|
808
|
-
await
|
|
866
|
+
await runWithConcurrency(tasks, this.getIndexConcurrency());
|
|
809
867
|
const staleRows = this.db
|
|
810
868
|
.prepare(`SELECT path FROM files WHERE source = ?`)
|
|
811
869
|
.all("memory");
|
|
@@ -895,7 +953,7 @@ export class MemoryIndexManager {
|
|
|
895
953
|
});
|
|
896
954
|
}
|
|
897
955
|
});
|
|
898
|
-
await
|
|
956
|
+
await runWithConcurrency(tasks, this.getIndexConcurrency());
|
|
899
957
|
const staleRows = this.db
|
|
900
958
|
.prepare(`SELECT path FROM files WHERE source = ?`)
|
|
901
959
|
.all("sessions");
|
|
@@ -1011,7 +1069,8 @@ export class MemoryIndexManager {
|
|
|
1011
1069
|
const batch = this.settings.remote?.batch;
|
|
1012
1070
|
const enabled = Boolean(batch?.enabled &&
|
|
1013
1071
|
((this.openAi && this.provider.id === "openai") ||
|
|
1014
|
-
(this.gemini && this.provider.id === "gemini")
|
|
1072
|
+
(this.gemini && this.provider.id === "gemini") ||
|
|
1073
|
+
(this.voyage && this.provider.id === "voyage")));
|
|
1015
1074
|
return {
|
|
1016
1075
|
enabled,
|
|
1017
1076
|
wait: batch?.wait ?? true,
|
|
@@ -1031,7 +1090,9 @@ export class MemoryIndexManager {
|
|
|
1031
1090
|
? DEFAULT_GEMINI_EMBEDDING_MODEL
|
|
1032
1091
|
: fallback === "openai"
|
|
1033
1092
|
? DEFAULT_OPENAI_EMBEDDING_MODEL
|
|
1034
|
-
:
|
|
1093
|
+
: fallback === "voyage"
|
|
1094
|
+
? DEFAULT_VOYAGE_EMBEDDING_MODEL
|
|
1095
|
+
: this.settings.model;
|
|
1035
1096
|
const fallbackResult = await createEmbeddingProvider({
|
|
1036
1097
|
config: this.cfg,
|
|
1037
1098
|
agentDir: resolveAgentDir(this.cfg, this.agentId),
|
|
@@ -1046,6 +1107,7 @@ export class MemoryIndexManager {
|
|
|
1046
1107
|
this.provider = fallbackResult.provider;
|
|
1047
1108
|
this.openAi = fallbackResult.openAi;
|
|
1048
1109
|
this.gemini = fallbackResult.gemini;
|
|
1110
|
+
this.voyage = fallbackResult.voyage;
|
|
1049
1111
|
this.providerKey = this.computeProviderKey();
|
|
1050
1112
|
this.batch = this.resolveBatchConfig();
|
|
1051
1113
|
log.warn(`memory embeddings: switched to fallback provider (${fallback})`, { reason });
|
|
@@ -1402,7 +1464,7 @@ export class MemoryIndexManager {
|
|
|
1402
1464
|
if (this.provider.id === "openai" && this.openAi) {
|
|
1403
1465
|
const entries = Object.entries(this.openAi.headers)
|
|
1404
1466
|
.filter(([key]) => key.toLowerCase() !== "authorization")
|
|
1405
|
-
.
|
|
1467
|
+
.toSorted(([a], [b]) => a.localeCompare(b))
|
|
1406
1468
|
.map(([key, value]) => [key, value]);
|
|
1407
1469
|
return hashText(JSON.stringify({
|
|
1408
1470
|
provider: "openai",
|
|
@@ -1417,7 +1479,7 @@ export class MemoryIndexManager {
|
|
|
1417
1479
|
const lower = key.toLowerCase();
|
|
1418
1480
|
return lower !== "authorization" && lower !== "x-goog-api-key";
|
|
1419
1481
|
})
|
|
1420
|
-
.
|
|
1482
|
+
.toSorted(([a], [b]) => a.localeCompare(b))
|
|
1421
1483
|
.map(([key, value]) => [key, value]);
|
|
1422
1484
|
return hashText(JSON.stringify({
|
|
1423
1485
|
provider: "gemini",
|
|
@@ -1435,8 +1497,78 @@ export class MemoryIndexManager {
|
|
|
1435
1497
|
if (this.provider.id === "gemini" && this.gemini) {
|
|
1436
1498
|
return this.embedChunksWithGeminiBatch(chunks, entry, source);
|
|
1437
1499
|
}
|
|
1500
|
+
if (this.provider.id === "voyage" && this.voyage) {
|
|
1501
|
+
return this.embedChunksWithVoyageBatch(chunks, entry, source);
|
|
1502
|
+
}
|
|
1438
1503
|
return this.embedChunksInBatches(chunks);
|
|
1439
1504
|
}
|
|
1505
|
+
async embedChunksWithVoyageBatch(chunks, entry, source) {
|
|
1506
|
+
const voyage = this.voyage;
|
|
1507
|
+
if (!voyage) {
|
|
1508
|
+
return this.embedChunksInBatches(chunks);
|
|
1509
|
+
}
|
|
1510
|
+
if (chunks.length === 0) {
|
|
1511
|
+
return [];
|
|
1512
|
+
}
|
|
1513
|
+
const cached = this.loadEmbeddingCache(chunks.map((chunk) => chunk.hash));
|
|
1514
|
+
const embeddings = Array.from({ length: chunks.length }, () => []);
|
|
1515
|
+
const missing = [];
|
|
1516
|
+
for (let i = 0; i < chunks.length; i += 1) {
|
|
1517
|
+
const chunk = chunks[i];
|
|
1518
|
+
const hit = chunk?.hash ? cached.get(chunk.hash) : undefined;
|
|
1519
|
+
if (hit && hit.length > 0) {
|
|
1520
|
+
embeddings[i] = hit;
|
|
1521
|
+
}
|
|
1522
|
+
else if (chunk) {
|
|
1523
|
+
missing.push({ index: i, chunk });
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
if (missing.length === 0) {
|
|
1527
|
+
return embeddings;
|
|
1528
|
+
}
|
|
1529
|
+
const requests = [];
|
|
1530
|
+
const mapping = new Map();
|
|
1531
|
+
for (const item of missing) {
|
|
1532
|
+
const chunk = item.chunk;
|
|
1533
|
+
const customId = hashText(`${source}:${entry.path}:${chunk.startLine}:${chunk.endLine}:${chunk.hash}:${item.index}`);
|
|
1534
|
+
mapping.set(customId, { index: item.index, hash: chunk.hash });
|
|
1535
|
+
requests.push({
|
|
1536
|
+
custom_id: customId,
|
|
1537
|
+
body: {
|
|
1538
|
+
input: chunk.text,
|
|
1539
|
+
},
|
|
1540
|
+
});
|
|
1541
|
+
}
|
|
1542
|
+
const batchResult = await this.runBatchWithFallback({
|
|
1543
|
+
provider: "voyage",
|
|
1544
|
+
run: async () => await runVoyageEmbeddingBatches({
|
|
1545
|
+
client: voyage,
|
|
1546
|
+
agentId: this.agentId,
|
|
1547
|
+
requests,
|
|
1548
|
+
wait: this.batch.wait,
|
|
1549
|
+
concurrency: this.batch.concurrency,
|
|
1550
|
+
pollIntervalMs: this.batch.pollIntervalMs,
|
|
1551
|
+
timeoutMs: this.batch.timeoutMs,
|
|
1552
|
+
debug: (message, data) => log.debug(message, { ...data, source, chunks: chunks.length }),
|
|
1553
|
+
}),
|
|
1554
|
+
fallback: async () => await this.embedChunksInBatches(chunks),
|
|
1555
|
+
});
|
|
1556
|
+
if (Array.isArray(batchResult)) {
|
|
1557
|
+
return batchResult;
|
|
1558
|
+
}
|
|
1559
|
+
const byCustomId = batchResult;
|
|
1560
|
+
const toCache = [];
|
|
1561
|
+
for (const [customId, embedding] of byCustomId.entries()) {
|
|
1562
|
+
const mapped = mapping.get(customId);
|
|
1563
|
+
if (!mapped) {
|
|
1564
|
+
continue;
|
|
1565
|
+
}
|
|
1566
|
+
embeddings[mapped.index] = embedding;
|
|
1567
|
+
toCache.push({ hash: mapped.hash, embedding });
|
|
1568
|
+
}
|
|
1569
|
+
this.upsertEmbeddingCache(toCache);
|
|
1570
|
+
return embeddings;
|
|
1571
|
+
}
|
|
1440
1572
|
async embedChunksWithOpenAiBatch(chunks, entry, source) {
|
|
1441
1573
|
const openAi = this.openAi;
|
|
1442
1574
|
if (!openAi) {
|
|
@@ -1623,35 +1755,6 @@ export class MemoryIndexManager {
|
|
|
1623
1755
|
clearTimeout(timer);
|
|
1624
1756
|
}
|
|
1625
1757
|
}
|
|
1626
|
-
async runWithConcurrency(tasks, limit) {
|
|
1627
|
-
if (tasks.length === 0)
|
|
1628
|
-
return [];
|
|
1629
|
-
const resolvedLimit = Math.max(1, Math.min(limit, tasks.length));
|
|
1630
|
-
const results = Array.from({ length: tasks.length });
|
|
1631
|
-
let next = 0;
|
|
1632
|
-
let firstError = null;
|
|
1633
|
-
const workers = Array.from({ length: resolvedLimit }, async () => {
|
|
1634
|
-
while (true) {
|
|
1635
|
-
if (firstError)
|
|
1636
|
-
return;
|
|
1637
|
-
const index = next;
|
|
1638
|
-
next += 1;
|
|
1639
|
-
if (index >= tasks.length)
|
|
1640
|
-
return;
|
|
1641
|
-
try {
|
|
1642
|
-
results[index] = await tasks[index]();
|
|
1643
|
-
}
|
|
1644
|
-
catch (err) {
|
|
1645
|
-
firstError = err;
|
|
1646
|
-
return;
|
|
1647
|
-
}
|
|
1648
|
-
}
|
|
1649
|
-
});
|
|
1650
|
-
await Promise.allSettled(workers);
|
|
1651
|
-
if (firstError)
|
|
1652
|
-
throw firstError;
|
|
1653
|
-
return results;
|
|
1654
|
-
}
|
|
1655
1758
|
async withBatchFailureLock(fn) {
|
|
1656
1759
|
let release;
|
|
1657
1760
|
const wait = this.batchFailureLock;
|
|
@@ -1749,6 +1852,9 @@ export class MemoryIndexManager {
|
|
|
1749
1852
|
async indexFile(entry, options) {
|
|
1750
1853
|
const content = options.content ?? (await fs.readFile(entry.absPath, "utf-8"));
|
|
1751
1854
|
const chunks = chunkMarkdown(content, this.settings.chunking).filter((chunk) => chunk.text.trim().length > 0);
|
|
1855
|
+
if (options.source === "sessions" && "lineMap" in entry) {
|
|
1856
|
+
remapChunkLines(chunks, entry.lineMap);
|
|
1857
|
+
}
|
|
1752
1858
|
const embeddings = this.batch.enabled
|
|
1753
1859
|
? await this.embedChunksWithBatch(chunks, entry, options.source)
|
|
1754
1860
|
: await this.embedChunksInBatches(chunks);
|
|
@@ -1,4 +1,40 @@
|
|
|
1
|
+
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
2
|
+
import { resolveMemoryBackendConfig } from "./backend-config.js";
|
|
3
|
+
const log = createSubsystemLogger("memory");
|
|
4
|
+
const QMD_MANAGER_CACHE = new Map();
|
|
1
5
|
export async function getMemorySearchManager(params) {
|
|
6
|
+
const resolved = resolveMemoryBackendConfig(params);
|
|
7
|
+
if (resolved.backend === "qmd" && resolved.qmd) {
|
|
8
|
+
const cacheKey = buildQmdCacheKey(params.agentId, resolved.qmd);
|
|
9
|
+
const cached = QMD_MANAGER_CACHE.get(cacheKey);
|
|
10
|
+
if (cached) {
|
|
11
|
+
return { manager: cached };
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
// @ts-expect-error qmd-manager is an optional module that may not exist at build time
|
|
15
|
+
const { QmdMemoryManager } = await import("./qmd-manager.js");
|
|
16
|
+
const primary = await QmdMemoryManager.create({
|
|
17
|
+
cfg: params.cfg,
|
|
18
|
+
agentId: params.agentId,
|
|
19
|
+
resolved,
|
|
20
|
+
});
|
|
21
|
+
if (primary) {
|
|
22
|
+
const wrapper = new FallbackMemoryManager({
|
|
23
|
+
primary,
|
|
24
|
+
fallbackFactory: async () => {
|
|
25
|
+
const { MemoryIndexManager } = await import("./manager.js");
|
|
26
|
+
return await MemoryIndexManager.get(params);
|
|
27
|
+
},
|
|
28
|
+
}, () => QMD_MANAGER_CACHE.delete(cacheKey));
|
|
29
|
+
QMD_MANAGER_CACHE.set(cacheKey, wrapper);
|
|
30
|
+
return { manager: wrapper };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
35
|
+
log.warn(`qmd memory unavailable; falling back to builtin: ${message}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
2
38
|
try {
|
|
3
39
|
const { MemoryIndexManager } = await import("./manager.js");
|
|
4
40
|
const manager = await MemoryIndexManager.get(params);
|
|
@@ -9,3 +45,140 @@ export async function getMemorySearchManager(params) {
|
|
|
9
45
|
return { manager: null, error: message };
|
|
10
46
|
}
|
|
11
47
|
}
|
|
48
|
+
class FallbackMemoryManager {
|
|
49
|
+
deps;
|
|
50
|
+
onClose;
|
|
51
|
+
fallback = null;
|
|
52
|
+
primaryFailed = false;
|
|
53
|
+
lastError;
|
|
54
|
+
cacheEvicted = false;
|
|
55
|
+
constructor(deps, onClose) {
|
|
56
|
+
this.deps = deps;
|
|
57
|
+
this.onClose = onClose;
|
|
58
|
+
}
|
|
59
|
+
async search(query, opts) {
|
|
60
|
+
if (!this.primaryFailed) {
|
|
61
|
+
try {
|
|
62
|
+
return await this.deps.primary.search(query, opts);
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
this.primaryFailed = true;
|
|
66
|
+
this.lastError = err instanceof Error ? err.message : String(err);
|
|
67
|
+
log.warn(`qmd memory failed; switching to builtin index: ${this.lastError}`);
|
|
68
|
+
await this.deps.primary.close?.().catch(() => { });
|
|
69
|
+
// Evict the failed wrapper so the next request can retry QMD with a fresh manager.
|
|
70
|
+
this.evictCacheEntry();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const fallback = await this.ensureFallback();
|
|
74
|
+
if (fallback) {
|
|
75
|
+
return await fallback.search(query, opts);
|
|
76
|
+
}
|
|
77
|
+
throw new Error(this.lastError ?? "memory search unavailable");
|
|
78
|
+
}
|
|
79
|
+
async readFile(params) {
|
|
80
|
+
if (!this.primaryFailed) {
|
|
81
|
+
return await this.deps.primary.readFile(params);
|
|
82
|
+
}
|
|
83
|
+
const fallback = await this.ensureFallback();
|
|
84
|
+
if (fallback) {
|
|
85
|
+
return await fallback.readFile(params);
|
|
86
|
+
}
|
|
87
|
+
throw new Error(this.lastError ?? "memory read unavailable");
|
|
88
|
+
}
|
|
89
|
+
status() {
|
|
90
|
+
if (!this.primaryFailed) {
|
|
91
|
+
return this.deps.primary.status();
|
|
92
|
+
}
|
|
93
|
+
const fallbackStatus = this.fallback?.status();
|
|
94
|
+
const fallbackInfo = { from: "qmd", reason: this.lastError ?? "unknown" };
|
|
95
|
+
if (fallbackStatus) {
|
|
96
|
+
const custom = fallbackStatus.custom ?? {};
|
|
97
|
+
return {
|
|
98
|
+
...fallbackStatus,
|
|
99
|
+
fallback: fallbackInfo,
|
|
100
|
+
custom: {
|
|
101
|
+
...custom,
|
|
102
|
+
fallback: { disabled: true, reason: this.lastError ?? "unknown" },
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
const primaryStatus = this.deps.primary.status();
|
|
107
|
+
const custom = primaryStatus.custom ?? {};
|
|
108
|
+
return {
|
|
109
|
+
...primaryStatus,
|
|
110
|
+
fallback: fallbackInfo,
|
|
111
|
+
custom: {
|
|
112
|
+
...custom,
|
|
113
|
+
fallback: { disabled: true, reason: this.lastError ?? "unknown" },
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
async sync(params) {
|
|
118
|
+
if (!this.primaryFailed) {
|
|
119
|
+
await this.deps.primary.sync?.(params);
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const fallback = await this.ensureFallback();
|
|
123
|
+
await fallback?.sync?.(params);
|
|
124
|
+
}
|
|
125
|
+
async probeEmbeddingAvailability() {
|
|
126
|
+
if (!this.primaryFailed) {
|
|
127
|
+
return await this.deps.primary.probeEmbeddingAvailability();
|
|
128
|
+
}
|
|
129
|
+
const fallback = await this.ensureFallback();
|
|
130
|
+
if (fallback) {
|
|
131
|
+
return await fallback.probeEmbeddingAvailability();
|
|
132
|
+
}
|
|
133
|
+
return { ok: false, error: this.lastError ?? "memory embeddings unavailable" };
|
|
134
|
+
}
|
|
135
|
+
async probeVectorAvailability() {
|
|
136
|
+
if (!this.primaryFailed) {
|
|
137
|
+
return await this.deps.primary.probeVectorAvailability();
|
|
138
|
+
}
|
|
139
|
+
const fallback = await this.ensureFallback();
|
|
140
|
+
return (await fallback?.probeVectorAvailability()) ?? false;
|
|
141
|
+
}
|
|
142
|
+
async close() {
|
|
143
|
+
await this.deps.primary.close?.();
|
|
144
|
+
await this.fallback?.close?.();
|
|
145
|
+
this.evictCacheEntry();
|
|
146
|
+
}
|
|
147
|
+
async ensureFallback() {
|
|
148
|
+
if (this.fallback) {
|
|
149
|
+
return this.fallback;
|
|
150
|
+
}
|
|
151
|
+
const fallback = await this.deps.fallbackFactory();
|
|
152
|
+
if (!fallback) {
|
|
153
|
+
log.warn("memory fallback requested but builtin index is unavailable");
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
this.fallback = fallback;
|
|
157
|
+
return this.fallback;
|
|
158
|
+
}
|
|
159
|
+
evictCacheEntry() {
|
|
160
|
+
if (this.cacheEvicted) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
this.cacheEvicted = true;
|
|
164
|
+
this.onClose?.();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function buildQmdCacheKey(agentId, config) {
|
|
168
|
+
return `${agentId}:${stableSerialize(config)}`;
|
|
169
|
+
}
|
|
170
|
+
function stableSerialize(value) {
|
|
171
|
+
return JSON.stringify(sortValue(value));
|
|
172
|
+
}
|
|
173
|
+
function sortValue(value) {
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
return value.map((entry) => sortValue(entry));
|
|
176
|
+
}
|
|
177
|
+
if (value && typeof value === "object") {
|
|
178
|
+
const sortedEntries = Object.keys(value)
|
|
179
|
+
.toSorted((a, b) => a.localeCompare(b))
|
|
180
|
+
.map((key) => [key, sortValue(value[key])]);
|
|
181
|
+
return Object.fromEntries(sortedEntries);
|
|
182
|
+
}
|
|
183
|
+
return value;
|
|
184
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { resolveSessionTranscriptsDirForAgent } from "../config/sessions/paths.js";
|
|
4
|
+
import { redactSensitiveText } from "../logging/redact.js";
|
|
4
5
|
import { createSubsystemLogger } from "../logging/subsystem.js";
|
|
5
6
|
import { hashText } from "./internal.js";
|
|
6
7
|
const log = createSubsystemLogger("memory");
|
|
@@ -55,7 +56,9 @@ export async function buildSessionEntry(absPath) {
|
|
|
55
56
|
const raw = await fs.readFile(absPath, "utf-8");
|
|
56
57
|
const lines = raw.split("\n");
|
|
57
58
|
const collected = [];
|
|
58
|
-
|
|
59
|
+
const lineMap = [];
|
|
60
|
+
for (let jsonlIdx = 0; jsonlIdx < lines.length; jsonlIdx++) {
|
|
61
|
+
const line = lines[jsonlIdx];
|
|
59
62
|
if (!line.trim())
|
|
60
63
|
continue;
|
|
61
64
|
let record;
|
|
@@ -78,8 +81,10 @@ export async function buildSessionEntry(absPath) {
|
|
|
78
81
|
const text = extractSessionText(message.content);
|
|
79
82
|
if (!text)
|
|
80
83
|
continue;
|
|
84
|
+
const safe = redactSensitiveText(text, { mode: "tools" });
|
|
81
85
|
const label = message.role === "user" ? "User" : "Assistant";
|
|
82
|
-
collected.push(`${label}: ${
|
|
86
|
+
collected.push(`${label}: ${safe}`);
|
|
87
|
+
lineMap.push(jsonlIdx + 1);
|
|
83
88
|
}
|
|
84
89
|
const content = collected.join("\n");
|
|
85
90
|
return {
|
|
@@ -87,8 +92,9 @@ export async function buildSessionEntry(absPath) {
|
|
|
87
92
|
absPath,
|
|
88
93
|
mtimeMs: stat.mtimeMs,
|
|
89
94
|
size: stat.size,
|
|
90
|
-
hash: hashText(content),
|
|
95
|
+
hash: hashText(content + "\n" + lineMap.join(",")),
|
|
91
96
|
content,
|
|
97
|
+
lineMap,
|
|
92
98
|
};
|
|
93
99
|
}
|
|
94
100
|
catch (err) {
|
package/dist/node-host/runner.js
CHANGED
|
@@ -17,10 +17,16 @@ import { ensurePoolbotCliOnPath } from "../infra/path-env.js";
|
|
|
17
17
|
import { VERSION } from "../version.js";
|
|
18
18
|
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
|
|
19
19
|
import { ensureNodeHostConfig, saveNodeHostConfig } from "./config.js";
|
|
20
|
+
import { withTimeout } from "./with-timeout.js";
|
|
20
21
|
import { GatewayClient } from "../gateway/client.js";
|
|
21
22
|
function resolveExecSecurity(value) {
|
|
22
23
|
return value === "deny" || value === "allowlist" || value === "full" ? value : "allowlist";
|
|
23
24
|
}
|
|
25
|
+
function isCmdExeInvocation(argv) {
|
|
26
|
+
const bin = argv[0]?.trim().toLowerCase().replace(/\\/g, "/") ?? "";
|
|
27
|
+
const base = bin.split("/").pop() ?? "";
|
|
28
|
+
return base === "cmd" || base === "cmd.exe";
|
|
29
|
+
}
|
|
24
30
|
function resolveExecAsk(value) {
|
|
25
31
|
return value === "off" || value === "on-miss" || value === "always" ? value : "on-miss";
|
|
26
32
|
}
|
|
@@ -124,26 +130,6 @@ async function ensureBrowserControlService() {
|
|
|
124
130
|
})();
|
|
125
131
|
return browserControlReady;
|
|
126
132
|
}
|
|
127
|
-
async function withTimeout(promise, timeoutMs, label) {
|
|
128
|
-
const resolved = typeof timeoutMs === "number" && Number.isFinite(timeoutMs)
|
|
129
|
-
? Math.max(1, Math.floor(timeoutMs))
|
|
130
|
-
: undefined;
|
|
131
|
-
if (!resolved)
|
|
132
|
-
return await promise;
|
|
133
|
-
let timer;
|
|
134
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
135
|
-
timer = setTimeout(() => {
|
|
136
|
-
reject(new Error(`${label ?? "request"} timed out`));
|
|
137
|
-
}, resolved);
|
|
138
|
-
});
|
|
139
|
-
try {
|
|
140
|
-
return await Promise.race([promise, timeoutPromise]);
|
|
141
|
-
}
|
|
142
|
-
finally {
|
|
143
|
-
if (timer)
|
|
144
|
-
clearTimeout(timer);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
133
|
function isProfileAllowed(params) {
|
|
148
134
|
const { allowProfiles, profile } = params;
|
|
149
135
|
if (!allowProfiles.length)
|
|
@@ -422,7 +408,7 @@ export async function runNodeHost(opts) {
|
|
|
422
408
|
},
|
|
423
409
|
});
|
|
424
410
|
const skillBins = new SkillBinsCache(async () => {
|
|
425
|
-
const res =
|
|
411
|
+
const res = await client.request("skills.bins", {});
|
|
426
412
|
const bins = Array.isArray(res?.bins) ? res.bins.map((bin) => String(bin)) : [];
|
|
427
413
|
return bins;
|
|
428
414
|
});
|
|
@@ -562,7 +548,7 @@ async function handleInvoke(frame, client, skillBins) {
|
|
|
562
548
|
query[key] = typeof value === "string" ? value : String(value);
|
|
563
549
|
}
|
|
564
550
|
const dispatcher = createBrowserRouteDispatcher(createBrowserControlContext());
|
|
565
|
-
const response = await withTimeout(dispatcher.dispatch({
|
|
551
|
+
const response = await withTimeout((_signal) => dispatcher.dispatch({
|
|
566
552
|
method: method === "DELETE" ? "DELETE" : method === "POST" ? "POST" : "GET",
|
|
567
553
|
path,
|
|
568
554
|
query,
|
|
@@ -597,7 +583,9 @@ async function handleInvoke(frame, client, skillBins) {
|
|
|
597
583
|
return file;
|
|
598
584
|
}
|
|
599
585
|
catch (err) {
|
|
600
|
-
throw new Error(`browser proxy file read failed for ${p}: ${String(err)}
|
|
586
|
+
throw new Error(`browser proxy file read failed for ${p}: ${String(err)}`, {
|
|
587
|
+
cause: err,
|
|
588
|
+
});
|
|
601
589
|
}
|
|
602
590
|
}));
|
|
603
591
|
if (loaded.length > 0)
|
|
@@ -675,6 +663,7 @@ async function handleInvoke(frame, client, skillBins) {
|
|
|
675
663
|
env,
|
|
676
664
|
skillBins: bins,
|
|
677
665
|
autoAllowSkills,
|
|
666
|
+
platform: process.platform,
|
|
678
667
|
});
|
|
679
668
|
analysisOk = allowlistEval.analysisOk;
|
|
680
669
|
allowlistMatches = allowlistEval.allowlistMatches;
|
|
@@ -698,6 +687,14 @@ async function handleInvoke(frame, client, skillBins) {
|
|
|
698
687
|
security === "allowlist" && analysisOk ? allowlistEval.allowlistSatisfied : false;
|
|
699
688
|
segments = analysis.segments;
|
|
700
689
|
}
|
|
690
|
+
const isWindows = process.platform === "win32";
|
|
691
|
+
const cmdInvocation = rawCommand
|
|
692
|
+
? isCmdExeInvocation(segments[0]?.argv ?? [])
|
|
693
|
+
: isCmdExeInvocation(argv);
|
|
694
|
+
if (security === "allowlist" && isWindows && cmdInvocation) {
|
|
695
|
+
analysisOk = false;
|
|
696
|
+
allowlistSatisfied = false;
|
|
697
|
+
}
|
|
701
698
|
const useMacAppExec = process.platform === "darwin";
|
|
702
699
|
if (useMacAppExec) {
|
|
703
700
|
const approvalDecision = params.approvalDecision === "allow-once" || params.approvalDecision === "allow-always"
|
|
@@ -853,7 +850,20 @@ async function handleInvoke(frame, client, skillBins) {
|
|
|
853
850
|
});
|
|
854
851
|
return;
|
|
855
852
|
}
|
|
856
|
-
|
|
853
|
+
/* On Windows, avoid spawning via cmd.exe when the allowlist already approved
|
|
854
|
+
* a single-segment command — run the parsed argv directly instead. */
|
|
855
|
+
let execArgv = argv;
|
|
856
|
+
if (security === "allowlist" &&
|
|
857
|
+
isWindows &&
|
|
858
|
+
!approvedByAsk &&
|
|
859
|
+
rawCommand &&
|
|
860
|
+
analysisOk &&
|
|
861
|
+
allowlistSatisfied &&
|
|
862
|
+
segments.length === 1 &&
|
|
863
|
+
segments[0]?.argv.length > 0) {
|
|
864
|
+
execArgv = segments[0].argv;
|
|
865
|
+
}
|
|
866
|
+
const result = await runCommand(execArgv, params.cwd?.trim() || undefined, env, params.timeoutMs ?? undefined);
|
|
857
867
|
if (result.truncated) {
|
|
858
868
|
const suffix = "... (truncated)";
|
|
859
869
|
if (result.stderr.trim().length > 0) {
|