anyclaude-sdk 0.1.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/LICENSE +21 -0
- package/README.md +295 -0
- package/dist/agent.d.ts +110 -0
- package/dist/agent.js +897 -0
- package/dist/background/index.d.ts +3 -0
- package/dist/background/index.js +9 -0
- package/dist/background/manager.d.ts +32 -0
- package/dist/background/manager.js +108 -0
- package/dist/background/tools.d.ts +5 -0
- package/dist/background/tools.js +98 -0
- package/dist/background/worker.d.ts +19 -0
- package/dist/background/worker.js +30 -0
- package/dist/commands/builtins.d.ts +2 -0
- package/dist/commands/builtins.js +306 -0
- package/dist/commands/index.d.ts +21 -0
- package/dist/commands/index.js +56 -0
- package/dist/commands/types.d.ts +110 -0
- package/dist/commands/types.js +5 -0
- package/dist/compact.d.ts +22 -0
- package/dist/compact.js +67 -0
- package/dist/fs/dexie.d.ts +57 -0
- package/dist/fs/dexie.js +243 -0
- package/dist/fs/index.d.ts +4 -0
- package/dist/fs/index.js +13 -0
- package/dist/fs/linuxTree.d.ts +11 -0
- package/dist/fs/linuxTree.js +43 -0
- package/dist/fs/opfs.d.ts +23 -0
- package/dist/fs/opfs.js +112 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +29 -0
- package/dist/llm/anthropic.d.ts +24 -0
- package/dist/llm/anthropic.js +280 -0
- package/dist/llm/index.d.ts +3 -0
- package/dist/llm/index.js +3 -0
- package/dist/llm/inlineTools.d.ts +11 -0
- package/dist/llm/inlineTools.js +72 -0
- package/dist/llm/openai.d.ts +29 -0
- package/dist/llm/openai.js +224 -0
- package/dist/llm/responses.d.ts +18 -0
- package/dist/llm/responses.js +256 -0
- package/dist/mcp/client.d.ts +20 -0
- package/dist/mcp/client.js +156 -0
- package/dist/mcp/index.d.ts +24 -0
- package/dist/mcp/index.js +157 -0
- package/dist/mcp/proxy.d.ts +3 -0
- package/dist/mcp/proxy.js +25 -0
- package/dist/mcp/sdkServer.d.ts +21 -0
- package/dist/mcp/sdkServer.js +28 -0
- package/dist/mcp/types.d.ts +92 -0
- package/dist/mcp/types.js +5 -0
- package/dist/memory/index.d.ts +4 -0
- package/dist/memory/index.js +5 -0
- package/dist/memory/render.d.ts +7 -0
- package/dist/memory/render.js +46 -0
- package/dist/memory/store.d.ts +20 -0
- package/dist/memory/store.js +79 -0
- package/dist/memory/tools.d.ts +5 -0
- package/dist/memory/tools.js +95 -0
- package/dist/memory/types.d.ts +15 -0
- package/dist/memory/types.js +4 -0
- package/dist/permissions/dangerous.d.ts +4 -0
- package/dist/permissions/dangerous.js +24 -0
- package/dist/permissions/gate.d.ts +21 -0
- package/dist/permissions/gate.js +66 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.js +6 -0
- package/dist/permissions/match.d.ts +19 -0
- package/dist/permissions/match.js +104 -0
- package/dist/permissions/planMode.d.ts +3 -0
- package/dist/permissions/planMode.js +33 -0
- package/dist/permissions/types.d.ts +19 -0
- package/dist/permissions/types.js +2 -0
- package/dist/persist.d.ts +15 -0
- package/dist/persist.js +58 -0
- package/dist/prompt.d.ts +6 -0
- package/dist/prompt.js +34 -0
- package/dist/query.d.ts +105 -0
- package/dist/query.js +115 -0
- package/dist/queue.d.ts +23 -0
- package/dist/queue.js +43 -0
- package/dist/sandbox/cloudflare.d.ts +48 -0
- package/dist/sandbox/cloudflare.js +124 -0
- package/dist/sandbox/daytona.d.ts +48 -0
- package/dist/sandbox/daytona.js +79 -0
- package/dist/sandbox/e2b.d.ts +54 -0
- package/dist/sandbox/e2b.js +87 -0
- package/dist/sandbox/index.d.ts +8 -0
- package/dist/sandbox/index.js +19 -0
- package/dist/sandbox/local.d.ts +51 -0
- package/dist/sandbox/local.js +155 -0
- package/dist/sandbox/types.d.ts +18 -0
- package/dist/sandbox/types.js +27 -0
- package/dist/sandbox/util.d.ts +15 -0
- package/dist/sandbox/util.js +100 -0
- package/dist/sandbox/vercel.d.ts +48 -0
- package/dist/sandbox/vercel.js +130 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +6 -0
- package/dist/session/store.d.ts +28 -0
- package/dist/session/store.js +122 -0
- package/dist/session/types.d.ts +22 -0
- package/dist/session/types.js +2 -0
- package/dist/settings/index.d.ts +3 -0
- package/dist/settings/index.js +3 -0
- package/dist/settings/load.d.ts +20 -0
- package/dist/settings/load.js +36 -0
- package/dist/settings/merge.d.ts +13 -0
- package/dist/settings/merge.js +65 -0
- package/dist/settings/types.d.ts +17 -0
- package/dist/settings/types.js +3 -0
- package/dist/skills/index.d.ts +4 -0
- package/dist/skills/index.js +5 -0
- package/dist/skills/load.d.ts +23 -0
- package/dist/skills/load.js +54 -0
- package/dist/skills/parse.d.ts +7 -0
- package/dist/skills/parse.js +40 -0
- package/dist/skills/tool.d.ts +2 -0
- package/dist/skills/tool.js +39 -0
- package/dist/skills/types.d.ts +10 -0
- package/dist/skills/types.js +4 -0
- package/dist/team/dispatch.d.ts +2 -0
- package/dist/team/dispatch.js +41 -0
- package/dist/team/index.d.ts +9 -0
- package/dist/team/index.js +11 -0
- package/dist/team/mailbox.d.ts +24 -0
- package/dist/team/mailbox.js +33 -0
- package/dist/team/prompt.d.ts +1 -0
- package/dist/team/prompt.js +12 -0
- package/dist/team/runner.d.ts +20 -0
- package/dist/team/runner.js +45 -0
- package/dist/team/taskBoard.d.ts +41 -0
- package/dist/team/taskBoard.js +73 -0
- package/dist/team/tools.d.ts +7 -0
- package/dist/team/tools.js +190 -0
- package/dist/tools/bash.d.ts +2 -0
- package/dist/tools/bash.js +45 -0
- package/dist/tools/config.d.ts +2 -0
- package/dist/tools/config.js +44 -0
- package/dist/tools/define.d.ts +18 -0
- package/dist/tools/define.js +21 -0
- package/dist/tools/delete_file.d.ts +2 -0
- package/dist/tools/delete_file.js +33 -0
- package/dist/tools/edit_file.d.ts +2 -0
- package/dist/tools/edit_file.js +93 -0
- package/dist/tools/fileTypes.d.ts +32 -0
- package/dist/tools/fileTypes.js +166 -0
- package/dist/tools/glob.d.ts +2 -0
- package/dist/tools/glob.js +53 -0
- package/dist/tools/grep.d.ts +2 -0
- package/dist/tools/grep.js +110 -0
- package/dist/tools/imageProcessor.d.ts +15 -0
- package/dist/tools/imageProcessor.js +83 -0
- package/dist/tools/index.d.ts +28 -0
- package/dist/tools/index.js +45 -0
- package/dist/tools/list_files.d.ts +2 -0
- package/dist/tools/list_files.js +42 -0
- package/dist/tools/multi_edit.d.ts +2 -0
- package/dist/tools/multi_edit.js +112 -0
- package/dist/tools/notebook_edit.d.ts +2 -0
- package/dist/tools/notebook_edit.js +118 -0
- package/dist/tools/plan_mode.d.ts +4 -0
- package/dist/tools/plan_mode.js +44 -0
- package/dist/tools/read_file.d.ts +2 -0
- package/dist/tools/read_file.js +193 -0
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +77 -0
- package/dist/tools/todo_write.d.ts +2 -0
- package/dist/tools/todo_write.js +104 -0
- package/dist/tools/tool_search.d.ts +2 -0
- package/dist/tools/tool_search.js +49 -0
- package/dist/tools/types.d.ts +82 -0
- package/dist/tools/types.js +1 -0
- package/dist/tools/walk.d.ts +29 -0
- package/dist/tools/walk.js +82 -0
- package/dist/tools/web_fetch.d.ts +2 -0
- package/dist/tools/web_fetch.js +76 -0
- package/dist/tools/web_search.d.ts +22 -0
- package/dist/tools/web_search.js +195 -0
- package/dist/tools/write_file.d.ts +2 -0
- package/dist/tools/write_file.js +39 -0
- package/dist/types/index.d.ts +363 -0
- package/dist/types/index.js +9 -0
- package/dist/util/ids.d.ts +3 -0
- package/dist/util/ids.js +22 -0
- package/dist/util/paths.d.ts +16 -0
- package/dist/util/paths.js +72 -0
- package/dist/util/pricing.d.ts +15 -0
- package/dist/util/pricing.js +81 -0
- package/dist/workspace/index.d.ts +2 -0
- package/dist/workspace/index.js +2 -0
- package/dist/workspace/memory.d.ts +28 -0
- package/dist/workspace/memory.js +97 -0
- package/dist/workspace/webcontainer.d.ts +65 -0
- package/dist/workspace/webcontainer.js +156 -0
- package/package.json +78 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// Conservative detection of obviously-destructive shell commands. Used as a
|
|
2
|
+
// last-resort backstop when no explicit allow rule matched.
|
|
3
|
+
const PATTERNS = [
|
|
4
|
+
{ re: /\brm\s+(-[a-z]*\s+)*-?[rf][rf]?\s+(\/|~|\/\*|\$HOME|\.\s*$|\*\s*$)/i, reason: 'recursive force-delete of a top-level/home path' },
|
|
5
|
+
{ re: /\brm\s+-[a-z]*r[a-z]*f|\brm\s+-[a-z]*f[a-z]*r/i, reason: 'rm -rf' },
|
|
6
|
+
{ re: /\bmkfs(\.\w+)?\b/i, reason: 'filesystem format (mkfs)' },
|
|
7
|
+
{ re: /\bdd\b[^\n]*\bof=\/dev\/[sh]d/i, reason: 'dd write to a raw disk device' },
|
|
8
|
+
{ re: />\s*\/dev\/[sh]d[a-z]/i, reason: 'redirect to a raw disk device' },
|
|
9
|
+
{ re: /:\s*\(\s*\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;\s*:/, reason: 'fork bomb' },
|
|
10
|
+
{ re: /\b(curl|wget)\b[^\n|]*\|\s*(sudo\s+)?(sh|bash|zsh)\b/i, reason: 'pipe remote script straight into a shell' },
|
|
11
|
+
{ re: /\bchmod\s+-R\s+777\s+\//i, reason: 'recursive chmod 777 on root' },
|
|
12
|
+
{ re: /\bgit\s+push\b[^\n]*--force[^\n]*\b(origin\s+)?(main|master)\b/i, reason: 'force-push to main/master' },
|
|
13
|
+
{ re: /\bgit\s+push\b[^\n]*\b(main|master)\b[^\n]*--force/i, reason: 'force-push to main/master' },
|
|
14
|
+
{ re: /\b(shutdown|reboot|halt|poweroff)\b/i, reason: 'system power command' },
|
|
15
|
+
{ re: /\bsudo\s+rm\b/i, reason: 'sudo rm' },
|
|
16
|
+
];
|
|
17
|
+
export function isDangerousBash(command) {
|
|
18
|
+
const cmd = (command || '').trim();
|
|
19
|
+
for (const { re, reason } of PATTERNS) {
|
|
20
|
+
if (re.test(cmd))
|
|
21
|
+
return { dangerous: true, reason };
|
|
22
|
+
}
|
|
23
|
+
return { dangerous: false };
|
|
24
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CanUseTool, PermissionMode } from '../types/index.js';
|
|
2
|
+
import type { PermissionRuleSet, PermissionUpdateInput } from './types.js';
|
|
3
|
+
export interface GateOptions {
|
|
4
|
+
/** Permission mode; 'bypassPermissions' allows all, 'plan' denies non-read-only tools. */
|
|
5
|
+
mode?: PermissionMode;
|
|
6
|
+
/** Interactive prompt for 'ask'/unmatched calls. true → allow, false → deny. */
|
|
7
|
+
onAsk?: (toolName: string, input: Record<string, unknown>) => Promise<boolean>;
|
|
8
|
+
/** Block obviously-destructive bash unless an explicit allow rule matched. Default true. */
|
|
9
|
+
flagDangerous?: boolean;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build a CanUseTool from a rule set.
|
|
13
|
+
*
|
|
14
|
+
* Resolution order: bypass → plan-mode read-only check → deny rule → allow rule
|
|
15
|
+
* → dangerous-bash backstop → ask (onAsk / mode). With no UI and mode 'default'
|
|
16
|
+
* an unmatched call is ALLOWED (documented); use 'dontAsk' to deny-by-default,
|
|
17
|
+
* or provide `onAsk`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function rulesToCanUseTool(ruleset: PermissionRuleSet, opts?: GateOptions): CanUseTool;
|
|
20
|
+
/** Apply a permission update to a rule set (pure; setMode is a no-op on rules). */
|
|
21
|
+
export declare function applyPermissionUpdate(ruleset: PermissionRuleSet, update: PermissionUpdateInput): PermissionRuleSet;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Turn a rule set into a CanUseTool gate, and apply permission updates.
|
|
2
|
+
import { canonical, evaluate } from './match.js';
|
|
3
|
+
import { isDangerousBash } from './dangerous.js';
|
|
4
|
+
import { isReadOnlyTool } from './planMode.js';
|
|
5
|
+
/**
|
|
6
|
+
* Build a CanUseTool from a rule set.
|
|
7
|
+
*
|
|
8
|
+
* Resolution order: bypass → plan-mode read-only check → deny rule → allow rule
|
|
9
|
+
* → dangerous-bash backstop → ask (onAsk / mode). With no UI and mode 'default'
|
|
10
|
+
* an unmatched call is ALLOWED (documented); use 'dontAsk' to deny-by-default,
|
|
11
|
+
* or provide `onAsk`.
|
|
12
|
+
*/
|
|
13
|
+
export function rulesToCanUseTool(ruleset, opts = {}) {
|
|
14
|
+
const flagDangerous = opts.flagDangerous !== false;
|
|
15
|
+
return async (toolName, input) => {
|
|
16
|
+
const behavior = evaluate(ruleset, toolName, input);
|
|
17
|
+
// Explicit deny is a hard block — it wins even in bypassPermissions mode.
|
|
18
|
+
if (behavior === 'deny') {
|
|
19
|
+
return { behavior: 'deny', message: `Denied by permission rule for "${toolName}".` };
|
|
20
|
+
}
|
|
21
|
+
if (opts.mode === 'bypassPermissions')
|
|
22
|
+
return { behavior: 'allow' };
|
|
23
|
+
// Plan mode: only read-only tools may run.
|
|
24
|
+
if (opts.mode === 'plan' && !isReadOnlyTool(toolName, input)) {
|
|
25
|
+
return { behavior: 'deny', message: `Plan mode: "${toolName}" is not a read-only tool; planning only.` };
|
|
26
|
+
}
|
|
27
|
+
if (behavior === 'allow')
|
|
28
|
+
return { behavior: 'allow' };
|
|
29
|
+
// Dangerous-bash backstop (only when not explicitly allowed above).
|
|
30
|
+
if (flagDangerous && canonical(toolName) === 'bash') {
|
|
31
|
+
const d = isDangerousBash(String(input.command ?? ''));
|
|
32
|
+
if (d.dangerous) {
|
|
33
|
+
return { behavior: 'deny', message: `Blocked potentially destructive command (${d.reason}). Add an allow rule to override.` };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// 'ask' rule or no match.
|
|
37
|
+
if (opts.onAsk) {
|
|
38
|
+
const ok = await opts.onAsk(toolName, input);
|
|
39
|
+
return ok ? { behavior: 'allow' } : { behavior: 'deny', message: 'Denied by user.' };
|
|
40
|
+
}
|
|
41
|
+
if (opts.mode === 'dontAsk') {
|
|
42
|
+
return { behavior: 'deny', message: `No matching allow rule for "${toolName}" (dontAsk mode).` };
|
|
43
|
+
}
|
|
44
|
+
return { behavior: 'allow' }; // default: no UI available → allow
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** Apply a permission update to a rule set (pure; setMode is a no-op on rules). */
|
|
48
|
+
export function applyPermissionUpdate(ruleset, update) {
|
|
49
|
+
const next = {
|
|
50
|
+
allow: [...ruleset.allow],
|
|
51
|
+
deny: [...ruleset.deny],
|
|
52
|
+
ask: [...ruleset.ask],
|
|
53
|
+
};
|
|
54
|
+
const beh = update.behavior;
|
|
55
|
+
const rules = update.rules ?? [];
|
|
56
|
+
if (!beh)
|
|
57
|
+
return next;
|
|
58
|
+
if (update.type === 'addRules')
|
|
59
|
+
next[beh] = [...next[beh], ...rules];
|
|
60
|
+
else if (update.type === 'replaceRules')
|
|
61
|
+
next[beh] = [...rules];
|
|
62
|
+
else if (update.type === 'removeRules') {
|
|
63
|
+
next[beh] = next[beh].filter((r) => !rules.some((x) => x.toolName === r.toolName && x.ruleContent === r.ruleContent));
|
|
64
|
+
}
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export type { PermissionBehavior, PermissionRule, PermissionRuleSet, PermissionUpdateInput, } from './types.js';
|
|
2
|
+
export { canonical, parseRule, ruleContentForInput, matchContent, matchRule, evaluate, ruleSetFromStrings, } from './match.js';
|
|
3
|
+
export { isDangerousBash } from './dangerous.js';
|
|
4
|
+
export { READ_ONLY_TOOLS, isReadOnlyTool } from './planMode.js';
|
|
5
|
+
export { rulesToCanUseTool, applyPermissionUpdate, type GateOptions } from './gate.js';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Permission rule-matching system: allow/deny/ask rules, dangerous-command
|
|
2
|
+
// detection, and plan-mode tool classification.
|
|
3
|
+
export { canonical, parseRule, ruleContentForInput, matchContent, matchRule, evaluate, ruleSetFromStrings, } from './match.js';
|
|
4
|
+
export { isDangerousBash } from './dangerous.js';
|
|
5
|
+
export { READ_ONLY_TOOLS, isReadOnlyTool } from './planMode.js';
|
|
6
|
+
export { rulesToCanUseTool, applyPermissionUpdate } from './gate.js';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { PermissionBehavior, PermissionRule, PermissionRuleSet } from './types.js';
|
|
2
|
+
/** Canonicalize a tool/rule name so PascalCase rules match snake_case tools. */
|
|
3
|
+
export declare function canonical(name: string): string;
|
|
4
|
+
/** Parse a rule string like `Bash(rm *)` or `write_file` into a PermissionRule. */
|
|
5
|
+
export declare function parseRule(s: string): PermissionRule;
|
|
6
|
+
/** The string a rule's content matches against, by tool. */
|
|
7
|
+
export declare function ruleContentForInput(toolName: string, input: Record<string, unknown>): string | undefined;
|
|
8
|
+
/** Glob/prefix match for rule content (`*`/`**` = any; bare strings prefix-match commands). */
|
|
9
|
+
export declare function matchContent(pattern: string | undefined, value: string | undefined): boolean;
|
|
10
|
+
/** Does a rule match this tool call? */
|
|
11
|
+
export declare function matchRule(rule: PermissionRule, toolName: string, input: Record<string, unknown>): boolean;
|
|
12
|
+
/** Evaluate a ruleset: deny wins, then allow, then ask; undefined if no match. */
|
|
13
|
+
export declare function evaluate(ruleset: PermissionRuleSet, toolName: string, input: Record<string, unknown>): PermissionBehavior | undefined;
|
|
14
|
+
/** Build a PermissionRuleSet from string-rule arrays. */
|
|
15
|
+
export declare function ruleSetFromStrings(input: {
|
|
16
|
+
allow?: string[];
|
|
17
|
+
deny?: string[];
|
|
18
|
+
ask?: string[];
|
|
19
|
+
}): PermissionRuleSet;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// Rule parsing + matching. Rules use names like "Bash(rm *)" or "write_file";
|
|
2
|
+
// we canonicalize tool names (Claude Code PascalCase ↔ our snake_case) and
|
|
3
|
+
// glob/prefix-match the rule content against the relevant input field.
|
|
4
|
+
// Map common rule/tool name variants → our canonical snake_case tool name.
|
|
5
|
+
const ALIAS = {
|
|
6
|
+
bash: 'bash', shell: 'bash', sh: 'bash', powershell: 'bash',
|
|
7
|
+
edit: 'edit_file', editfile: 'edit_file',
|
|
8
|
+
multiedit: 'multi_edit',
|
|
9
|
+
write: 'write_file', writefile: 'write_file',
|
|
10
|
+
read: 'read_file', readfile: 'read_file',
|
|
11
|
+
glob: 'glob', grep: 'grep',
|
|
12
|
+
ls: 'list_files', list: 'list_files', listfiles: 'list_files',
|
|
13
|
+
notebookedit: 'notebook_edit',
|
|
14
|
+
todowrite: 'todo_write', todo: 'todo_write',
|
|
15
|
+
webfetch: 'web_fetch', fetch: 'web_fetch',
|
|
16
|
+
websearch: 'web_search', search: 'web_search',
|
|
17
|
+
toolsearch: 'tool_search',
|
|
18
|
+
config: 'config', task: 'task', agent: 'task',
|
|
19
|
+
delete: 'delete_file', deletefile: 'delete_file',
|
|
20
|
+
};
|
|
21
|
+
/** Canonicalize a tool/rule name so PascalCase rules match snake_case tools. */
|
|
22
|
+
export function canonical(name) {
|
|
23
|
+
const lower = name.toLowerCase().trim();
|
|
24
|
+
if (lower.startsWith('mcp__'))
|
|
25
|
+
return lower; // MCP tools match exactly
|
|
26
|
+
const key = lower.replace(/[\s_-]+/g, '');
|
|
27
|
+
return ALIAS[key] ?? lower.replace(/[\s-]+/g, '_');
|
|
28
|
+
}
|
|
29
|
+
/** Parse a rule string like `Bash(rm *)` or `write_file` into a PermissionRule. */
|
|
30
|
+
export function parseRule(s) {
|
|
31
|
+
const m = s.match(/^\s*([^()]+?)\s*\(([\s\S]*)\)\s*$/);
|
|
32
|
+
if (m)
|
|
33
|
+
return { toolName: m[1].trim(), ruleContent: m[2].trim() };
|
|
34
|
+
return { toolName: s.trim() };
|
|
35
|
+
}
|
|
36
|
+
/** The string a rule's content matches against, by tool. */
|
|
37
|
+
export function ruleContentForInput(toolName, input) {
|
|
38
|
+
const t = canonical(toolName);
|
|
39
|
+
if (t === 'bash')
|
|
40
|
+
return typeof input.command === 'string' ? input.command : undefined;
|
|
41
|
+
if (['read_file', 'write_file', 'edit_file', 'multi_edit', 'delete_file', 'notebook_edit', 'list_files', 'glob'].includes(t)) {
|
|
42
|
+
if (typeof input.path === 'string')
|
|
43
|
+
return input.path;
|
|
44
|
+
if (typeof input.pattern === 'string')
|
|
45
|
+
return input.pattern;
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
if (t === 'web_fetch' || t === 'web_search') {
|
|
49
|
+
if (typeof input.url === 'string')
|
|
50
|
+
return input.url;
|
|
51
|
+
if (typeof input.query === 'string')
|
|
52
|
+
return input.query;
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
function escapeRegex(s) {
|
|
58
|
+
// Escape every regex special INCLUDING '*' (so glob '*' becomes '\*' which we
|
|
59
|
+
// then turn into '.*' below — without this, '*'/'**' stay as invalid quantifiers).
|
|
60
|
+
return s.replace(/[.*+^${}()|[\]\\]/g, '\\$&');
|
|
61
|
+
}
|
|
62
|
+
/** Glob/prefix match for rule content (`*`/`**` = any; bare strings prefix-match commands). */
|
|
63
|
+
export function matchContent(pattern, value) {
|
|
64
|
+
if (!pattern)
|
|
65
|
+
return true; // no content constraint → matches any input
|
|
66
|
+
if (value == null)
|
|
67
|
+
return false;
|
|
68
|
+
if (pattern === '*' || pattern === '**')
|
|
69
|
+
return true;
|
|
70
|
+
const re = new RegExp('^' + escapeRegex(pattern).replace(/\\\*\\\*/g, '.*').replace(/\\\*/g, '.*') + '$');
|
|
71
|
+
if (re.test(value))
|
|
72
|
+
return true;
|
|
73
|
+
// Convenience: a glob-free pattern prefix-matches a command/path.
|
|
74
|
+
if (!pattern.includes('*') && (value === pattern || value.startsWith(pattern + ' ') || value.startsWith(pattern + '/'))) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
/** Does a rule match this tool call? */
|
|
80
|
+
export function matchRule(rule, toolName, input) {
|
|
81
|
+
if (canonical(rule.toolName) !== canonical(toolName))
|
|
82
|
+
return false;
|
|
83
|
+
if (!rule.ruleContent)
|
|
84
|
+
return true;
|
|
85
|
+
return matchContent(rule.ruleContent, ruleContentForInput(toolName, input));
|
|
86
|
+
}
|
|
87
|
+
/** Evaluate a ruleset: deny wins, then allow, then ask; undefined if no match. */
|
|
88
|
+
export function evaluate(ruleset, toolName, input) {
|
|
89
|
+
if (ruleset.deny.some((r) => matchRule(r, toolName, input)))
|
|
90
|
+
return 'deny';
|
|
91
|
+
if (ruleset.allow.some((r) => matchRule(r, toolName, input)))
|
|
92
|
+
return 'allow';
|
|
93
|
+
if (ruleset.ask.some((r) => matchRule(r, toolName, input)))
|
|
94
|
+
return 'ask';
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
/** Build a PermissionRuleSet from string-rule arrays. */
|
|
98
|
+
export function ruleSetFromStrings(input) {
|
|
99
|
+
return {
|
|
100
|
+
allow: (input.allow ?? []).map(parseRule),
|
|
101
|
+
deny: (input.deny ?? []).map(parseRule),
|
|
102
|
+
ask: (input.ask ?? []).map(parseRule),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Plan-mode tool classification: in 'plan' mode only read-only tools run.
|
|
2
|
+
import { canonical } from './match.js';
|
|
3
|
+
export const READ_ONLY_TOOLS = new Set([
|
|
4
|
+
'read_file', 'glob', 'grep', 'list_files',
|
|
5
|
+
'web_fetch', 'web_search', 'tool_search',
|
|
6
|
+
'task_get', 'board_list', 'memory_list',
|
|
7
|
+
]);
|
|
8
|
+
/** Read-only bash command prefixes (program names). */
|
|
9
|
+
const READ_ONLY_BASH = [
|
|
10
|
+
'ls', 'cat', 'grep', 'rg', 'find', 'pwd', 'echo', 'head', 'tail', 'wc',
|
|
11
|
+
'which', 'whoami', 'date', 'env', 'tree', 'stat', 'file', 'du', 'df',
|
|
12
|
+
];
|
|
13
|
+
/** Is this tool call read-only (safe to run in plan mode)? */
|
|
14
|
+
export function isReadOnlyTool(toolName, input) {
|
|
15
|
+
const t = canonical(toolName);
|
|
16
|
+
if (READ_ONLY_TOOLS.has(t))
|
|
17
|
+
return true;
|
|
18
|
+
if (t === 'config') {
|
|
19
|
+
const action = String(input?.action ?? 'get');
|
|
20
|
+
return action !== 'set';
|
|
21
|
+
}
|
|
22
|
+
if (t === 'bash') {
|
|
23
|
+
const cmd = String(input?.command ?? '').trim().toLowerCase();
|
|
24
|
+
if (!cmd)
|
|
25
|
+
return false;
|
|
26
|
+
// Read-only git subcommands.
|
|
27
|
+
if (/^git\s+(status|diff|log|show|branch|remote|config\s+--get|rev-parse)\b/.test(cmd))
|
|
28
|
+
return true;
|
|
29
|
+
const prog = cmd.split(/[\s|;&]+/)[0];
|
|
30
|
+
return READ_ONLY_BASH.includes(prog);
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export type PermissionBehavior = 'allow' | 'deny' | 'ask';
|
|
2
|
+
/** A single rule: a tool name plus an optional content matcher (glob/prefix). */
|
|
3
|
+
export type PermissionRule = {
|
|
4
|
+
toolName: string;
|
|
5
|
+
ruleContent?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Rules grouped by behavior. */
|
|
8
|
+
export type PermissionRuleSet = {
|
|
9
|
+
allow: PermissionRule[];
|
|
10
|
+
deny: PermissionRule[];
|
|
11
|
+
ask: PermissionRule[];
|
|
12
|
+
};
|
|
13
|
+
/** A loose permission-update shape (mirrors the SDK's PermissionUpdate variants). */
|
|
14
|
+
export type PermissionUpdateInput = {
|
|
15
|
+
type: 'addRules' | 'replaceRules' | 'removeRules' | 'setMode' | string;
|
|
16
|
+
rules?: PermissionRule[];
|
|
17
|
+
behavior?: PermissionBehavior;
|
|
18
|
+
mode?: string;
|
|
19
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FileSystem } from './types/index.js';
|
|
2
|
+
/** Default char threshold before a tool result spills to disk (Claude Code uses 50k). */
|
|
3
|
+
export declare const DEFAULT_MAX_RESULT_CHARS = 50000;
|
|
4
|
+
/** Bytes of the full output shown inline as a preview. */
|
|
5
|
+
export declare const PREVIEW_CHARS = 2000;
|
|
6
|
+
/** Directory (relative to cwd) where spilled tool outputs are stored. */
|
|
7
|
+
export declare const TOOL_RESULTS_DIR = ".bcs/tool-results";
|
|
8
|
+
export declare const PERSISTED_OUTPUT_TAG = "<persisted-output>";
|
|
9
|
+
export declare const PERSISTED_OUTPUT_CLOSING_TAG = "</persisted-output>";
|
|
10
|
+
/**
|
|
11
|
+
* If `content` exceeds `threshold`, write it to a file under the workspace and
|
|
12
|
+
* return a preview message pointing the model at the path. Otherwise returns
|
|
13
|
+
* the content unchanged. Failures fall back to returning the original content.
|
|
14
|
+
*/
|
|
15
|
+
export declare function maybePersistLargeResult(content: string, toolUseId: string, fs: FileSystem, cwd: string, threshold?: number): Promise<string>;
|
package/dist/persist.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Large-output handling — mirrors Claude Code's tool-result persistence.
|
|
2
|
+
//
|
|
3
|
+
// When a tool returns more text than the threshold, the full output is written
|
|
4
|
+
// to a file in the workspace and the model receives a <persisted-output>
|
|
5
|
+
// preview + path instead. The model then pages through the full content with
|
|
6
|
+
// read_file (offset/limit). This keeps huge outputs (big greps, verbose builds,
|
|
7
|
+
// long fetches) out of the context window while keeping them fully accessible.
|
|
8
|
+
import { joinPath } from './tools/walk.js';
|
|
9
|
+
/** Default char threshold before a tool result spills to disk (Claude Code uses 50k). */
|
|
10
|
+
export const DEFAULT_MAX_RESULT_CHARS = 50_000;
|
|
11
|
+
/** Bytes of the full output shown inline as a preview. */
|
|
12
|
+
export const PREVIEW_CHARS = 2000;
|
|
13
|
+
/** Directory (relative to cwd) where spilled tool outputs are stored. */
|
|
14
|
+
export const TOOL_RESULTS_DIR = '.bcs/tool-results';
|
|
15
|
+
export const PERSISTED_OUTPUT_TAG = '<persisted-output>';
|
|
16
|
+
export const PERSISTED_OUTPUT_CLOSING_TAG = '</persisted-output>';
|
|
17
|
+
function formatSize(chars) {
|
|
18
|
+
if (chars < 1024)
|
|
19
|
+
return `${chars} chars`;
|
|
20
|
+
if (chars < 1024 * 1024)
|
|
21
|
+
return `${(chars / 1024).toFixed(1)} KB`;
|
|
22
|
+
return `${(chars / (1024 * 1024)).toFixed(1)} MB`;
|
|
23
|
+
}
|
|
24
|
+
/** Truncate at a newline boundary near the limit when possible. */
|
|
25
|
+
function preview(content) {
|
|
26
|
+
if (content.length <= PREVIEW_CHARS)
|
|
27
|
+
return { text: content, hasMore: false };
|
|
28
|
+
const slice = content.slice(0, PREVIEW_CHARS);
|
|
29
|
+
const lastNl = slice.lastIndexOf('\n');
|
|
30
|
+
const cut = lastNl > PREVIEW_CHARS * 0.5 ? slice.slice(0, lastNl) : slice;
|
|
31
|
+
return { text: cut, hasMore: true };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* If `content` exceeds `threshold`, write it to a file under the workspace and
|
|
35
|
+
* return a preview message pointing the model at the path. Otherwise returns
|
|
36
|
+
* the content unchanged. Failures fall back to returning the original content.
|
|
37
|
+
*/
|
|
38
|
+
export async function maybePersistLargeResult(content, toolUseId, fs, cwd, threshold = DEFAULT_MAX_RESULT_CHARS) {
|
|
39
|
+
if (!Number.isFinite(threshold) || content.length <= threshold)
|
|
40
|
+
return content;
|
|
41
|
+
const relPath = `${TOOL_RESULTS_DIR}/${toolUseId}.txt`;
|
|
42
|
+
const absPath = joinPath(cwd, relPath);
|
|
43
|
+
try {
|
|
44
|
+
await fs.writeFile(absPath, content);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// If we can't persist, leave the content inline (better than losing it).
|
|
48
|
+
return content;
|
|
49
|
+
}
|
|
50
|
+
const { text, hasMore } = preview(content);
|
|
51
|
+
return (`${PERSISTED_OUTPUT_TAG}\n` +
|
|
52
|
+
`Output too large (${formatSize(content.length)}). Full output saved to: ${absPath}\n` +
|
|
53
|
+
`Read it with read_file (use offset/limit to page through), or grep it for what you need.\n\n` +
|
|
54
|
+
`Preview (first ${formatSize(PREVIEW_CHARS)}):\n` +
|
|
55
|
+
text +
|
|
56
|
+
(hasMore ? '\n...' : '') +
|
|
57
|
+
`\n${PERSISTED_OUTPUT_CLOSING_TAG}`);
|
|
58
|
+
}
|
package/dist/prompt.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function defaultSystemPrompt(cwd: string): string;
|
|
2
|
+
/**
|
|
3
|
+
* Default system prompt for a general-purpose sub-agent spawned via the `task`
|
|
4
|
+
* tool. The sub-agent runs autonomously and returns only its final answer.
|
|
5
|
+
*/
|
|
6
|
+
export declare function defaultSubagentPrompt(cwd: string): string;
|
package/dist/prompt.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Default system prompt — a compact, faithful distillation of the Claude Code
|
|
2
|
+
// agent contract. Environment-neutral: the host (WebContainer, local OS, or a
|
|
3
|
+
// cloud sandbox) is described by the caller via appendSystemPrompt when needed.
|
|
4
|
+
export function defaultSystemPrompt(cwd) {
|
|
5
|
+
return `You are an interactive agent that helps users with software engineering tasks, operating on a real workspace (files + shell) via your tools.
|
|
6
|
+
|
|
7
|
+
You have access to tools for reading, writing, and editing files, running shell commands, and searching the codebase (glob/grep). Use them to accomplish the user's request.
|
|
8
|
+
|
|
9
|
+
# Working style
|
|
10
|
+
- Be concise and direct. Do what is asked; nothing more, nothing less.
|
|
11
|
+
- Prefer the dedicated file tools (read_file, write_file, edit_file) over shell commands like cat/sed for file operations.
|
|
12
|
+
- You MUST read a file with read_file before editing it with edit_file.
|
|
13
|
+
- When using edit_file, old_string must match the file exactly. If it is not unique, include more surrounding context or use replace_all.
|
|
14
|
+
- Run independent tool calls together when possible.
|
|
15
|
+
- Verify your work (run tests / commands) when practical before declaring success.
|
|
16
|
+
- Do not add comments to code unless asked or where the intent is non-obvious.
|
|
17
|
+
|
|
18
|
+
# Environment
|
|
19
|
+
- Working directory: ${cwd}
|
|
20
|
+
- Commands run in the workspace's shell; use commands appropriate to the host OS (the caller may specify the platform). The working directory persists between commands.
|
|
21
|
+
|
|
22
|
+
When the task is complete, stop calling tools and give a short summary of what you did.`;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Default system prompt for a general-purpose sub-agent spawned via the `task`
|
|
26
|
+
* tool. The sub-agent runs autonomously and returns only its final answer.
|
|
27
|
+
*/
|
|
28
|
+
export function defaultSubagentPrompt(cwd) {
|
|
29
|
+
return `You are a general-purpose sub-agent working autonomously on a delegated task in a real workspace (working directory: ${cwd}).
|
|
30
|
+
|
|
31
|
+
You have the same file, shell, and search tools as the main agent. Work through the task end to end on your own — you cannot ask the user questions.
|
|
32
|
+
|
|
33
|
+
Your FINAL message is the only thing returned to whoever delegated the task. Make it a complete, self-contained answer: report what you found or did, include the concrete results (paths, values, conclusions), and don't reference "the task" abstractly. Be concise and factual.`;
|
|
34
|
+
}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type { AgentDefinition, CanUseTool, HookCallback, HookEvent, LLMClient, PermissionMode, SDKMessage, SDKUserMessage } from './types/index.js';
|
|
2
|
+
import type { FileReadLimits, Tool } from './tools/types.js';
|
|
3
|
+
import type { McpServers, McpProxy } from './mcp/index.js';
|
|
4
|
+
import type { SlashCommand } from './commands/index.js';
|
|
5
|
+
import type { SessionStore } from './session/index.js';
|
|
6
|
+
import type { MemoryStore } from './memory/index.js';
|
|
7
|
+
import { type Workspace } from './agent.js';
|
|
8
|
+
export interface QueryOptions {
|
|
9
|
+
/** A plain string (single turn) or a stream of user messages (multi-turn). */
|
|
10
|
+
prompt: string | AsyncIterable<SDKUserMessage>;
|
|
11
|
+
/** Workspace implementing FileSystem + CommandExecutor (e.g. WebContainerWorkspace). */
|
|
12
|
+
workspace: Workspace;
|
|
13
|
+
/** Any OpenAI/Anthropic-compatible LLM client. */
|
|
14
|
+
llm: LLMClient;
|
|
15
|
+
/** Tools available to the agent. REPLACES the builtins. Defaults to ALL_CLAUDE_CODE_TOOLS. */
|
|
16
|
+
tools?: Tool[];
|
|
17
|
+
/** Custom tools ADDED to the builtins (use `defineTool`). Filtered by allowed/disallowedTools. */
|
|
18
|
+
extraTools?: Tool[];
|
|
19
|
+
model?: string;
|
|
20
|
+
systemPrompt?: string;
|
|
21
|
+
appendSystemPrompt?: string;
|
|
22
|
+
allowedTools?: string[];
|
|
23
|
+
disallowedTools?: string[];
|
|
24
|
+
maxTurns?: number;
|
|
25
|
+
cwd?: string;
|
|
26
|
+
sessionId?: string;
|
|
27
|
+
abortController?: AbortController;
|
|
28
|
+
/** Permission gate invoked before each tool call. */
|
|
29
|
+
canUseTool?: CanUseTool;
|
|
30
|
+
permissionMode?: PermissionMode;
|
|
31
|
+
/** Lifecycle hooks keyed by event. */
|
|
32
|
+
hooks?: Partial<Record<HookEvent, HookCallback[]>>;
|
|
33
|
+
/** File-read tuning passed to tools. */
|
|
34
|
+
limits?: Partial<FileReadLimits>;
|
|
35
|
+
/** Custom sub-agents invokable via the `task` tool, keyed by type name. */
|
|
36
|
+
agents?: Record<string, AgentDefinition>;
|
|
37
|
+
/** Max sub-agent nesting depth. Default 2. */
|
|
38
|
+
maxSubagentDepth?: number;
|
|
39
|
+
/** External MCP servers (HTTP/SSE) or in-process SDK servers. */
|
|
40
|
+
mcpServers?: McpServers;
|
|
41
|
+
/** Route remote MCP requests through a proxy (works around browser CORS). */
|
|
42
|
+
mcpProxy?: McpProxy;
|
|
43
|
+
/** Custom slash commands (merged with built-ins like /help, /compact). */
|
|
44
|
+
commands?: SlashCommand[];
|
|
45
|
+
/** Enable background tasks (task_list/task_output/task_stop + task run_in_background). */
|
|
46
|
+
background?: boolean;
|
|
47
|
+
/** Inject a shared BackgroundTaskManager so background tasks persist across turns. */
|
|
48
|
+
backgroundManager?: import('./background/index.js').BackgroundTaskManager;
|
|
49
|
+
/** Queue for interjecting user messages into the live loop (delivered one per turn boundary). */
|
|
50
|
+
messageQueue?: import('./queue.js').MessageQueue;
|
|
51
|
+
/** Emit `stream_event` partial-assistant messages (text deltas) as they arrive. */
|
|
52
|
+
includePartialMessages?: boolean;
|
|
53
|
+
/** Enable teammate coordination (shared mailbox + task board + team tools + coordinator prompt). */
|
|
54
|
+
team?: boolean;
|
|
55
|
+
/** Inject a shared Mailbox so team messaging persists across turns. */
|
|
56
|
+
mailbox?: import('./team/index.js').Mailbox;
|
|
57
|
+
/** Inject a shared TaskBoard so the task board persists across turns. */
|
|
58
|
+
board?: import('./team/index.js').TaskBoard;
|
|
59
|
+
/** This agent's name/label for messaging (default 'coordinator'). */
|
|
60
|
+
agentName?: string;
|
|
61
|
+
/** Persist the transcript to this store (keyed by sessionId) for resume. */
|
|
62
|
+
sessionStore?: SessionStore;
|
|
63
|
+
/** Load the stored transcript for sessionId before the first turn. */
|
|
64
|
+
resume?: boolean;
|
|
65
|
+
/** Auto-compact the transcript when it nears the context limit. */
|
|
66
|
+
autoCompact?: boolean;
|
|
67
|
+
/** Context window in tokens for auto-compaction (default: model window or 200k). */
|
|
68
|
+
contextLimit?: number;
|
|
69
|
+
/** Fraction of the context limit that triggers compaction (default 0.8). */
|
|
70
|
+
compactThreshold?: number;
|
|
71
|
+
/** Persistent memory store; entries load into the system prompt and are editable via memory tools. */
|
|
72
|
+
memory?: MemoryStore;
|
|
73
|
+
/** Permission rules (allow/deny/ask rule strings) → builds a canUseTool gate. */
|
|
74
|
+
permissionRules?: {
|
|
75
|
+
allow?: string[];
|
|
76
|
+
deny?: string[];
|
|
77
|
+
ask?: string[];
|
|
78
|
+
};
|
|
79
|
+
/** Prompt callback for 'ask' permission decisions. */
|
|
80
|
+
onPermissionAsk?: (toolName: string, input: Record<string, unknown>) => Promise<boolean>;
|
|
81
|
+
/** Load `.claude/settings.json` (project/local cascade), or pass a Settings object. */
|
|
82
|
+
settings?: boolean | import('./settings/index.js').Settings;
|
|
83
|
+
/** Load `.claude/skills/*.md` as slash commands + a skill registry, or pass a Skill[]. */
|
|
84
|
+
skills?: boolean | import('./skills/index.js').Skill[];
|
|
85
|
+
}
|
|
86
|
+
/** An async iterator of SDK messages, augmented with session controls. */
|
|
87
|
+
export interface Query extends AsyncGenerator<SDKMessage, void, void> {
|
|
88
|
+
/** Abort the in-flight run (stops the LLM stream and pending tools). */
|
|
89
|
+
interrupt(): void;
|
|
90
|
+
}
|
|
91
|
+
export declare function query(options: QueryOptions): Query;
|
|
92
|
+
/** Wrap a single text prompt into the async-iterable form runAgent expects. */
|
|
93
|
+
export declare function singlePrompt(text: string): AsyncIterable<SDKUserMessage>;
|
|
94
|
+
/**
|
|
95
|
+
* A simple push-based prompt queue for interactive sessions. Feed user turns
|
|
96
|
+
* with `.push(text)` and end the conversation with `.end()`.
|
|
97
|
+
*/
|
|
98
|
+
export declare class PromptStream implements AsyncIterable<SDKUserMessage> {
|
|
99
|
+
private queue;
|
|
100
|
+
private resolvers;
|
|
101
|
+
private done;
|
|
102
|
+
push(content: string | SDKUserMessage['message']['content']): void;
|
|
103
|
+
end(): void;
|
|
104
|
+
[Symbol.asyncIterator](): AsyncIterator<SDKUserMessage>;
|
|
105
|
+
}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Public entry point — mirrors @anthropic-ai/claude-agent-sdk's query().
|
|
2
|
+
//
|
|
3
|
+
// Returns an AsyncGenerator<SDKMessage>. Accepts either a single string prompt
|
|
4
|
+
// or an async iterable of SDKUserMessage (for multi-turn / interactive use).
|
|
5
|
+
import { runAgent } from './agent.js';
|
|
6
|
+
export function query(options) {
|
|
7
|
+
const prompt = typeof options.prompt === 'string'
|
|
8
|
+
? singlePrompt(options.prompt)
|
|
9
|
+
: options.prompt;
|
|
10
|
+
const abortController = options.abortController ?? new AbortController();
|
|
11
|
+
const gen = runAgent({
|
|
12
|
+
prompt,
|
|
13
|
+
workspace: options.workspace,
|
|
14
|
+
llm: options.llm,
|
|
15
|
+
tools: options.tools,
|
|
16
|
+
extraTools: options.extraTools,
|
|
17
|
+
model: options.model,
|
|
18
|
+
systemPrompt: options.systemPrompt,
|
|
19
|
+
appendSystemPrompt: options.appendSystemPrompt,
|
|
20
|
+
allowedTools: options.allowedTools,
|
|
21
|
+
disallowedTools: options.disallowedTools,
|
|
22
|
+
maxTurns: options.maxTurns,
|
|
23
|
+
cwd: options.cwd,
|
|
24
|
+
sessionId: options.sessionId,
|
|
25
|
+
abortController,
|
|
26
|
+
canUseTool: options.canUseTool,
|
|
27
|
+
permissionMode: options.permissionMode,
|
|
28
|
+
hooks: options.hooks,
|
|
29
|
+
limits: options.limits,
|
|
30
|
+
agents: options.agents,
|
|
31
|
+
maxSubagentDepth: options.maxSubagentDepth,
|
|
32
|
+
mcpServers: options.mcpServers,
|
|
33
|
+
mcpProxy: options.mcpProxy,
|
|
34
|
+
commands: options.commands,
|
|
35
|
+
background: options.background,
|
|
36
|
+
backgroundManager: options.backgroundManager,
|
|
37
|
+
messageQueue: options.messageQueue,
|
|
38
|
+
includePartialMessages: options.includePartialMessages,
|
|
39
|
+
team: options.team,
|
|
40
|
+
mailbox: options.mailbox,
|
|
41
|
+
board: options.board,
|
|
42
|
+
agentName: options.agentName,
|
|
43
|
+
sessionStore: options.sessionStore,
|
|
44
|
+
resume: options.resume,
|
|
45
|
+
autoCompact: options.autoCompact,
|
|
46
|
+
contextLimit: options.contextLimit,
|
|
47
|
+
compactThreshold: options.compactThreshold,
|
|
48
|
+
memory: options.memory,
|
|
49
|
+
permissionRules: options.permissionRules,
|
|
50
|
+
onPermissionAsk: options.onPermissionAsk,
|
|
51
|
+
settings: options.settings,
|
|
52
|
+
skills: options.skills,
|
|
53
|
+
});
|
|
54
|
+
gen.interrupt = () => abortController.abort();
|
|
55
|
+
return gen;
|
|
56
|
+
}
|
|
57
|
+
/** Wrap a single text prompt into the async-iterable form runAgent expects. */
|
|
58
|
+
export async function* singlePrompt(text) {
|
|
59
|
+
yield {
|
|
60
|
+
type: 'user',
|
|
61
|
+
message: { role: 'user', content: text },
|
|
62
|
+
parent_tool_use_id: null,
|
|
63
|
+
timestamp: new Date().toISOString(),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* A simple push-based prompt queue for interactive sessions. Feed user turns
|
|
68
|
+
* with `.push(text)` and end the conversation with `.end()`.
|
|
69
|
+
*/
|
|
70
|
+
export class PromptStream {
|
|
71
|
+
constructor() {
|
|
72
|
+
this.queue = [];
|
|
73
|
+
this.resolvers = [];
|
|
74
|
+
this.done = false;
|
|
75
|
+
}
|
|
76
|
+
push(content) {
|
|
77
|
+
const message = {
|
|
78
|
+
type: 'user',
|
|
79
|
+
message: {
|
|
80
|
+
role: 'user',
|
|
81
|
+
content: typeof content === 'string' ? content : content,
|
|
82
|
+
},
|
|
83
|
+
parent_tool_use_id: null,
|
|
84
|
+
timestamp: new Date().toISOString(),
|
|
85
|
+
};
|
|
86
|
+
const r = this.resolvers.shift();
|
|
87
|
+
if (r)
|
|
88
|
+
r({ value: message, done: false });
|
|
89
|
+
else
|
|
90
|
+
this.queue.push(message);
|
|
91
|
+
}
|
|
92
|
+
end() {
|
|
93
|
+
this.done = true;
|
|
94
|
+
let r = this.resolvers.shift();
|
|
95
|
+
while (r) {
|
|
96
|
+
r({ value: undefined, done: true });
|
|
97
|
+
r = this.resolvers.shift();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
[Symbol.asyncIterator]() {
|
|
101
|
+
return {
|
|
102
|
+
next: () => {
|
|
103
|
+
const queued = this.queue.shift();
|
|
104
|
+
if (queued)
|
|
105
|
+
return Promise.resolve({ value: queued, done: false });
|
|
106
|
+
if (this.done)
|
|
107
|
+
return Promise.resolve({
|
|
108
|
+
value: undefined,
|
|
109
|
+
done: true,
|
|
110
|
+
});
|
|
111
|
+
return new Promise((resolve) => this.resolvers.push(resolve));
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|