@oyasmi/pipiclaw 0.5.9 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent/channel-runner.d.ts +8 -0
- package/dist/agent/channel-runner.js +132 -24
- package/dist/agent/context-budget.d.ts +9 -0
- package/dist/agent/context-budget.js +31 -0
- package/dist/agent/session-events.js +11 -4
- package/dist/agent/type-guards.js +4 -2
- package/dist/agent/types.d.ts +10 -3
- package/dist/agent/types.js +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -2
- package/dist/memory/candidates.d.ts +8 -5
- package/dist/memory/candidates.js +92 -42
- package/dist/memory/consolidation.js +13 -4
- package/dist/memory/recall.d.ts +2 -2
- package/dist/memory/recall.js +2 -3
- package/dist/memory/session.js +2 -2
- package/dist/memory/sidecar-worker.d.ts +1 -0
- package/dist/memory/sidecar-worker.js +56 -1
- package/dist/paths.d.ts +1 -0
- package/dist/paths.js +1 -0
- package/dist/runtime/bootstrap.d.ts +1 -0
- package/dist/runtime/bootstrap.js +52 -13
- package/dist/runtime/delivery.js +101 -12
- package/dist/runtime/dingtalk.d.ts +11 -1
- package/dist/runtime/dingtalk.js +69 -24
- package/dist/runtime/events.d.ts +17 -2
- package/dist/runtime/events.js +107 -19
- package/dist/security/command-guard.js +4 -0
- package/dist/security/config.d.ts +6 -0
- package/dist/security/config.js +38 -6
- package/dist/security/path-guard.js +4 -0
- package/dist/security/platform.d.ts +1 -0
- package/dist/security/platform.js +3 -0
- package/dist/settings.d.ts +4 -1
- package/dist/settings.js +31 -6
- package/dist/shared/config-diagnostics.d.ts +7 -0
- package/dist/shared/config-diagnostics.js +3 -0
- package/dist/subagents/tool.d.ts +2 -0
- package/dist/subagents/tool.js +2 -3
- package/dist/tools/config.d.ts +7 -0
- package/dist/tools/config.js +63 -7
- package/dist/tools/index.d.ts +5 -0
- package/dist/tools/index.js +3 -2
- package/dist/web/client.d.ts +1 -0
- package/dist/web/client.js +30 -18
- package/dist/web/config.d.ts +1 -0
- package/dist/web/config.js +1 -0
- package/dist/web/fetch.d.ts +1 -0
- package/dist/web/fetch.js +7 -5
- package/dist/web/search-providers.js +6 -3
- package/package.json +1 -1
package/dist/settings.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
10
10
|
import { dirname, join } from "path";
|
|
11
|
+
import * as log from "./log.js";
|
|
11
12
|
const DEFAULT_COMPACTION = {
|
|
12
13
|
enabled: true,
|
|
13
14
|
reserveTokens: 16384,
|
|
@@ -40,18 +41,32 @@ const DEFAULT_SESSION_MEMORY = {
|
|
|
40
41
|
*/
|
|
41
42
|
export class PipiclawSettingsManager {
|
|
42
43
|
constructor(baseDir) {
|
|
44
|
+
this.loadErrors = [];
|
|
43
45
|
this.settingsPath = join(baseDir, "settings.json");
|
|
44
46
|
this.settings = this.load();
|
|
45
47
|
}
|
|
46
48
|
load() {
|
|
49
|
+
this.loadErrors = [];
|
|
47
50
|
if (!existsSync(this.settingsPath)) {
|
|
48
51
|
return {};
|
|
49
52
|
}
|
|
50
53
|
try {
|
|
51
54
|
const content = readFileSync(this.settingsPath, "utf-8");
|
|
52
|
-
|
|
55
|
+
const parsed = JSON.parse(content);
|
|
56
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
57
|
+
this.loadErrors.push({
|
|
58
|
+
scope: "global",
|
|
59
|
+
error: new Error(`Expected a JSON object in ${this.settingsPath}`),
|
|
60
|
+
});
|
|
61
|
+
return {};
|
|
62
|
+
}
|
|
63
|
+
return parsed;
|
|
53
64
|
}
|
|
54
|
-
catch {
|
|
65
|
+
catch (error) {
|
|
66
|
+
this.loadErrors.push({
|
|
67
|
+
scope: "global",
|
|
68
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
69
|
+
});
|
|
55
70
|
return {};
|
|
56
71
|
}
|
|
57
72
|
}
|
|
@@ -64,12 +79,25 @@ export class PipiclawSettingsManager {
|
|
|
64
79
|
writeFileSync(this.settingsPath, JSON.stringify(this.settings, null, 2), "utf-8");
|
|
65
80
|
}
|
|
66
81
|
catch (error) {
|
|
67
|
-
|
|
82
|
+
log.logWarning(`Could not save settings file`, `${this.settingsPath}\n${String(error)}`);
|
|
68
83
|
}
|
|
69
84
|
}
|
|
70
85
|
reload() {
|
|
71
86
|
this.settings = this.load();
|
|
72
87
|
}
|
|
88
|
+
drainErrors() {
|
|
89
|
+
const errors = this.loadErrors;
|
|
90
|
+
this.loadErrors = [];
|
|
91
|
+
return errors;
|
|
92
|
+
}
|
|
93
|
+
getDiagnostics() {
|
|
94
|
+
return this.loadErrors.map(({ error }) => ({
|
|
95
|
+
source: "settings",
|
|
96
|
+
path: this.settingsPath,
|
|
97
|
+
severity: "error",
|
|
98
|
+
message: error.message,
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
73
101
|
getCompactionSettings() {
|
|
74
102
|
return {
|
|
75
103
|
...DEFAULT_COMPACTION,
|
|
@@ -377,7 +405,4 @@ export class PipiclawSettingsManager {
|
|
|
377
405
|
flush() {
|
|
378
406
|
return Promise.resolve();
|
|
379
407
|
}
|
|
380
|
-
drainErrors() {
|
|
381
|
-
return [];
|
|
382
|
-
}
|
|
383
408
|
}
|
package/dist/subagents/tool.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AgentEvent, type AgentMessage, type AgentTool } from "@mariozechner/pi-agent-core";
|
|
2
2
|
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
3
|
+
import type { MemoryCandidateStore } from "../memory/candidates.js";
|
|
3
4
|
import type { Executor } from "../sandbox.js";
|
|
4
5
|
import type { SecurityConfig } from "../security/types.js";
|
|
5
6
|
import type { PipiclawMemoryRecallSettings } from "../settings.js";
|
|
@@ -44,6 +45,7 @@ export interface SubAgentToolOptions {
|
|
|
44
45
|
channelDir: string;
|
|
45
46
|
getSubAgentDiscovery?: () => SubAgentDiscoveryResult;
|
|
46
47
|
getMemoryRecallSettings?: () => PipiclawMemoryRecallSettings;
|
|
48
|
+
memoryCandidateStore?: MemoryCandidateStore;
|
|
47
49
|
securityConfig?: SecurityConfig;
|
|
48
50
|
webConfig?: PipiclawWebToolsConfig;
|
|
49
51
|
runtimeContext: {
|
package/dist/subagents/tool.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Agent } from "@mariozechner/pi-agent-core";
|
|
2
2
|
import { convertToLlm } from "@mariozechner/pi-coding-agent";
|
|
3
3
|
import { Type } from "@sinclair/typebox";
|
|
4
|
-
import { createMemoryCandidateCache } from "../memory/candidates.js";
|
|
5
4
|
import { readChannelSession } from "../memory/files.js";
|
|
6
5
|
import { recallRelevantMemory } from "../memory/recall.js";
|
|
7
6
|
import { formatModelReference } from "../models/utils.js";
|
|
@@ -187,7 +186,7 @@ function stripRuntimeContextWrapper(renderedText) {
|
|
|
187
186
|
.replace(/\s*<\/runtime_context>$/i, "")
|
|
188
187
|
.trim();
|
|
189
188
|
}
|
|
190
|
-
async function buildContextualBlocks(task, config, options, currentModel
|
|
189
|
+
async function buildContextualBlocks(task, config, options, currentModel) {
|
|
191
190
|
if (config.contextMode !== "contextual") {
|
|
192
191
|
return [];
|
|
193
192
|
}
|
|
@@ -226,7 +225,7 @@ async function buildContextualBlocks(task, config, options, currentModel, candid
|
|
|
226
225
|
model: currentModel,
|
|
227
226
|
resolveApiKey: options.resolveApiKey,
|
|
228
227
|
allowedSources: ["workspace-memory", "channel-memory", "channel-history"],
|
|
229
|
-
|
|
228
|
+
candidateStore: options.memoryCandidateStore,
|
|
230
229
|
});
|
|
231
230
|
const recalledText = stripRuntimeContextWrapper(recalled.renderedText);
|
|
232
231
|
if (recalledText) {
|
package/dist/tools/config.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConfigDiagnostic } from "../shared/config-diagnostics.js";
|
|
1
2
|
export type WebSearchProvider = "brave" | "tavily" | "jina" | "searxng" | "duckduckgo";
|
|
2
3
|
export interface PipiclawWebSearchConfig {
|
|
3
4
|
provider: WebSearchProvider;
|
|
@@ -10,6 +11,7 @@ export interface PipiclawWebFetchConfig {
|
|
|
10
11
|
maxChars: number;
|
|
11
12
|
timeoutMs: number;
|
|
12
13
|
maxImageBytes: number;
|
|
14
|
+
maxResponseBytes: number;
|
|
13
15
|
preferJina: boolean;
|
|
14
16
|
enableJinaFallback: boolean;
|
|
15
17
|
defaultExtractMode: "markdown" | "text";
|
|
@@ -25,6 +27,11 @@ export interface PipiclawToolsConfig {
|
|
|
25
27
|
web: PipiclawWebToolsConfig;
|
|
26
28
|
};
|
|
27
29
|
}
|
|
30
|
+
export interface LoadedToolsConfig {
|
|
31
|
+
config: PipiclawToolsConfig;
|
|
32
|
+
diagnostics: ConfigDiagnostic[];
|
|
33
|
+
}
|
|
28
34
|
export declare const DEFAULT_TOOLS_CONFIG: PipiclawToolsConfig;
|
|
29
35
|
export declare function getToolsConfigPath(appHomeDir?: string): string;
|
|
36
|
+
export declare function loadToolsConfigWithDiagnostics(appHomeDir?: string): LoadedToolsConfig;
|
|
30
37
|
export declare function loadToolsConfig(appHomeDir?: string): PipiclawToolsConfig;
|
package/dist/tools/config.js
CHANGED
|
@@ -19,6 +19,7 @@ export const DEFAULT_TOOLS_CONFIG = {
|
|
|
19
19
|
maxChars: 50_000,
|
|
20
20
|
timeoutMs: 30_000,
|
|
21
21
|
maxImageBytes: 10 * 1024 * 1024,
|
|
22
|
+
maxResponseBytes: 5 * 1024 * 1024,
|
|
22
23
|
preferJina: false,
|
|
23
24
|
enableJinaFallback: false,
|
|
24
25
|
defaultExtractMode: "markdown",
|
|
@@ -52,8 +53,17 @@ function asOptionalProxy(value) {
|
|
|
52
53
|
const trimmed = value.trim();
|
|
53
54
|
return trimmed.length > 0 ? trimmed : null;
|
|
54
55
|
}
|
|
55
|
-
function
|
|
56
|
+
function pushInvalidValueDiagnostic(diagnostics, configPath, field, message) {
|
|
57
|
+
diagnostics.push({
|
|
58
|
+
source: "tools",
|
|
59
|
+
path: configPath,
|
|
60
|
+
severity: "warning",
|
|
61
|
+
message: `${field}: ${message}`,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
function mergeToolsConfig(source, configPath, diagnostics) {
|
|
56
65
|
if (!isRecord(source)) {
|
|
66
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "root", "expected a JSON object; using defaults");
|
|
57
67
|
return DEFAULT_TOOLS_CONFIG;
|
|
58
68
|
}
|
|
59
69
|
const tools = isRecord(source.tools) ? source.tools : {};
|
|
@@ -63,8 +73,37 @@ function mergeToolsConfig(source) {
|
|
|
63
73
|
const providerValue = asTrimmedString(search.provider, DEFAULT_TOOLS_CONFIG.tools.web.search.provider).toLowerCase();
|
|
64
74
|
const provider = WEB_SEARCH_PROVIDERS.includes(providerValue)
|
|
65
75
|
? providerValue
|
|
66
|
-
:
|
|
76
|
+
: (() => {
|
|
77
|
+
if (search.provider !== undefined) {
|
|
78
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.search.provider", `unknown provider "${String(search.provider)}"; using ${DEFAULT_TOOLS_CONFIG.tools.web.search.provider}`);
|
|
79
|
+
}
|
|
80
|
+
return DEFAULT_TOOLS_CONFIG.tools.web.search.provider;
|
|
81
|
+
})();
|
|
67
82
|
const defaultExtractMode = asTrimmedString(fetch.defaultExtractMode, DEFAULT_TOOLS_CONFIG.tools.web.fetch.defaultExtractMode);
|
|
83
|
+
if (web.proxy !== undefined && web.proxy !== null && typeof web.proxy !== "string") {
|
|
84
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.proxy", "expected a string or null; using null");
|
|
85
|
+
}
|
|
86
|
+
if (search.maxResults !== undefined && clampInteger(search.maxResults, -1, 1, 10) === -1) {
|
|
87
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.search.maxResults", "expected an integer between 1 and 10; using default");
|
|
88
|
+
}
|
|
89
|
+
if (search.timeoutMs !== undefined && clampInteger(search.timeoutMs, -1, 1) === -1) {
|
|
90
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.search.timeoutMs", "expected a positive integer; using default");
|
|
91
|
+
}
|
|
92
|
+
if (fetch.maxChars !== undefined && clampInteger(fetch.maxChars, -1, 100) === -1) {
|
|
93
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.fetch.maxChars", "expected an integer >= 100; using default");
|
|
94
|
+
}
|
|
95
|
+
if (fetch.timeoutMs !== undefined && clampInteger(fetch.timeoutMs, -1, 1) === -1) {
|
|
96
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.fetch.timeoutMs", "expected a positive integer; using default");
|
|
97
|
+
}
|
|
98
|
+
if (fetch.maxImageBytes !== undefined && clampInteger(fetch.maxImageBytes, -1, 1) === -1) {
|
|
99
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.fetch.maxImageBytes", "expected a positive integer; using default");
|
|
100
|
+
}
|
|
101
|
+
if (fetch.maxResponseBytes !== undefined && clampInteger(fetch.maxResponseBytes, -1, 1) === -1) {
|
|
102
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.fetch.maxResponseBytes", "expected a positive integer; using default");
|
|
103
|
+
}
|
|
104
|
+
if (fetch.defaultExtractMode !== undefined && defaultExtractMode !== "text" && defaultExtractMode !== "markdown") {
|
|
105
|
+
pushInvalidValueDiagnostic(diagnostics, configPath, "tools.web.fetch.defaultExtractMode", `expected "markdown" or "text"; using ${DEFAULT_TOOLS_CONFIG.tools.web.fetch.defaultExtractMode}`);
|
|
106
|
+
}
|
|
68
107
|
return {
|
|
69
108
|
tools: {
|
|
70
109
|
web: {
|
|
@@ -81,6 +120,7 @@ function mergeToolsConfig(source) {
|
|
|
81
120
|
maxChars: clampInteger(fetch.maxChars, DEFAULT_TOOLS_CONFIG.tools.web.fetch.maxChars, 100),
|
|
82
121
|
timeoutMs: clampInteger(fetch.timeoutMs, DEFAULT_TOOLS_CONFIG.tools.web.fetch.timeoutMs, 1),
|
|
83
122
|
maxImageBytes: clampInteger(fetch.maxImageBytes, DEFAULT_TOOLS_CONFIG.tools.web.fetch.maxImageBytes, 1),
|
|
123
|
+
maxResponseBytes: clampInteger(fetch.maxResponseBytes, DEFAULT_TOOLS_CONFIG.tools.web.fetch.maxResponseBytes, 1),
|
|
84
124
|
preferJina: typeof fetch.preferJina === "boolean"
|
|
85
125
|
? fetch.preferJina
|
|
86
126
|
: DEFAULT_TOOLS_CONFIG.tools.web.fetch.preferJina,
|
|
@@ -98,17 +138,33 @@ function mergeToolsConfig(source) {
|
|
|
98
138
|
export function getToolsConfigPath(appHomeDir = APP_HOME_DIR) {
|
|
99
139
|
return appHomeDir === APP_HOME_DIR ? TOOLS_CONFIG_PATH : join(appHomeDir, "tools.json");
|
|
100
140
|
}
|
|
101
|
-
export function
|
|
141
|
+
export function loadToolsConfigWithDiagnostics(appHomeDir = APP_HOME_DIR) {
|
|
102
142
|
const configPath = getToolsConfigPath(appHomeDir);
|
|
103
143
|
if (!existsSync(configPath)) {
|
|
104
|
-
return DEFAULT_TOOLS_CONFIG;
|
|
144
|
+
return { config: DEFAULT_TOOLS_CONFIG, diagnostics: [] };
|
|
105
145
|
}
|
|
106
146
|
try {
|
|
107
147
|
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
108
|
-
|
|
148
|
+
const diagnostics = [];
|
|
149
|
+
return {
|
|
150
|
+
config: mergeToolsConfig(raw, configPath, diagnostics),
|
|
151
|
+
diagnostics,
|
|
152
|
+
};
|
|
109
153
|
}
|
|
110
154
|
catch (error) {
|
|
111
|
-
|
|
112
|
-
|
|
155
|
+
return {
|
|
156
|
+
config: DEFAULT_TOOLS_CONFIG,
|
|
157
|
+
diagnostics: [
|
|
158
|
+
{
|
|
159
|
+
source: "tools",
|
|
160
|
+
path: configPath,
|
|
161
|
+
severity: "error",
|
|
162
|
+
message: error instanceof Error ? error.message : String(error),
|
|
163
|
+
},
|
|
164
|
+
],
|
|
165
|
+
};
|
|
113
166
|
}
|
|
114
167
|
}
|
|
168
|
+
export function loadToolsConfig(appHomeDir = APP_HOME_DIR) {
|
|
169
|
+
return loadToolsConfigWithDiagnostics(appHomeDir).config;
|
|
170
|
+
}
|
package/dist/tools/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { AgentTool } from "@mariozechner/pi-agent-core";
|
|
2
2
|
import type { Api, Model } from "@mariozechner/pi-ai";
|
|
3
|
+
import type { MemoryCandidateStore } from "../memory/candidates.js";
|
|
3
4
|
import type { Executor, SandboxConfig } from "../sandbox.js";
|
|
4
5
|
import type { SecurityConfig, SecurityRuntimeContext } from "../security/types.js";
|
|
5
6
|
import type { PipiclawMemoryRecallSettings } from "../settings.js";
|
|
6
7
|
import type { SubAgentDiscoveryResult } from "../subagents/discovery.js";
|
|
8
|
+
import type { PipiclawToolsConfig } from "./config.js";
|
|
7
9
|
export interface CreatePipiclawToolsOptions {
|
|
8
10
|
executor: Executor;
|
|
9
11
|
getCurrentModel: () => Model<Api>;
|
|
@@ -16,6 +18,9 @@ export interface CreatePipiclawToolsOptions {
|
|
|
16
18
|
sandboxConfig: SandboxConfig;
|
|
17
19
|
getSubAgentDiscovery: () => SubAgentDiscoveryResult;
|
|
18
20
|
getMemoryRecallSettings: () => PipiclawMemoryRecallSettings;
|
|
21
|
+
memoryCandidateStore: MemoryCandidateStore;
|
|
22
|
+
securityConfig?: SecurityConfig;
|
|
23
|
+
toolsConfig?: PipiclawToolsConfig;
|
|
19
24
|
}
|
|
20
25
|
export interface CreatePipiclawBaseToolsOptions {
|
|
21
26
|
securityConfig?: SecurityConfig;
|
package/dist/tools/index.js
CHANGED
|
@@ -25,8 +25,8 @@ export function createPipiclawBaseTools(executor, options = {}) {
|
|
|
25
25
|
];
|
|
26
26
|
}
|
|
27
27
|
export function createPipiclawTools(options) {
|
|
28
|
-
const securityConfig = loadSecurityConfig(APP_HOME_DIR);
|
|
29
|
-
const toolsConfig = loadToolsConfig(APP_HOME_DIR);
|
|
28
|
+
const securityConfig = options.securityConfig ?? loadSecurityConfig(APP_HOME_DIR);
|
|
29
|
+
const toolsConfig = options.toolsConfig ?? loadToolsConfig(APP_HOME_DIR);
|
|
30
30
|
const securityContext = {
|
|
31
31
|
workspaceDir: options.workspaceDir,
|
|
32
32
|
workspacePath: options.workspacePath,
|
|
@@ -65,6 +65,7 @@ export function createPipiclawTools(options) {
|
|
|
65
65
|
channelDir: options.channelDir,
|
|
66
66
|
getSubAgentDiscovery: options.getSubAgentDiscovery,
|
|
67
67
|
getMemoryRecallSettings: options.getMemoryRecallSettings,
|
|
68
|
+
memoryCandidateStore: options.memoryCandidateStore,
|
|
68
69
|
securityConfig,
|
|
69
70
|
webConfig: toolsConfig.tools.web,
|
|
70
71
|
runtimeContext: {
|
package/dist/web/client.d.ts
CHANGED
package/dist/web/client.js
CHANGED
|
@@ -108,24 +108,36 @@ export class WebHttpClient {
|
|
|
108
108
|
throw error;
|
|
109
109
|
}
|
|
110
110
|
const agent = getProxyAgent(currentUrl, this.context.webConfig.proxy);
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
111
|
+
let response;
|
|
112
|
+
try {
|
|
113
|
+
response = await axios.request({
|
|
114
|
+
method,
|
|
115
|
+
url: currentUrl,
|
|
116
|
+
data,
|
|
117
|
+
headers: {
|
|
118
|
+
"User-Agent": WEB_USER_AGENT,
|
|
119
|
+
Accept: "*/*",
|
|
120
|
+
...options.headers,
|
|
121
|
+
},
|
|
122
|
+
responseType: "arraybuffer",
|
|
123
|
+
validateStatus: () => true,
|
|
124
|
+
timeout: options.timeoutMs,
|
|
125
|
+
signal: options.signal,
|
|
126
|
+
maxRedirects: 0,
|
|
127
|
+
maxContentLength: options.maxResponseBytes ?? Number.POSITIVE_INFINITY,
|
|
128
|
+
proxy: false,
|
|
129
|
+
httpAgent: agent,
|
|
130
|
+
httpsAgent: agent,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
if (options.maxResponseBytes &&
|
|
135
|
+
typeof error?.message === "string" &&
|
|
136
|
+
error.message.includes("maxContentLength")) {
|
|
137
|
+
throw new Error(`Response exceeds maxResponseBytes (${options.maxResponseBytes} bytes)`);
|
|
138
|
+
}
|
|
139
|
+
throw error;
|
|
140
|
+
}
|
|
129
141
|
const headers = normalizeHeaders(response.headers);
|
|
130
142
|
const body = Buffer.isBuffer(response.data) ? response.data : Buffer.from(response.data);
|
|
131
143
|
if (isRedirectStatus(response.status) && headers.location) {
|
package/dist/web/config.d.ts
CHANGED
package/dist/web/config.js
CHANGED
|
@@ -12,6 +12,7 @@ export function resolveWebFetchRequest(config, url, extractMode, maxChars) {
|
|
|
12
12
|
maxChars: clamp(maxChars, config.maxChars, 100),
|
|
13
13
|
timeoutMs: config.timeoutMs,
|
|
14
14
|
maxImageBytes: config.maxImageBytes,
|
|
15
|
+
maxResponseBytes: config.maxResponseBytes,
|
|
15
16
|
preferJina: config.preferJina,
|
|
16
17
|
enableJinaFallback: config.enableJinaFallback,
|
|
17
18
|
};
|
package/dist/web/fetch.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare function runWebFetch(context: WebFetchExecutionContext, request:
|
|
|
17
17
|
extractMode: "markdown" | "text";
|
|
18
18
|
maxChars: number;
|
|
19
19
|
maxImageBytes: number;
|
|
20
|
+
maxResponseBytes: number;
|
|
20
21
|
preferJina: boolean;
|
|
21
22
|
enableJinaFallback: boolean;
|
|
22
23
|
}, signal?: AbortSignal): Promise<WebFetchOutput>;
|
package/dist/web/fetch.js
CHANGED
|
@@ -22,7 +22,7 @@ function isHtmlContent(contentType, body) {
|
|
|
22
22
|
.toLowerCase();
|
|
23
23
|
return head.startsWith("<!doctype") || head.startsWith("<html");
|
|
24
24
|
}
|
|
25
|
-
async function tryFetchViaJina(context, url, maxChars, signal) {
|
|
25
|
+
async function tryFetchViaJina(context, url, maxChars, maxResponseBytes, signal) {
|
|
26
26
|
const client = createWebHttpClient({
|
|
27
27
|
webConfig: context.webConfig,
|
|
28
28
|
securityConfig: context.securityConfig,
|
|
@@ -37,6 +37,7 @@ async function tryFetchViaJina(context, url, maxChars, signal) {
|
|
|
37
37
|
url: `https://r.jina.ai/${url}`,
|
|
38
38
|
headers,
|
|
39
39
|
timeoutMs: context.webConfig.fetch.timeoutMs,
|
|
40
|
+
maxResponseBytes,
|
|
40
41
|
signal,
|
|
41
42
|
});
|
|
42
43
|
if (response.status < 200 || response.status >= 300 || !data.data?.content) {
|
|
@@ -59,7 +60,7 @@ async function tryFetchViaJina(context, url, maxChars, signal) {
|
|
|
59
60
|
},
|
|
60
61
|
};
|
|
61
62
|
}
|
|
62
|
-
async function fetchDirect(context, url, extractMode, maxChars, maxImageBytes, signal) {
|
|
63
|
+
async function fetchDirect(context, url, extractMode, maxChars, maxImageBytes, maxResponseBytes, signal) {
|
|
63
64
|
const client = createWebHttpClient({
|
|
64
65
|
webConfig: context.webConfig,
|
|
65
66
|
securityConfig: context.securityConfig,
|
|
@@ -69,6 +70,7 @@ async function fetchDirect(context, url, extractMode, maxChars, maxImageBytes, s
|
|
|
69
70
|
const response = await client.request({
|
|
70
71
|
url,
|
|
71
72
|
timeoutMs: context.webConfig.fetch.timeoutMs,
|
|
73
|
+
maxResponseBytes,
|
|
72
74
|
signal,
|
|
73
75
|
});
|
|
74
76
|
if (response.status < 200 || response.status >= 300) {
|
|
@@ -127,19 +129,19 @@ async function fetchDirect(context, url, extractMode, maxChars, maxImageBytes, s
|
|
|
127
129
|
}
|
|
128
130
|
export async function runWebFetch(context, request, signal) {
|
|
129
131
|
if (request.preferJina) {
|
|
130
|
-
const jinaResult = await tryFetchViaJina(context, request.url, request.maxChars, signal);
|
|
132
|
+
const jinaResult = await tryFetchViaJina(context, request.url, request.maxChars, request.maxResponseBytes, signal);
|
|
131
133
|
if (jinaResult) {
|
|
132
134
|
return jinaResult;
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
try {
|
|
136
|
-
return await fetchDirect(context, request.url, request.extractMode, request.maxChars, request.maxImageBytes, signal);
|
|
138
|
+
return await fetchDirect(context, request.url, request.extractMode, request.maxChars, request.maxImageBytes, request.maxResponseBytes, signal);
|
|
137
139
|
}
|
|
138
140
|
catch (error) {
|
|
139
141
|
if (!request.enableJinaFallback) {
|
|
140
142
|
throw error;
|
|
141
143
|
}
|
|
142
|
-
const jinaResult = await tryFetchViaJina(context, request.url, request.maxChars, signal);
|
|
144
|
+
const jinaResult = await tryFetchViaJina(context, request.url, request.maxChars, request.maxResponseBytes, signal);
|
|
143
145
|
if (jinaResult) {
|
|
144
146
|
return jinaResult;
|
|
145
147
|
}
|
|
@@ -19,6 +19,9 @@ function normalizeResult(item) {
|
|
|
19
19
|
snippet,
|
|
20
20
|
};
|
|
21
21
|
}
|
|
22
|
+
function isProviderConfigStatus(status) {
|
|
23
|
+
return status === 400 || status === 401 || status === 403 || status === 422 || status === 429;
|
|
24
|
+
}
|
|
22
25
|
class BraveSearchProvider {
|
|
23
26
|
constructor(context) {
|
|
24
27
|
this.context = context;
|
|
@@ -38,7 +41,7 @@ class BraveSearchProvider {
|
|
|
38
41
|
signal,
|
|
39
42
|
});
|
|
40
43
|
if (response.status < 200 || response.status >= 300) {
|
|
41
|
-
throw new WebSearchProviderError("provider", `Brave search failed with HTTP ${response.status}`);
|
|
44
|
+
throw new WebSearchProviderError(isProviderConfigStatus(response.status) ? "config" : "provider", `Brave search failed with HTTP ${response.status}`);
|
|
42
45
|
}
|
|
43
46
|
return (data.web?.results ?? [])
|
|
44
47
|
.map((item) => normalizeResult({
|
|
@@ -71,7 +74,7 @@ class TavilySearchProvider {
|
|
|
71
74
|
signal,
|
|
72
75
|
});
|
|
73
76
|
if (response.status < 200 || response.status >= 300) {
|
|
74
|
-
throw new WebSearchProviderError("provider", `Tavily search failed with HTTP ${response.status}`);
|
|
77
|
+
throw new WebSearchProviderError(isProviderConfigStatus(response.status) ? "config" : "provider", `Tavily search failed with HTTP ${response.status}`);
|
|
75
78
|
}
|
|
76
79
|
return (data.results ?? [])
|
|
77
80
|
.map((item) => normalizeResult({
|
|
@@ -101,7 +104,7 @@ class JinaSearchProvider {
|
|
|
101
104
|
signal,
|
|
102
105
|
});
|
|
103
106
|
if (response.status < 200 || response.status >= 300) {
|
|
104
|
-
throw new WebSearchProviderError("provider", `Jina search failed with HTTP ${response.status}`);
|
|
107
|
+
throw new WebSearchProviderError(isProviderConfigStatus(response.status) ? "config" : "provider", `Jina search failed with HTTP ${response.status}`);
|
|
105
108
|
}
|
|
106
109
|
return (data.data ?? [])
|
|
107
110
|
.map((item) => normalizeResult({
|
package/package.json
CHANGED