praxis-agent 0.26.0 → 0.28.0
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/application/session-memory.d.ts +43 -10
- package/dist/application/session-memory.js +263 -62
- package/dist/application/session-service.d.ts +13 -4
- package/dist/application/session-service.js +139 -36
- package/dist/application/subagent-service.d.ts +1 -0
- package/dist/application/subagent-service.js +23 -1
- package/dist/cli-runtime.d.ts +4 -0
- package/dist/cli-runtime.js +10 -0
- package/dist/core/runtime.d.ts +10 -0
- package/dist/core/runtime.js +111 -28
- package/dist/core/tool-execution-scheduler.d.ts +38 -0
- package/dist/core/tool-execution-scheduler.js +95 -0
- package/dist/core/tool-scheduling-policy.d.ts +4 -0
- package/dist/core/tool-scheduling-policy.js +24 -0
- package/dist/extensions/claude-extension-tools.d.ts +1 -0
- package/dist/extensions/claude-extension-tools.js +6 -0
- package/dist/hooks/claude-hook-tools.d.ts +4 -0
- package/dist/hooks/claude-hook-tools.js +6 -0
- package/dist/mcp/claude-mcp-tools.d.ts +1 -0
- package/dist/mcp/claude-mcp-tools.js +42 -0
- package/dist/permissions/claude-permission-resolver.js +3 -3
- package/dist/permissions/permission-updates.d.ts +1 -0
- package/dist/permissions/permission-updates.js +13 -1
- package/dist/tools/claude-capabilities.d.ts +1 -0
- package/dist/tools/claude-capabilities.js +5 -0
- package/dist/tools/claude-interactive-tools.js +7 -0
- package/dist/tools/claude-lsp-tool.js +7 -0
- package/dist/tools/claude-scheduled-tools.d.ts +1 -0
- package/dist/tools/claude-scheduled-tools.js +7 -0
- package/dist/tools/claude-task-tools.d.ts +1 -0
- package/dist/tools/claude-task-tools.js +11 -0
- package/dist/tools/claude-user-message.d.ts +1 -0
- package/dist/tools/claude-user-message.js +7 -0
- package/dist/tools/claude-workflow-tools.d.ts +1 -0
- package/dist/tools/claude-workflow-tools.js +7 -0
- package/dist/tools/claude-worktree-tools.d.ts +1 -0
- package/dist/tools/claude-worktree-tools.js +7 -0
- package/dist/tools/filtered-tool-registry.d.ts +1 -0
- package/dist/tools/filtered-tool-registry.js +5 -0
- package/dist/tools/local-tools.d.ts +17 -0
- package/dist/tools/local-tools.js +86 -1
- package/dist/tools/web.d.ts +1 -0
- package/dist/tools/web.js +27 -0
- package/package.json +1 -1
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { mkdir, readFile, realpath } from 'node:fs/promises';
|
|
2
2
|
import { basename, dirname, join, resolve } from 'node:path';
|
|
3
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
3
4
|
const ASK_USER_QUESTION = {
|
|
4
5
|
name: 'AskUserQuestion',
|
|
5
6
|
description: "Use this tool only when you are blocked on a decision that is genuinely the user's to make: one you cannot resolve from the request, the code, or sensible defaults. Users can always provide custom text. Use multiSelect for non-exclusive choices. In plan mode, use this tool to clarify requirements before ExitPlanMode; do not use it to request plan approval.",
|
|
@@ -197,6 +198,12 @@ class ClaudeInteractiveToolRegistry {
|
|
|
197
198
|
...INTERACTIVE_DEFINITIONS.filter(({ name }) => this.manager.enabledNames.has(name)),
|
|
198
199
|
];
|
|
199
200
|
}
|
|
201
|
+
schedulingPolicy(call) {
|
|
202
|
+
if (this.manager.enabledNames.has(call.name)) {
|
|
203
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
204
|
+
}
|
|
205
|
+
return resolveToolSchedulingPolicy(this.base, call);
|
|
206
|
+
}
|
|
200
207
|
async prepare(call, context) {
|
|
201
208
|
return this.manager.enabledNames.has(call.name)
|
|
202
209
|
? call
|
|
@@ -4,6 +4,7 @@ import { homedir } from 'node:os';
|
|
|
4
4
|
import { extname, resolve, sep } from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
6
|
import { promisify } from 'node:util';
|
|
7
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
7
8
|
import { redactSensitiveText, sanitizeChildEnvironment, sensitiveEnvironmentValues, } from '../platform/sensitive-data.js';
|
|
8
9
|
import { formatClaudeLspResult } from './claude-lsp-formatters.js';
|
|
9
10
|
const MAX_MESSAGE_BYTES = 8 * 1024 * 1024;
|
|
@@ -730,6 +731,12 @@ class ClaudeLspToolRegistry {
|
|
|
730
731
|
definitions() {
|
|
731
732
|
return [...this.base.definitions(), LSP_DEFINITION];
|
|
732
733
|
}
|
|
734
|
+
schedulingPolicy(call) {
|
|
735
|
+
if (call.name === 'LSP') {
|
|
736
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
737
|
+
}
|
|
738
|
+
return resolveToolSchedulingPolicy(this.base, call);
|
|
739
|
+
}
|
|
733
740
|
prepare(call, context) {
|
|
734
741
|
return call.name === 'LSP'
|
|
735
742
|
? Promise.resolve(call)
|
|
@@ -16,6 +16,7 @@ export declare class ClaudeScheduledToolRegistry implements ToolRegistry {
|
|
|
16
16
|
private readonly now;
|
|
17
17
|
constructor(options: ClaudeScheduledToolRegistryOptions);
|
|
18
18
|
definitions(): readonly ModelToolDefinition[];
|
|
19
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
19
20
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
20
21
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
21
22
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
1
2
|
const SCHEMA = 'https://json-schema.org/draft/2020-12/schema';
|
|
2
3
|
const CRON_CREATE_DESCRIPTION = `Schedule a prompt to be enqueued at a future time. Use for both recurring schedules and one-shot reminders.
|
|
3
4
|
|
|
@@ -191,6 +192,12 @@ export class ClaudeScheduledToolRegistry {
|
|
|
191
192
|
...scheduledDefinitions.filter(({ name }) => (this.enabled?.has(name) ?? true) && !existing.has(name)),
|
|
192
193
|
];
|
|
193
194
|
}
|
|
195
|
+
schedulingPolicy(call) {
|
|
196
|
+
if (DEFINITION_NAMES.has(call.name)) {
|
|
197
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
198
|
+
}
|
|
199
|
+
return resolveToolSchedulingPolicy(this.options.base, call);
|
|
200
|
+
}
|
|
194
201
|
async prepare(call, context) {
|
|
195
202
|
if (!DEFINITION_NAMES.has(call.name)) {
|
|
196
203
|
return this.options.base.prepare(call, context);
|
|
@@ -22,6 +22,7 @@ export declare class ClaudeTaskToolRegistry implements ToolRegistry {
|
|
|
22
22
|
stopBackgroundTask(taskId: string): Promise<BackgroundBashToolResult>;
|
|
23
23
|
isEnabled(name: string): boolean;
|
|
24
24
|
definitions(): readonly ModelToolDefinition[];
|
|
25
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
25
26
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
26
27
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
27
28
|
notifications(waitForRunning: boolean): Promise<string[]>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { resolve } from 'node:path';
|
|
2
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
2
3
|
import { BackgroundBashManager, } from '../application/background-bash-manager.js';
|
|
3
4
|
import { isBackgroundBashTaskId } from '../application/background-task-id.js';
|
|
4
5
|
import { ClaudeTaskStore, } from '../persistence/claude-task-store.js';
|
|
@@ -260,6 +261,16 @@ export class ClaudeTaskToolRegistry {
|
|
|
260
261
|
...TASK_DEFINITIONS.filter(({ name }) => this.isEnabled(name) && !existing.has(name)),
|
|
261
262
|
];
|
|
262
263
|
}
|
|
264
|
+
schedulingPolicy(call) {
|
|
265
|
+
if (call.name !== 'Bash' &&
|
|
266
|
+
!TASK_DEFINITIONS.some(({ name }) => name === call.name)) {
|
|
267
|
+
return resolveToolSchedulingPolicy(this.options.base, call);
|
|
268
|
+
}
|
|
269
|
+
if (call.name === 'Bash' && call.input.run_in_background !== true) {
|
|
270
|
+
return resolveToolSchedulingPolicy(this.options.base, call);
|
|
271
|
+
}
|
|
272
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
273
|
+
}
|
|
263
274
|
async prepare(call, context) {
|
|
264
275
|
if (call.name === 'Bash') {
|
|
265
276
|
return this.prepareBash(call, context);
|
|
@@ -10,6 +10,7 @@ export declare class ClaudeUserMessageToolRegistry implements ToolRegistry {
|
|
|
10
10
|
private readonly onMessage;
|
|
11
11
|
constructor(base: ToolRegistry, onMessage: (message: UserMessage) => void);
|
|
12
12
|
definitions(): readonly ModelToolDefinition[];
|
|
13
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
13
14
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
14
15
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
15
16
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { stat } from 'node:fs/promises';
|
|
2
2
|
import { extname, resolve } from 'node:path';
|
|
3
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
3
4
|
export const CLAUDE_USER_MESSAGE_PROMPT = 'When brief mode is enabled, SendUserMessage is the primary user-visible reply channel. Use it for the answer, progress checkpoints, and blockers. Set status to normal for a direct reply and proactive for an unsolicited update.';
|
|
4
5
|
const DEFINITION = {
|
|
5
6
|
name: 'SendUserMessage',
|
|
@@ -42,6 +43,12 @@ export class ClaudeUserMessageToolRegistry {
|
|
|
42
43
|
definitions() {
|
|
43
44
|
return [...this.base.definitions(), DEFINITION];
|
|
44
45
|
}
|
|
46
|
+
schedulingPolicy(call) {
|
|
47
|
+
if (call.name === DEFINITION.name) {
|
|
48
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
49
|
+
}
|
|
50
|
+
return resolveToolSchedulingPolicy(this.base, call);
|
|
51
|
+
}
|
|
45
52
|
prepare(call, context) {
|
|
46
53
|
if (call.name !== DEFINITION.name)
|
|
47
54
|
return this.base.prepare(call, context);
|
|
@@ -21,6 +21,7 @@ export declare class ClaudeWorkflowToolRegistry implements ToolRegistry {
|
|
|
21
21
|
private readonly prepared;
|
|
22
22
|
constructor(options: ClaudeWorkflowToolRegistryOptions);
|
|
23
23
|
definitions(): readonly ModelToolDefinition[];
|
|
24
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
24
25
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
25
26
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
26
27
|
private resolveSource;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { isAbsolute, resolve } from 'node:path';
|
|
3
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
3
4
|
import { parseWorkflowScript, } from '../application/workflow-manager.js';
|
|
4
5
|
const MAX_SCRIPT_BYTES = 524_288;
|
|
5
6
|
function workflowDefinition(dataPlane) {
|
|
@@ -91,6 +92,12 @@ export class ClaudeWorkflowToolRegistry {
|
|
|
91
92
|
}
|
|
92
93
|
return [...base, workflowDefinition(this.options.dataPlane ?? 'claude')];
|
|
93
94
|
}
|
|
95
|
+
schedulingPolicy(call) {
|
|
96
|
+
if (['Workflow', 'TaskOutput', 'TaskStop'].includes(call.name)) {
|
|
97
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
98
|
+
}
|
|
99
|
+
return resolveToolSchedulingPolicy(this.options.base, call);
|
|
100
|
+
}
|
|
94
101
|
async prepare(call, context) {
|
|
95
102
|
if (call.name === 'TaskOutput' || call.name === 'TaskStop') {
|
|
96
103
|
const taskId = call.input.task_id ?? call.input.shell_id;
|
|
@@ -12,6 +12,7 @@ export declare class ClaudeWorktreeToolRegistry implements ToolRegistry {
|
|
|
12
12
|
dataPlane?: DataPlane;
|
|
13
13
|
});
|
|
14
14
|
definitions(): readonly ModelToolDefinition[];
|
|
15
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
15
16
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
16
17
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
17
18
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
1
2
|
const ENTER_DEFINITION = {
|
|
2
3
|
name: 'EnterWorktree',
|
|
3
4
|
description: 'Create or enter an isolated Git worktree and switch the current session into it.',
|
|
@@ -96,6 +97,12 @@ export class ClaudeWorktreeToolRegistry {
|
|
|
96
97
|
...definitions.filter((definition) => this.enabled.has(definition.name) && !existing.has(definition.name)),
|
|
97
98
|
];
|
|
98
99
|
}
|
|
100
|
+
schedulingPolicy(call) {
|
|
101
|
+
if (['EnterWorktree', 'ExitWorktree'].includes(call.name)) {
|
|
102
|
+
return { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
103
|
+
}
|
|
104
|
+
return resolveToolSchedulingPolicy(this.options.base, call);
|
|
105
|
+
}
|
|
99
106
|
async prepare(call, context) {
|
|
100
107
|
if (call.name === 'EnterWorktree' && this.enabled.has(call.name)) {
|
|
101
108
|
const input = objectInput(call);
|
|
@@ -9,6 +9,7 @@ export declare class FilteredToolRegistry implements ToolRegistry {
|
|
|
9
9
|
private readonly enabledNames;
|
|
10
10
|
constructor(base: ToolRegistry, options?: FilteredToolRegistryOptions);
|
|
11
11
|
definitions(): readonly ModelToolDefinition[];
|
|
12
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
12
13
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
13
14
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
14
15
|
private assertEnabled;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
1
2
|
function exactToolNames(rules) {
|
|
2
3
|
return new Set(rules.flatMap((rule) => {
|
|
3
4
|
const match = /^([A-Za-z][\w-]*)$/.exec(rule);
|
|
@@ -34,6 +35,10 @@ export class FilteredToolRegistry {
|
|
|
34
35
|
return definition;
|
|
35
36
|
});
|
|
36
37
|
}
|
|
38
|
+
schedulingPolicy(call) {
|
|
39
|
+
this.assertEnabled(call.name);
|
|
40
|
+
return resolveToolSchedulingPolicy(this.base, call);
|
|
41
|
+
}
|
|
37
42
|
async prepare(call, context) {
|
|
38
43
|
this.assertEnabled(call.name);
|
|
39
44
|
return this.base.prepare(call, context);
|
|
@@ -53,6 +53,23 @@ export declare class LocalToolRegistry implements ToolRegistry {
|
|
|
53
53
|
private assertProtectedBashCommand;
|
|
54
54
|
private currentCwd;
|
|
55
55
|
definitions(): readonly ModelToolDefinition[];
|
|
56
|
+
schedulingPolicy(call: ModelToolCall): {
|
|
57
|
+
concurrency: "concurrent";
|
|
58
|
+
cancelOnInterrupt: boolean;
|
|
59
|
+
abortGroupOnError?: never;
|
|
60
|
+
} | {
|
|
61
|
+
concurrency: "concurrent";
|
|
62
|
+
cancelOnInterrupt: boolean;
|
|
63
|
+
abortGroupOnError: boolean;
|
|
64
|
+
} | {
|
|
65
|
+
abortGroupOnError?: boolean;
|
|
66
|
+
concurrency: "exclusive";
|
|
67
|
+
cancelOnInterrupt: boolean;
|
|
68
|
+
} | {
|
|
69
|
+
concurrency: "exclusive";
|
|
70
|
+
cancelOnInterrupt?: never;
|
|
71
|
+
abortGroupOnError?: never;
|
|
72
|
+
};
|
|
56
73
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
57
74
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
58
75
|
private workspacePath;
|
|
@@ -11,7 +11,7 @@ import { editNotebook, formatNotebookForRead } from './notebook.js';
|
|
|
11
11
|
import { openPdf } from './pdf.js';
|
|
12
12
|
import { validateBashPathSafety } from '../permissions/bash-path-safety.js';
|
|
13
13
|
import { protectedWritePathReason } from '../permissions/bypass-immune-paths.js';
|
|
14
|
-
import { effectiveAdditionalDirectories } from '../permissions/permission-updates.js';
|
|
14
|
+
import { effectiveAdditionalDirectories, shellInputIsReadOnly, } from '../permissions/permission-updates.js';
|
|
15
15
|
const REPORT_FINDINGS_DEFINITION = {
|
|
16
16
|
name: 'ReportFindings',
|
|
17
17
|
description: "Report code-review findings as a typed list so the host UI can render them. Use this only when the active code-review instructions tell you to report findings with this tool; otherwise follow whatever output format those instructions specify. When reporting a review's results, call it once with the verified findings ranked most-severe first (empty array if nothing survived verification) and do not also print the findings as text. When re-reporting after applying fixes (only if the apply instructions ask for it), set outcome on each finding to what actually happened.",
|
|
@@ -277,6 +277,64 @@ function optionalNonNegativeInteger(input, name) {
|
|
|
277
277
|
function isRecord(value) {
|
|
278
278
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
279
279
|
}
|
|
280
|
+
function hasOnlyInputKeys(input, allowed) {
|
|
281
|
+
const names = new Set(allowed);
|
|
282
|
+
return Object.keys(input).every((name) => names.has(name));
|
|
283
|
+
}
|
|
284
|
+
function localSchedulingInputIsValid(call) {
|
|
285
|
+
try {
|
|
286
|
+
if (call.name === 'Read') {
|
|
287
|
+
stringInput(call.input, 'file_path');
|
|
288
|
+
optionalNonNegativeInteger(call.input, 'offset');
|
|
289
|
+
optionalPositiveInteger(call.input, 'limit');
|
|
290
|
+
optionalString(call.input, 'pages');
|
|
291
|
+
return hasOnlyInputKeys(call.input, [
|
|
292
|
+
'file_path',
|
|
293
|
+
'offset',
|
|
294
|
+
'limit',
|
|
295
|
+
'pages',
|
|
296
|
+
]);
|
|
297
|
+
}
|
|
298
|
+
if (call.name === 'Glob') {
|
|
299
|
+
stringInput(call.input, 'pattern', true);
|
|
300
|
+
optionalString(call.input, 'path');
|
|
301
|
+
return hasOnlyInputKeys(call.input, ['pattern', 'path']);
|
|
302
|
+
}
|
|
303
|
+
if (call.name === 'Grep') {
|
|
304
|
+
stringInput(call.input, 'pattern');
|
|
305
|
+
optionalString(call.input, 'path');
|
|
306
|
+
optionalString(call.input, 'glob');
|
|
307
|
+
return hasOnlyInputKeys(call.input, ['pattern', 'path', 'glob']);
|
|
308
|
+
}
|
|
309
|
+
if (call.name === 'Bash') {
|
|
310
|
+
stringInput(call.input, 'command');
|
|
311
|
+
optionalPositiveInteger(call.input, 'timeout');
|
|
312
|
+
const description = call.input.description;
|
|
313
|
+
const background = call.input.run_in_background;
|
|
314
|
+
const disableSandbox = call.input.dangerouslyDisableSandbox;
|
|
315
|
+
if (description !== undefined && typeof description !== 'string') {
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
if (background !== undefined && typeof background !== 'boolean') {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
if (disableSandbox !== undefined && typeof disableSandbox !== 'boolean') {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
return hasOnlyInputKeys(call.input, [
|
|
325
|
+
'command',
|
|
326
|
+
'timeout',
|
|
327
|
+
'description',
|
|
328
|
+
'run_in_background',
|
|
329
|
+
'dangerouslyDisableSandbox',
|
|
330
|
+
]);
|
|
331
|
+
}
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
280
338
|
const REPORT_LEVELS = ['low', 'medium', 'high', 'xhigh', 'max'];
|
|
281
339
|
const REPORT_VERDICTS = ['CONFIRMED', 'PLAUSIBLE'];
|
|
282
340
|
const REPORT_OUTCOMES = ['fixed', 'skipped', 'no_change_needed'];
|
|
@@ -535,6 +593,33 @@ export class LocalToolRegistry {
|
|
|
535
593
|
}
|
|
536
594
|
: definition);
|
|
537
595
|
}
|
|
596
|
+
schedulingPolicy(call) {
|
|
597
|
+
if (['Read', 'Glob', 'Grep'].includes(call.name) &&
|
|
598
|
+
localSchedulingInputIsValid(call)) {
|
|
599
|
+
return { concurrency: 'concurrent', cancelOnInterrupt: true };
|
|
600
|
+
}
|
|
601
|
+
if (call.name === 'Bash' &&
|
|
602
|
+
localSchedulingInputIsValid(call) &&
|
|
603
|
+
call.input.run_in_background !== true &&
|
|
604
|
+
typeof call.input.command === 'string' &&
|
|
605
|
+
shellInputIsReadOnly(call.input.command)) {
|
|
606
|
+
return {
|
|
607
|
+
concurrency: 'concurrent',
|
|
608
|
+
cancelOnInterrupt: true,
|
|
609
|
+
abortGroupOnError: true,
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
if (TOOL_DEFINITIONS.some(({ name }) => name === call.name)) {
|
|
613
|
+
return {
|
|
614
|
+
concurrency: 'exclusive',
|
|
615
|
+
cancelOnInterrupt: true,
|
|
616
|
+
...(call.name === 'Bash' ? { abortGroupOnError: true } : {}),
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
return {
|
|
620
|
+
concurrency: 'exclusive',
|
|
621
|
+
};
|
|
622
|
+
}
|
|
538
623
|
async prepare(call, context) {
|
|
539
624
|
if (context.signal?.aborted)
|
|
540
625
|
throw abortError();
|
package/dist/tools/web.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export declare class WebToolRegistry implements ToolRegistry {
|
|
|
36
36
|
private cacheBytes;
|
|
37
37
|
constructor(options: WebToolRegistryOptions);
|
|
38
38
|
definitions(): readonly ModelToolDefinition[];
|
|
39
|
+
schedulingPolicy(call: ModelToolCall): import("../core/runtime.js").ToolSchedulingPolicy;
|
|
39
40
|
prepare(call: ModelToolCall, context: ToolExecutionContext): Promise<ModelToolCall>;
|
|
40
41
|
execute(call: ModelToolCall, context: ToolExecutionContext): Promise<ToolExecutionResult>;
|
|
41
42
|
private webFetch;
|
package/dist/tools/web.js
CHANGED
|
@@ -6,6 +6,7 @@ import { isIP } from 'node:net';
|
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import ipaddr from 'ipaddr.js';
|
|
8
8
|
import TurndownService from 'turndown';
|
|
9
|
+
import { resolveToolSchedulingPolicy } from '../core/tool-scheduling-policy.js';
|
|
9
10
|
const MAX_URL_LENGTH = 2_000;
|
|
10
11
|
const MAX_MARKDOWN_LENGTH = 100_000;
|
|
11
12
|
const DEFAULT_MAX_RESPONSE_BYTES = 10 * 1024 * 1024;
|
|
@@ -273,6 +274,24 @@ function parseWebSearchInput(input) {
|
|
|
273
274
|
...(blockedDomains ? { blockedDomains } : {}),
|
|
274
275
|
};
|
|
275
276
|
}
|
|
277
|
+
function webSchedulingInputIsValid(call) {
|
|
278
|
+
try {
|
|
279
|
+
if (call.name === 'WebFetch') {
|
|
280
|
+
parseWebFetchInput(call.input);
|
|
281
|
+
return Object.keys(call.input).every((name) => name === 'url' || name === 'prompt');
|
|
282
|
+
}
|
|
283
|
+
if (call.name === 'WebSearch') {
|
|
284
|
+
parseWebSearchInput(call.input);
|
|
285
|
+
return Object.keys(call.input).every((name) => name === 'query' ||
|
|
286
|
+
name === 'allowed_domains' ||
|
|
287
|
+
name === 'blocked_domains');
|
|
288
|
+
}
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
276
295
|
function normalizedAddress(value) {
|
|
277
296
|
const parsed = ipaddr.parse(value);
|
|
278
297
|
return parsed instanceof ipaddr.IPv6 && parsed.isIPv4MappedAddress()
|
|
@@ -475,6 +494,14 @@ export class WebToolRegistry {
|
|
|
475
494
|
: []),
|
|
476
495
|
];
|
|
477
496
|
}
|
|
497
|
+
schedulingPolicy(call) {
|
|
498
|
+
if (call.name === 'WebFetch' || call.name === 'WebSearch') {
|
|
499
|
+
return webSchedulingInputIsValid(call)
|
|
500
|
+
? { concurrency: 'concurrent', cancelOnInterrupt: true }
|
|
501
|
+
: { concurrency: 'exclusive', cancelOnInterrupt: true };
|
|
502
|
+
}
|
|
503
|
+
return resolveToolSchedulingPolicy(this.options.base, call);
|
|
504
|
+
}
|
|
478
505
|
async prepare(call, context) {
|
|
479
506
|
if (context.signal?.aborted)
|
|
480
507
|
throw abortError();
|