beast-agent 2.15.0 → 2.17.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/package.json +1 -1
- package/src/agent/engine.js +505 -156
- package/src/agent/opencode/agents.js +161 -0
- package/src/agent/opencode/askperm.js +135 -0
- package/src/agent/opencode/index.js +51 -0
- package/src/agent/opencode/permission.js +108 -0
- package/src/agent/opencode/prompts/anthropic.txt +105 -0
- package/src/agent/opencode/prompts/beast.txt +147 -0
- package/src/agent/opencode/prompts/build-switch.txt +5 -0
- package/src/agent/opencode/prompts/codex.txt +79 -0
- package/src/agent/opencode/prompts/default.txt +95 -0
- package/src/agent/opencode/prompts/edit.txt +10 -0
- package/src/agent/opencode/prompts/explore.txt +18 -0
- package/src/agent/opencode/prompts/gemini.txt +155 -0
- package/src/agent/opencode/prompts/glob.txt +6 -0
- package/src/agent/opencode/prompts/gpt.txt +107 -0
- package/src/agent/opencode/prompts/grep.txt +8 -0
- package/src/agent/opencode/prompts/kimi.txt +95 -0
- package/src/agent/opencode/prompts/max-steps.txt +16 -0
- package/src/agent/opencode/prompts/meta.txt +65 -0
- package/src/agent/opencode/prompts/plan.txt +26 -0
- package/src/agent/opencode/prompts/read.txt +14 -0
- package/src/agent/opencode/prompts/skill.txt +5 -0
- package/src/agent/opencode/prompts/task.txt +19 -0
- package/src/agent/opencode/prompts/todowrite.txt +44 -0
- package/src/agent/opencode/prompts/trinity.txt +97 -0
- package/src/agent/opencode/prompts/webfetch.txt +13 -0
- package/src/agent/opencode/prompts/websearch.txt +14 -0
- package/src/agent/opencode/prompts/write.txt +8 -0
- package/src/agent/opencode/systemprompt.js +157 -0
- package/src/agent/opencode/toolmap.js +316 -0
- package/src/agent/supermemory.js +132 -0
- package/src/main.js +135 -41
- package/src/preload.js +5 -0
- package/src/renderer/renderer.js +59 -15
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* ---------- opencode session/system.ts + reminders.ts portu (birebir) ----------
|
|
4
|
+
Beast Code (bcCode) oturumlarında system prompt artık TEK elle yazılmış
|
|
5
|
+
metin değil, opencode birleşimidir (prompt.ts:1257-1269):
|
|
6
|
+
|
|
7
|
+
system = [ ...environment(model), → bu modül
|
|
8
|
+
...instructions(AGENTS.md), → engine._projectInstructions
|
|
9
|
+
...(skills kataloğu), → bu modül (skill/skill.ts fmt)
|
|
10
|
+
...ajan tanımının promptu ] → agents.js (explore vb.)
|
|
11
|
+
|
|
12
|
+
Ana prompt MODEL BAZLI seçilir (system.ts:27-49): gpt-4 ailesi, o1, o3 → beast.txt,
|
|
13
|
+
gpt* → gpt.txt (codex* → codex.txt), gemini-* → gemini.txt, claude →
|
|
14
|
+
anthropic.txt, kimi → kimi.txt, muse → meta.txt, trinity → trinity.txt,
|
|
15
|
+
diğer → default.txt.
|
|
16
|
+
|
|
17
|
+
Reminders (reminders.ts:15-90): plan ajanındaysa son user mesajına plan.txt
|
|
18
|
+
synthetic part'ı; geçmişte plan koşmuşsa ve şimdi build'se build-switch.txt
|
|
19
|
+
eklenir. Belleği kirletmemek için payload ÖNCESİ kopya mesajlara uygulanır
|
|
20
|
+
(her tur taze — opencode her turda DB'den yeniden yükleyip uygular). */
|
|
21
|
+
|
|
22
|
+
const fs = require('fs');
|
|
23
|
+
const path = require('path');
|
|
24
|
+
const Permission = require('./permission');
|
|
25
|
+
|
|
26
|
+
const PROMPT_DIR = path.join(__dirname, 'prompts');
|
|
27
|
+
function loadPrompt(name) {
|
|
28
|
+
try {
|
|
29
|
+
let t = fs.readFileSync(path.join(PROMPT_DIR, name), 'utf8');
|
|
30
|
+
if (t.charCodeAt(0) === 0xfeff) t = t.slice(1); // BOM-strip
|
|
31
|
+
return t.replace(/\s+$/, '');
|
|
32
|
+
} catch {
|
|
33
|
+
return '';
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const PROMPTS = {
|
|
38
|
+
beast: loadPrompt('beast.txt'),
|
|
39
|
+
gpt: loadPrompt('gpt.txt'),
|
|
40
|
+
codex: loadPrompt('codex.txt'),
|
|
41
|
+
anthropic: loadPrompt('anthropic.txt'),
|
|
42
|
+
gemini: loadPrompt('gemini.txt'),
|
|
43
|
+
kimi: loadPrompt('kimi.txt'),
|
|
44
|
+
meta: loadPrompt('meta.txt'),
|
|
45
|
+
trinity: loadPrompt('trinity.txt'),
|
|
46
|
+
default: loadPrompt('default.txt'),
|
|
47
|
+
plan: loadPrompt('plan.txt'),
|
|
48
|
+
buildSwitch: loadPrompt('build-switch.txt'),
|
|
49
|
+
explore: loadPrompt('explore.txt'),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
/* opencode system.ts:27-49 — model-bazlı prompt seçimi (birebir) */
|
|
53
|
+
function provider(modelId) {
|
|
54
|
+
const id = String(modelId || '');
|
|
55
|
+
if (id.includes('muse')) {
|
|
56
|
+
const name = id.includes('muse-glimmer') ? 'Muse Glimmer' : 'Muse Spark';
|
|
57
|
+
return [PROMPTS.meta.replaceAll('{{MODEL_NAME}}', name)];
|
|
58
|
+
}
|
|
59
|
+
if (id.includes('gpt-4') || id.includes('o1') || id.includes('o3')) return [PROMPTS.beast];
|
|
60
|
+
if (id.includes('gpt')) {
|
|
61
|
+
if (id.includes('codex')) return [PROMPTS.codex];
|
|
62
|
+
return [PROMPTS.gpt];
|
|
63
|
+
}
|
|
64
|
+
if (id.includes('gemini-')) return [PROMPTS.gemini];
|
|
65
|
+
if (id.includes('claude')) return [PROMPTS.anthropic];
|
|
66
|
+
if (id.toLowerCase().includes('trinity')) return [PROMPTS.trinity];
|
|
67
|
+
if (id.toLowerCase().includes('kimi')) return [PROMPTS.kimi];
|
|
68
|
+
return [PROMPTS.default];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* opencode SystemPrompt.environment — model kimliği + <env> bloğu */
|
|
72
|
+
function environment({ modelId, providerId, directory, worktree }) {
|
|
73
|
+
const isGit = (() => {
|
|
74
|
+
try {
|
|
75
|
+
return fs.existsSync(path.join(String(directory || ''), '.git')) ? 'yes' : 'no';
|
|
76
|
+
} catch {
|
|
77
|
+
return 'no';
|
|
78
|
+
}
|
|
79
|
+
})();
|
|
80
|
+
return [
|
|
81
|
+
[
|
|
82
|
+
`You are powered by the model named ${modelId}. The exact model ID is ${providerId}/${modelId}`,
|
|
83
|
+
`Here is some useful information about the environment you are running in:`,
|
|
84
|
+
`<env>`,
|
|
85
|
+
` Working directory: ${directory}`,
|
|
86
|
+
` Workspace root folder: ${worktree}`,
|
|
87
|
+
` Is directory a git repo: ${isGit}`,
|
|
88
|
+
` Platform: ${process.platform}`,
|
|
89
|
+
` Today's date: ${new Date().toDateString()}`,
|
|
90
|
+
`</env>`,
|
|
91
|
+
].join('\n'),
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/* opencode skill/skill.ts fmt (verbose) — skills.js scan() listesini
|
|
96
|
+
opencode katalog biçimine basar */
|
|
97
|
+
function skillsCatalog(skills) {
|
|
98
|
+
const described = (skills || []).filter((s) => s && s.description);
|
|
99
|
+
if (!described.length) return undefined;
|
|
100
|
+
return [
|
|
101
|
+
'Skills provide specialized instructions and workflows for specific tasks.',
|
|
102
|
+
'Use the skill tool to load a skill when a task matches its description.',
|
|
103
|
+
'<available_skills>',
|
|
104
|
+
...described
|
|
105
|
+
.slice()
|
|
106
|
+
.sort((a, b) => String(a.name).localeCompare(String(b.name)))
|
|
107
|
+
.flatMap((s) => [
|
|
108
|
+
' <skill>',
|
|
109
|
+
` <name>${s.name}</name>`,
|
|
110
|
+
` <description>${s.description}</description>`,
|
|
111
|
+
` <location>${s.path}</location>`,
|
|
112
|
+
' </skill>',
|
|
113
|
+
]),
|
|
114
|
+
'</available_skills>',
|
|
115
|
+
].join('\n');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/* opencode SystemPrompt.skills — ajanın skill izni yoksa katalog gösterilmez */
|
|
119
|
+
function skills(ruleset, scanFn) {
|
|
120
|
+
if (Permission.disabled(['skill'], ruleset).has('skill')) return undefined;
|
|
121
|
+
try {
|
|
122
|
+
return skillsCatalog(scanFn());
|
|
123
|
+
} catch {
|
|
124
|
+
return undefined;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* opencode SessionReminders.apply — synthetic part yerine son user mesajının
|
|
129
|
+
KOPIASINA metin ekler; session.messages asla değişmez (her tur taze uygulanır).
|
|
130
|
+
wasPlan: bu oturumda daha önce plan ajanı koştu mu (engine takip eder). */
|
|
131
|
+
function applyReminders(messages, agent, wasPlan) {
|
|
132
|
+
const msgs = (messages || []).slice();
|
|
133
|
+
const lastUserIdx = msgs.findLastIndex ? msgs.findLastIndex((m) => m.role === 'user') : -1;
|
|
134
|
+
if (lastUserIdx < 0 || !agent) return msgs;
|
|
135
|
+
const adds = [];
|
|
136
|
+
if (agent.name === 'plan') adds.push(PROMPTS.plan);
|
|
137
|
+
if (wasPlan && agent.name === 'build') adds.push(PROMPTS.buildSwitch);
|
|
138
|
+
if (!adds.length) return msgs;
|
|
139
|
+
const base = msgs[lastUserIdx];
|
|
140
|
+
const baseText =
|
|
141
|
+
typeof base.content === 'string' ? base.content : '';
|
|
142
|
+
msgs[lastUserIdx] = {
|
|
143
|
+
...base,
|
|
144
|
+
content: (baseText ? baseText + '\n\n' : '') + adds.join('\n\n'),
|
|
145
|
+
};
|
|
146
|
+
return msgs;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
module.exports = {
|
|
150
|
+
PROMPTS,
|
|
151
|
+
provider,
|
|
152
|
+
environment,
|
|
153
|
+
skills,
|
|
154
|
+
skillsCatalog,
|
|
155
|
+
applyReminders,
|
|
156
|
+
loadPrompt,
|
|
157
|
+
};
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* ---------- opencode tool seti → Beast araç köprüsü ----------
|
|
4
|
+
Beast Code (bcCode) oturumlarında model opencode'un ARAÇ DİLİYLE konuşur:
|
|
5
|
+
read · edit · write · bash · glob · grep · list · fetch · websearch ·
|
|
6
|
+
todowrite · task · skill (opencode builtin registry sırası)
|
|
7
|
+
|
|
8
|
+
Tanımlar opencode-dev'den birebir: açıklama metinleri tool/*.txt,
|
|
9
|
+
parametre şemaları opencode Schema.Struct karşılıkları (JSON Schema).
|
|
10
|
+
execMap() çağrıyı Beast'in yerleşik aracına çevirir — implementasyon
|
|
11
|
+
değişmez, modelin gördüğü dünya opencode olur. */
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const PROMPT_DIR = path.join(__dirname, 'prompts');
|
|
17
|
+
function loadPrompt(name) {
|
|
18
|
+
try {
|
|
19
|
+
let t = fs.readFileSync(path.join(PROMPT_DIR, name), 'utf8');
|
|
20
|
+
if (t.charCodeAt(0) === 0xfeff) t = t.slice(1); // BOM-strip
|
|
21
|
+
return t.replace(/\s+$/, '') + '\n';
|
|
22
|
+
} catch {
|
|
23
|
+
return '';
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* opencode tool/shell/prompt.ts Windows (PowerShell) açıklaması — Beast
|
|
28
|
+
değerleriyle: varsayılan timeout 120s, 2000 satır / 50KB tavan, tam
|
|
29
|
+
çıktı geçici dosyaya düşer (Beast run_command zaten bu disiplinde) */
|
|
30
|
+
function bashDescription() {
|
|
31
|
+
const defaultTimeoutMs = 120000;
|
|
32
|
+
const maxLines = 2000;
|
|
33
|
+
const maxBytes = '50KB';
|
|
34
|
+
return `Executes a given PowerShell command in a persistent shell session.
|
|
35
|
+
|
|
36
|
+
## Command Execution
|
|
37
|
+
|
|
38
|
+
Before executing the command, please follow these steps:
|
|
39
|
+
1. If the command will create new directories or files, first use \`Test-Path -LiteralPath <parent>\` to verify the parent directory exists and is the correct location
|
|
40
|
+
2. If the command will modify or delete files, first verify the paths are correct
|
|
41
|
+
3. For commands expected to create new directories or files, verify the expected file/folder location is inside the working directory
|
|
42
|
+
|
|
43
|
+
After ensuring proper quoting, execute the command.
|
|
44
|
+
- Capture the output of the command.
|
|
45
|
+
|
|
46
|
+
## Usage Notes
|
|
47
|
+
|
|
48
|
+
- The command argument is required.
|
|
49
|
+
- You can specify an optional timeout in milliseconds. If not specified, commands will time out after ${defaultTimeoutMs}ms.
|
|
50
|
+
- If the output exceeds ${maxLines} lines or ${maxBytes} bytes, it will be truncated and the full output will be written to a file. You can use Read with offset/limit to read specific sections or Grep to search the full content. Do NOT use \`Select-Object -First\`, \`Select-Object -Last\`, or other truncation commands to limit output; the full output will already be captured to a file for more precise searching.
|
|
51
|
+
- Avoid using Shell with PowerShell file/content cmdlets unless explicitly instructed or when these cmdlets are truly necessary for the task. Instead, always prefer using the dedicated tools for reading, searching, and editing files:
|
|
52
|
+
- Read: Reading and analyzing file contents (with line numbers)
|
|
53
|
+
- Edit: Making targeted changes to specific sections of files
|
|
54
|
+
- Write: Creating new files from scratch
|
|
55
|
+
- Grep: Searching file contents with regex patterns
|
|
56
|
+
- Glob: Finding files by patterns (e.g., "src/**/*.ts")
|
|
57
|
+
- When issuing multiple commands:
|
|
58
|
+
- If the commands are independent and can run in parallel, make multiple bash tool calls in a single message. For example, if you need to run "git status" and "git diff", send a single message with two bash tool calls in parallel.
|
|
59
|
+
- Use ';' only when you need to run commands sequentially but don't care if earlier commands fail
|
|
60
|
+
- If the commands depend on each other and must run sequentially, avoid '&&' in this shell because Windows PowerShell (5.1) does not support it. Use PowerShell conditionals such as \`cmd1; if ($?) { cmd2 }\` when later commands must depend on earlier success.
|
|
61
|
+
- DO NOT use newlines to separate commands (newlines are ok in quoted strings)
|
|
62
|
+
- The shell is PERSISTENT: working directory and environment variables are preserved between calls.
|
|
63
|
+
- Commands run in Windows PowerShell (5.1).`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/* opencode tool şemaları — Schema.Struct karşılığı JSON Schema'lar */
|
|
67
|
+
const DEFS = () => [
|
|
68
|
+
{
|
|
69
|
+
name: 'bash',
|
|
70
|
+
description: bashDescription(),
|
|
71
|
+
parameters: {
|
|
72
|
+
type: 'object',
|
|
73
|
+
properties: {
|
|
74
|
+
command: { type: 'string', description: 'The command to execute' },
|
|
75
|
+
timeout: { type: 'integer', description: 'Optional timeout in milliseconds' },
|
|
76
|
+
},
|
|
77
|
+
required: ['command'],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
name: 'read',
|
|
82
|
+
description: loadPrompt('read.txt'),
|
|
83
|
+
parameters: {
|
|
84
|
+
type: 'object',
|
|
85
|
+
properties: {
|
|
86
|
+
filePath: { type: 'string', description: 'The absolute path to the file or directory to read' },
|
|
87
|
+
offset: { type: 'integer', description: 'The line number to start reading from (1-indexed)' },
|
|
88
|
+
limit: { type: 'integer', description: 'The maximum number of lines to read (defaults to 2000)' },
|
|
89
|
+
},
|
|
90
|
+
required: ['filePath'],
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'edit',
|
|
95
|
+
description: loadPrompt('edit.txt'),
|
|
96
|
+
parameters: {
|
|
97
|
+
type: 'object',
|
|
98
|
+
properties: {
|
|
99
|
+
filePath: { type: 'string', description: 'The absolute path to the file to modify' },
|
|
100
|
+
oldString: { type: 'string', description: 'The text to replace' },
|
|
101
|
+
newString: { type: 'string', description: 'The text to replace it with (must be different from oldString)' },
|
|
102
|
+
replaceAll: { type: 'boolean', description: 'Replace all occurrences of oldString (default false)' },
|
|
103
|
+
},
|
|
104
|
+
required: ['filePath', 'oldString', 'newString'],
|
|
105
|
+
},
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'write',
|
|
109
|
+
description: loadPrompt('write.txt'),
|
|
110
|
+
parameters: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
filePath: { type: 'string', description: 'The absolute path to the file to write (will create the file if it does not exist, will overwrite if it does)' },
|
|
114
|
+
content: { type: 'string', description: 'The content to write' },
|
|
115
|
+
},
|
|
116
|
+
required: ['filePath', 'content'],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'glob',
|
|
121
|
+
description: loadPrompt('glob.txt'),
|
|
122
|
+
parameters: {
|
|
123
|
+
type: 'object',
|
|
124
|
+
properties: {
|
|
125
|
+
pattern: { type: 'string', description: 'The glob pattern to match files against' },
|
|
126
|
+
path: { type: 'string', description: 'The directory to search in. If not specified, the current working directory will be used. IMPORTANT: Omit this field to use the default directory. DO NOT enter "undefined" or "null" - simply omit it for the default behavior. Must be a valid directory path if provided.' },
|
|
127
|
+
},
|
|
128
|
+
required: ['pattern'],
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'grep',
|
|
133
|
+
description: loadPrompt('grep.txt'),
|
|
134
|
+
parameters: {
|
|
135
|
+
type: 'object',
|
|
136
|
+
properties: {
|
|
137
|
+
pattern: { type: 'string', description: 'The regex pattern to search for in file contents' },
|
|
138
|
+
path: { type: 'string', description: 'The directory to search in. Defaults to the current working directory.' },
|
|
139
|
+
include: { type: 'string', description: 'File pattern to include in the search (e.g. "*.js", "*.{ts,tsx}")' },
|
|
140
|
+
},
|
|
141
|
+
required: ['pattern'],
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
name: 'list',
|
|
146
|
+
description: 'List files and directories in a given path. Returns entries sorted by name (directories first, then files), with type indicator and size.',
|
|
147
|
+
parameters: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
path: { type: 'string', description: 'The directory path to list. Defaults to the current working directory.' },
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
name: 'webfetch',
|
|
156
|
+
description: loadPrompt('webfetch.txt'),
|
|
157
|
+
parameters: {
|
|
158
|
+
type: 'object',
|
|
159
|
+
properties: {
|
|
160
|
+
url: { type: 'string', description: 'The URL to fetch' },
|
|
161
|
+
format: { type: 'string', enum: ['text', 'markdown', 'html'], description: 'The format to return the content in (text, markdown, or html). Defaults to markdown.' },
|
|
162
|
+
timeout: { type: 'integer', description: 'Optional timeout in seconds (max 120)' },
|
|
163
|
+
max_chars: { type: 'integer', description: 'Optional maximum characters to return' },
|
|
164
|
+
},
|
|
165
|
+
required: ['url'],
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
name: 'websearch',
|
|
170
|
+
description: loadPrompt('websearch.txt'),
|
|
171
|
+
parameters: {
|
|
172
|
+
type: 'object',
|
|
173
|
+
properties: {
|
|
174
|
+
query: { type: 'string', description: 'Websearch query' },
|
|
175
|
+
numResults: { type: 'integer', description: 'Number of search results to return (default: 8)' },
|
|
176
|
+
type: { type: 'string', enum: ['auto', 'fast', 'deep'], description: "Search type - 'auto': balanced search (default), 'fast': quick results, 'deep': comprehensive search" },
|
|
177
|
+
},
|
|
178
|
+
required: ['query'],
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: 'todowrite',
|
|
183
|
+
description: loadPrompt('todowrite.txt'),
|
|
184
|
+
parameters: {
|
|
185
|
+
type: 'object',
|
|
186
|
+
properties: {
|
|
187
|
+
todos: {
|
|
188
|
+
type: 'array',
|
|
189
|
+
description: 'The updated todo list',
|
|
190
|
+
items: {
|
|
191
|
+
type: 'object',
|
|
192
|
+
properties: {
|
|
193
|
+
content: { type: 'string', description: 'Brief description of the task' },
|
|
194
|
+
status: { type: 'string', enum: ['pending', 'in_progress', 'completed', 'cancelled'], description: 'Current status of the task' },
|
|
195
|
+
priority: { type: 'string', enum: ['high', 'medium', 'low'], description: 'Priority level of the task' },
|
|
196
|
+
},
|
|
197
|
+
required: ['content', 'status'],
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
required: ['todos'],
|
|
202
|
+
},
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
name: 'task',
|
|
206
|
+
description: loadPrompt('task.txt'),
|
|
207
|
+
parameters: {
|
|
208
|
+
type: 'object',
|
|
209
|
+
properties: {
|
|
210
|
+
description: { type: 'string', description: 'A short description of the task (max 80 chars)' },
|
|
211
|
+
prompt: { type: 'string', description: 'The full task description for the subagent to execute' },
|
|
212
|
+
subagent_type: { type: 'string', enum: ['general', 'explore'], description: 'Type of subagent to launch (options: explore, general)' },
|
|
213
|
+
},
|
|
214
|
+
required: ['description', 'prompt', 'subagent_type'],
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: 'skill',
|
|
219
|
+
description: loadPrompt('skill.txt'),
|
|
220
|
+
parameters: {
|
|
221
|
+
type: 'object',
|
|
222
|
+
properties: {
|
|
223
|
+
name: { type: 'string', description: 'The name of the skill from available_skills' },
|
|
224
|
+
},
|
|
225
|
+
required: ['name'],
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
/* Beast Sandbox'a özgü: UZUN SÜRELİ süreçleri (dev server vb.) ÇALIŞTIR
|
|
230
|
+
paneline devreder — tur BLOKLAMAZ. panel_run izni yalnız sandbox
|
|
231
|
+
ajanında allow edilir (agents.js); BC build/plan'da gizlidir. */
|
|
232
|
+
name: 'panel_run',
|
|
233
|
+
description:
|
|
234
|
+
'Run a LONG-RUNNING process (dev server, background service) in the managed RUN panel instead of the blocking shell. The turn does not wait for it to finish; output streams live in the panel and can be stopped by the user. Use this instead of starting servers with bash — a blocking bash call would stall the whole job. Only ONE process runs at a time.',
|
|
235
|
+
parameters: {
|
|
236
|
+
type: 'object',
|
|
237
|
+
properties: {
|
|
238
|
+
command: { type: 'string', description: 'The command to run in the panel' },
|
|
239
|
+
},
|
|
240
|
+
required: ['command'],
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
/* OpenAI function-call formatına çevirir (engine TOOLS formatı) */
|
|
246
|
+
function definitions() {
|
|
247
|
+
return DEFS().map((d) => ({ type: 'function', function: d }));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const TO_BUILTIN = {
|
|
251
|
+
bash: 'run_command',
|
|
252
|
+
read: 'read_file',
|
|
253
|
+
edit: 'edit_file',
|
|
254
|
+
write: 'write_file',
|
|
255
|
+
glob: 'glob',
|
|
256
|
+
grep: 'grep',
|
|
257
|
+
list: 'list_dir',
|
|
258
|
+
webfetch: 'webfetch',
|
|
259
|
+
websearch: 'web_search',
|
|
260
|
+
todowrite: 'todo_write',
|
|
261
|
+
task: 'delegate_task',
|
|
262
|
+
panel_run: 'panel_run',
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/* opencode parametreleri → Beast araç parametreleri */
|
|
266
|
+
function execMap(name, args) {
|
|
267
|
+
const a = args || {};
|
|
268
|
+
switch (name) {
|
|
269
|
+
case 'read':
|
|
270
|
+
return { name: 'read_file', args: { path: a.filePath, offset: a.offset, limit: a.limit } };
|
|
271
|
+
case 'edit':
|
|
272
|
+
return {
|
|
273
|
+
name: 'edit_file',
|
|
274
|
+
args: { path: a.filePath, old_string: a.oldString, new_string: a.newString, replace_all: a.replaceAll === true },
|
|
275
|
+
};
|
|
276
|
+
case 'write':
|
|
277
|
+
return { name: 'write_file', args: { path: a.filePath, content: a.content } };
|
|
278
|
+
case 'bash':
|
|
279
|
+
return { name: 'run_command', args: { command: a.command, timeout_ms: a.timeout } };
|
|
280
|
+
case 'list':
|
|
281
|
+
return { name: 'list_dir', args: { path: a.path } };
|
|
282
|
+
case 'webfetch':
|
|
283
|
+
return { name: 'webfetch', args: { url: a.url, format: a.format, timeout: a.timeout, max_chars: a.max_chars } };
|
|
284
|
+
case 'websearch':
|
|
285
|
+
return { name: 'web_search', args: { query: a.query, max_results: a.numResults || a.max_results } };
|
|
286
|
+
case 'todowrite':
|
|
287
|
+
return { name: 'todo_write', args: { items: (a.todos || []).map(opencodeTodoToItem) } };
|
|
288
|
+
case 'task':
|
|
289
|
+
return {
|
|
290
|
+
name: 'delegate_task',
|
|
291
|
+
args: { task: a.prompt || a.description, context: a.description || '', subagent_type: a.subagent_type },
|
|
292
|
+
};
|
|
293
|
+
default:
|
|
294
|
+
return { name: TO_BUILTIN[name] || name, args: a };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* opencode Todo.Info {content, status, priority} → Beast iç depo {title, status}
|
|
299
|
+
opencode durum makinesi: pending → in_progress → completed | cancelled
|
|
300
|
+
Beast iç durumu: pending | active | done */
|
|
301
|
+
const OC_TODO_STATUS = { pending: 'pending', in_progress: 'active', completed: 'done', cancelled: 'done' };
|
|
302
|
+
function opencodeTodoToItem(t) {
|
|
303
|
+
const it = t || {};
|
|
304
|
+
return {
|
|
305
|
+
title: String(it.content ?? '').replace(/\s+/g, ' ').trim().slice(0, 120),
|
|
306
|
+
status: OC_TODO_STATUS[String(it.status || 'pending')] || 'pending',
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/* BC oturumunda modele sunulan opencode araç adları (permission filtresinden
|
|
311
|
+
önce tam set — opencode registry builtin sırası) */
|
|
312
|
+
function names() {
|
|
313
|
+
return DEFS().map((d) => d.name);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
module.exports = { definitions, names, execMap, TO_BUILTIN, opencodeTodoToItem };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/* ---------- Supermemory (lokal) adaptörü ----------
|
|
4
|
+
Beast'in bellek katmanına supermemory (git.new/memory) köprüsü — mem0.js
|
|
5
|
+
deseniyle TAKILABİLİR katman: sunucu (npx supermemory local → localhost:6767)
|
|
6
|
+
ayaktayken daha akıllı bellek (graf motoru, otomatik unutma, çelişki
|
|
7
|
+
çözümü, kullanıcı profili); ayakta değilken engine mevcut hafızaya sorunsuz
|
|
8
|
+
düşer. Zero npm bağımlılık — Node 18+ global fetch.
|
|
9
|
+
|
|
10
|
+
Uçlar (supermemory docs birebir):
|
|
11
|
+
POST /v3/documents { content, containerTag, customId?, metadata? }
|
|
12
|
+
POST /v4/search { q, containerTag, searchMode, limit, include? }
|
|
13
|
+
POST /v4/profile { containerTag, q? }
|
|
14
|
+
Auth: Authorization: Bearer <apiKey> (local key ilk boot'ta basılır) */
|
|
15
|
+
|
|
16
|
+
const BASE_TIMEOUT_MS = 8000;
|
|
17
|
+
const PROBE_TIMEOUT_MS = 2500;
|
|
18
|
+
const HEALTH_COOLDOWN_MS = 60000; // kapalıya aynı dakikada 200 kez vurma
|
|
19
|
+
|
|
20
|
+
function normalizeBase(url) {
|
|
21
|
+
return String(url || '').trim().replace(/\/+$/, '');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function call(baseUrl, apiKey, path, body, timeoutMs) {
|
|
25
|
+
const base = normalizeBase(baseUrl);
|
|
26
|
+
if (!base) throw new Error('supermemory baseUrl boş');
|
|
27
|
+
const ctrl = new AbortController();
|
|
28
|
+
const timer = setTimeout(() => ctrl.abort(), timeoutMs || BASE_TIMEOUT_MS);
|
|
29
|
+
try {
|
|
30
|
+
const res = await fetch(base + path, {
|
|
31
|
+
method: 'POST',
|
|
32
|
+
headers: {
|
|
33
|
+
'content-type': 'application/json',
|
|
34
|
+
...(apiKey ? { authorization: 'Bearer ' + apiKey } : {}),
|
|
35
|
+
},
|
|
36
|
+
body: JSON.stringify(body || {}),
|
|
37
|
+
signal: ctrl.signal,
|
|
38
|
+
});
|
|
39
|
+
if (!res.ok) {
|
|
40
|
+
const txt = await res.text().catch(() => '');
|
|
41
|
+
throw new Error('HTTP ' + res.status + (txt ? ': ' + txt.slice(0, 180) : ''));
|
|
42
|
+
}
|
|
43
|
+
return await res.json();
|
|
44
|
+
} finally {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/* Sunucu ayakta mı? GET / ile ucuz sağlık kontrolü */
|
|
50
|
+
async function probe(baseUrl, apiKey) {
|
|
51
|
+
const base = normalizeBase(baseUrl);
|
|
52
|
+
if (!base) return false;
|
|
53
|
+
const ctrl = new AbortController();
|
|
54
|
+
const timer = setTimeout(() => ctrl.abort(), PROBE_TIMEOUT_MS);
|
|
55
|
+
try {
|
|
56
|
+
const res = await fetch(base, {
|
|
57
|
+
method: 'GET',
|
|
58
|
+
headers: apiKey ? { authorization: 'Bearer ' + apiKey } : {},
|
|
59
|
+
signal: ctrl.signal,
|
|
60
|
+
});
|
|
61
|
+
return res.status < 500;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
} finally {
|
|
65
|
+
clearTimeout(timer);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Konuşma/belgi yut — arka planda çağrılır (fire-and-forget desenine uygun) */
|
|
70
|
+
async function add(baseUrl, apiKey, { content, containerTag, customId, metadata }) {
|
|
71
|
+
return call(baseUrl, apiKey, '/v3/documents', {
|
|
72
|
+
content: String(content || ''),
|
|
73
|
+
containerTag: String(containerTag || 'beast'),
|
|
74
|
+
...(customId ? { customId: String(customId) } : {}),
|
|
75
|
+
...(metadata && typeof metadata === 'object' ? { metadata } : {}),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Hibrit arama → normalize: [{text, similarity}] — memories varsayılan;
|
|
80
|
+
'hybrid' RAG + bellek tek sorguda */
|
|
81
|
+
async function search(baseUrl, apiKey, { q, containerTag, limit, mode }) {
|
|
82
|
+
const r = await call(
|
|
83
|
+
baseUrl,
|
|
84
|
+
apiKey,
|
|
85
|
+
'/v4/search',
|
|
86
|
+
{
|
|
87
|
+
q: String(q || ''),
|
|
88
|
+
containerTag: String(containerTag || 'beast'),
|
|
89
|
+
searchMode: mode === 'documents' || mode === 'hybrid' ? mode : 'memories',
|
|
90
|
+
limit: Math.min(20, Math.max(1, Number(limit) || 5)),
|
|
91
|
+
},
|
|
92
|
+
12000
|
|
93
|
+
);
|
|
94
|
+
const out = [];
|
|
95
|
+
for (const hit of (r && Array.isArray(r.results) ? r.results : []).slice(0, 10)) {
|
|
96
|
+
if (hit && typeof hit.memory === 'string' && hit.memory.trim()) {
|
|
97
|
+
out.push({ text: hit.memory.trim().slice(0, 600), similarity: Number(hit.similarity) || 0 });
|
|
98
|
+
}
|
|
99
|
+
/* hybrid mod: chunk'lar da gelir */
|
|
100
|
+
for (const c of (hit && Array.isArray(hit.chunks) ? hit.chunks : []).slice(0, 3)) {
|
|
101
|
+
const t = c && (c.content || c.text);
|
|
102
|
+
if (typeof t === 'string' && t.trim()) {
|
|
103
|
+
out.push({ text: t.trim().slice(0, 600), similarity: Number(c && c.similarity) || 0 });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return out.slice(0, Math.max(1, Number(limit) || 5));
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/* Kullanıcı profili: { static: [], dynamic: [] } — her turda ucuz bağlam */
|
|
111
|
+
async function profile(baseUrl, apiKey, { containerTag, q }) {
|
|
112
|
+
const r = await call(
|
|
113
|
+
baseUrl,
|
|
114
|
+
apiKey,
|
|
115
|
+
'/v4/profile',
|
|
116
|
+
{
|
|
117
|
+
containerTag: String(containerTag || 'beast'),
|
|
118
|
+
...(q ? { q: String(q) } : {}),
|
|
119
|
+
},
|
|
120
|
+
12000
|
|
121
|
+
);
|
|
122
|
+
const p = (r && r.profile) || {};
|
|
123
|
+
const arr = (x) =>
|
|
124
|
+
Array.isArray(x)
|
|
125
|
+
? x.map((s) => String(s || '').trim()).filter(Boolean).slice(0, 12)
|
|
126
|
+
: typeof x === 'string' && x.trim()
|
|
127
|
+
? [x.trim().slice(0, 300)]
|
|
128
|
+
: [];
|
|
129
|
+
return { static: arr(p.static), dynamic: arr(p.dynamic) };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
module.exports = { probe, add, search, profile, HEALTH_COOLDOWN_MS };
|