@semalt-ai/code 1.8.0 → 1.8.3
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/.claude/settings.local.json +14 -1
- package/CLAUDE.md +2 -1
- package/index.js +15 -1
- package/lib/agent.js +607 -77
- package/lib/api.js +240 -23
- package/lib/commands.js +105 -81
- package/lib/config.js +32 -4
- package/lib/constants.js +67 -1
- package/lib/metrics.js +16 -3
- package/lib/permissions.js +66 -67
- package/lib/prompts.js +97 -83
- package/lib/tool_specs.js +499 -0
- package/lib/tools.js +645 -319
- package/lib/ui/ansi.js +17 -4
- package/lib/ui/chat-history.js +201 -61
- package/lib/ui/create-ui.js +116 -373
- package/lib/ui/diff.js +87 -75
- package/lib/ui/input-field.js +76 -58
- package/lib/ui/status-bar.js +56 -25
- package/lib/ui/terminal.js +58 -0
- package/lib/ui/theme.js +78 -0
- package/lib/ui/utils.js +63 -1
- package/lib/ui/writer.js +255 -0
- package/lib/ui.js +5 -0
- package/package.json +1 -1
package/lib/permissions.js
CHANGED
|
@@ -8,7 +8,6 @@ const TIER_SYS = ['system_info', 'get_env', 'set_env'];
|
|
|
8
8
|
const TIER_MAP = { fs: TIER_FS, exec: TIER_EXEC, net: TIER_NET, sys: TIER_SYS };
|
|
9
9
|
const READONLY_BLOCKED = new Set(['write_file', 'append_file', 'delete_file', 'move_file', 'copy_file']);
|
|
10
10
|
|
|
11
|
-
let _permissionCounter = 0;
|
|
12
11
|
let _permissionQueueTail = Promise.resolve();
|
|
13
12
|
|
|
14
13
|
function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {}) {
|
|
@@ -21,8 +20,8 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
const state = {
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
autoApproveAll: false,
|
|
24
|
+
sessionApprovedTags: new Set(),
|
|
26
25
|
};
|
|
27
26
|
|
|
28
27
|
let uiCallbacks = null;
|
|
@@ -40,92 +39,96 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
40
39
|
|
|
41
40
|
const MAX_DESC_LINES = 12;
|
|
42
41
|
|
|
43
|
-
|
|
42
|
+
// The picker renders into the writer's modal region — a live band above
|
|
43
|
+
// the status bar that redraws in place on every keystroke. Arrow-key
|
|
44
|
+
// navigation rebuilds the lines array and calls onShowModal again; nothing
|
|
45
|
+
// lands in scrollback until the user confirms. On resolve/cancel the
|
|
46
|
+
// modal is cleared and a single summary line is emitted to scrollback
|
|
47
|
+
// (for multi-line descriptions — e.g. a file-write diff — the full body
|
|
48
|
+
// is retained so the user can still see what was approved).
|
|
49
|
+
function requestPermission(description, onShowModal, onCloseModal, onCaptureNavigation) {
|
|
44
50
|
// Serialize dialogs: each permission waits for the previous one to be answered
|
|
45
51
|
const myTurn = _permissionQueueTail;
|
|
46
52
|
let releaseQueue;
|
|
47
53
|
_permissionQueueTail = new Promise((r) => { releaseQueue = r; });
|
|
48
54
|
|
|
49
55
|
return myTurn.then(() => new Promise((resolve) => {
|
|
50
|
-
const uniqueId = `perm_${++_permissionCounter}`;
|
|
51
56
|
const options = ['Yes', 'Always', 'No'];
|
|
52
57
|
let selectedIdx = 0;
|
|
53
58
|
const descLines = description.split('\n');
|
|
54
59
|
const truncatable = descLines.length > MAX_DESC_LINES;
|
|
55
60
|
|
|
56
|
-
function
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
function buildModalLines() {
|
|
62
|
+
const lines = [];
|
|
63
|
+
const visible = truncatable
|
|
64
|
+
? descLines.slice(0, MAX_DESC_LINES).concat([` \x1b[2m… ${descLines.length - MAX_DESC_LINES} more lines\x1b[0m`])
|
|
65
|
+
: descLines;
|
|
66
|
+
// First description line gets the bullet glyph; continuation lines
|
|
67
|
+
// are indented to align under it. Matches the pre-modal rendering
|
|
68
|
+
// that went through chatHistory's system-message renderer.
|
|
69
|
+
const first = visible[0] || '';
|
|
70
|
+
lines.push(` \x1b[38;5;244m●\x1b[0m \x1b[38;5;244m${first}\x1b[0m`);
|
|
71
|
+
for (let i = 1; i < visible.length; i++) {
|
|
72
|
+
lines.push(` \x1b[38;5;244m${visible[i]}\x1b[0m`);
|
|
63
73
|
}
|
|
64
|
-
|
|
74
|
+
lines.push('');
|
|
65
75
|
for (let i = 0; i < options.length; i++) {
|
|
66
|
-
|
|
76
|
+
lines.push(i === selectedIdx
|
|
67
77
|
? `\x1b[1m\x1b[36m ► ${options[i]}\x1b[0m`
|
|
68
78
|
: ` ${options[i]}`
|
|
69
79
|
);
|
|
70
80
|
}
|
|
71
|
-
return
|
|
81
|
+
return lines;
|
|
72
82
|
}
|
|
73
83
|
|
|
74
|
-
|
|
75
|
-
|
|
84
|
+
onShowModal(buildModalLines());
|
|
85
|
+
|
|
86
|
+
function finish(result) {
|
|
87
|
+
const chosen = result === 'cancel' ? 'no' : options[selectedIdx].toLowerCase();
|
|
88
|
+
const glyph = (chosen === 'no') ? '✗' : '✓';
|
|
89
|
+
// The full `description` is preserved in the summary so multi-line
|
|
90
|
+
// bodies (e.g. file-write diffs) remain visible in scrollback after
|
|
91
|
+
// the modal closes. chatHistory's system-message renderer styles the
|
|
92
|
+
// first line by the leading glyph and indents continuations.
|
|
93
|
+
onCloseModal(`${glyph} ${description}`);
|
|
94
|
+
releaseQueue();
|
|
95
|
+
resolve(chosen);
|
|
96
|
+
}
|
|
76
97
|
|
|
77
98
|
const releaseNav = onCaptureNavigation((action) => {
|
|
78
99
|
if (action === 'next') {
|
|
79
100
|
selectedIdx = (selectedIdx + 1) % options.length;
|
|
80
|
-
|
|
81
|
-
onRerenderMessage(uniqueId);
|
|
101
|
+
onShowModal(buildModalLines());
|
|
82
102
|
} else if (action === 'prev') {
|
|
83
103
|
selectedIdx = (selectedIdx - 1 + options.length) % options.length;
|
|
84
|
-
|
|
85
|
-
onRerenderMessage(uniqueId);
|
|
104
|
+
onShowModal(buildModalLines());
|
|
86
105
|
} else if (action === 'select') {
|
|
87
|
-
const chosen = options[selectedIdx];
|
|
88
106
|
releaseNav();
|
|
89
|
-
|
|
90
|
-
onCollapseMessage(uniqueId);
|
|
91
|
-
releaseQueue();
|
|
92
|
-
resolve(chosen.toLowerCase());
|
|
107
|
+
finish('select');
|
|
93
108
|
} else if (action === 'cancel') {
|
|
94
109
|
releaseNav();
|
|
95
|
-
|
|
96
|
-
onCollapseMessage(uniqueId);
|
|
97
|
-
releaseQueue();
|
|
98
|
-
resolve('no');
|
|
110
|
+
finish('cancel');
|
|
99
111
|
}
|
|
100
112
|
});
|
|
101
113
|
}));
|
|
102
114
|
}
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
if (
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
console.log(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approved: ${description}${RST}`);
|
|
110
|
-
}
|
|
111
|
-
return true;
|
|
116
|
+
function _emitAutoApproved(description) {
|
|
117
|
+
if (uiCallbacks) {
|
|
118
|
+
uiCallbacks.onAddMessage({ role: 'system', content: `✓ Auto-approved: ${description}` });
|
|
119
|
+
} else {
|
|
120
|
+
console.log(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approved: ${description}${RST}`);
|
|
112
121
|
}
|
|
122
|
+
}
|
|
113
123
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
} else {
|
|
118
|
-
console.log(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approved: ${description}${RST}`);
|
|
119
|
-
}
|
|
124
|
+
async function askPermission(actionType, description, tag) {
|
|
125
|
+
if (state.autoApproveAll) {
|
|
126
|
+
_emitAutoApproved(description);
|
|
120
127
|
return true;
|
|
121
128
|
}
|
|
122
129
|
|
|
123
|
-
if (
|
|
124
|
-
|
|
125
|
-
uiCallbacks.onAddMessage({ role: 'system', content: `✓ Auto-approved: ${description}` });
|
|
126
|
-
} else {
|
|
127
|
-
console.log(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approved: ${description}${RST}`);
|
|
128
|
-
}
|
|
130
|
+
if (tag && (autoApprovedTags.has(tag) || state.sessionApprovedTags.has(tag))) {
|
|
131
|
+
_emitAutoApproved(description);
|
|
129
132
|
return true;
|
|
130
133
|
}
|
|
131
134
|
|
|
@@ -137,25 +140,23 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
137
140
|
if (uiCallbacks) {
|
|
138
141
|
const result = await requestPermission(
|
|
139
142
|
`${actionType}: ${description}`,
|
|
140
|
-
uiCallbacks.
|
|
141
|
-
uiCallbacks.
|
|
142
|
-
uiCallbacks.onCollapseMessage,
|
|
143
|
+
uiCallbacks.onShowModal,
|
|
144
|
+
uiCallbacks.onCloseModal,
|
|
143
145
|
uiCallbacks.onCaptureNavigation,
|
|
144
146
|
);
|
|
145
147
|
|
|
146
148
|
if (result === 'no') return false;
|
|
147
149
|
|
|
148
|
-
if (result === 'always') {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
uiCallbacks.onAddMessage({ role: 'system', content: `✓ Auto-approve enabled for ${actionType} operations` });
|
|
150
|
+
if (result === 'always' && tag) {
|
|
151
|
+
state.sessionApprovedTags.add(tag);
|
|
152
|
+
uiCallbacks.onAddMessage({ role: 'system', content: `✓ Auto-approve enabled for \`${tag}\` this session` });
|
|
152
153
|
}
|
|
153
154
|
|
|
154
155
|
return true;
|
|
155
156
|
}
|
|
156
157
|
|
|
157
158
|
// Fallback: legacy TTY interactive select (used outside of chat UI)
|
|
158
|
-
const alwaysLabel =
|
|
159
|
+
const alwaysLabel = tag ? `Yes, always for <${tag}>` : 'Yes, always';
|
|
159
160
|
const choices = ['Yes', alwaysLabel, 'No'];
|
|
160
161
|
|
|
161
162
|
console.log();
|
|
@@ -177,10 +178,9 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
177
178
|
return false;
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
if (selectedIndex === 1) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
console.log(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approve enabled for ${actionType} operations${RST}`);
|
|
181
|
+
if (selectedIndex === 1 && tag) {
|
|
182
|
+
state.sessionApprovedTags.add(tag);
|
|
183
|
+
console.log(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approve enabled for <${tag}> this session${RST}`);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
186
|
return true;
|
|
@@ -194,14 +194,13 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
function clear() {
|
|
197
|
-
state.
|
|
198
|
-
state.
|
|
197
|
+
state.autoApproveAll = false;
|
|
198
|
+
state.sessionApprovedTags.clear();
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
function toggleAll() {
|
|
202
|
-
state.
|
|
203
|
-
|
|
204
|
-
return state.autoApproveShell;
|
|
202
|
+
state.autoApproveAll = !state.autoApproveAll;
|
|
203
|
+
return state.autoApproveAll;
|
|
205
204
|
}
|
|
206
205
|
|
|
207
206
|
return {
|
package/lib/prompts.js
CHANGED
|
@@ -1,105 +1,119 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
3
|
+
const { TAG_REGISTRY } = require('./constants');
|
|
4
|
+
|
|
5
|
+
const WRAPPER_NAMES = new Set([
|
|
6
|
+
'minimax:tool_call',
|
|
7
|
+
'qwen:tool_call',
|
|
8
|
+
'invoke',
|
|
9
|
+
'parameter',
|
|
10
|
+
'tool_call',
|
|
11
|
+
'function_call',
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
// For each tool tag: required attributes and a one-line purpose.
|
|
15
|
+
// Required attributes are derived from the matchers in `extractToolCalls`
|
|
16
|
+
// (lib/tools.js). Where a tag accepts either an attribute or inline content,
|
|
17
|
+
// the attribute is marked optional.
|
|
18
|
+
const TOOL_TAG_SPECS = {
|
|
19
|
+
exec: { attrs: [], purpose: 'Run a shell command (inline content).' },
|
|
20
|
+
shell: { attrs: [], purpose: 'Run a shell command (inline content).' },
|
|
21
|
+
read_file: { attrs: ['path?'], purpose: 'Read a file (path attr or inline content).' },
|
|
22
|
+
write_file: { attrs: ['path'], purpose: 'Write file with inline content (overwrites).' },
|
|
23
|
+
create_file: { attrs: ['path'], purpose: 'Create file with inline content.' },
|
|
24
|
+
append_file: { attrs: ['path'], purpose: 'Append inline content to file.' },
|
|
25
|
+
delete_file: { attrs: [], purpose: 'Delete a file (inline content = path).' },
|
|
26
|
+
list_dir: { attrs: [], purpose: 'List directory contents (inline content = path).' },
|
|
27
|
+
make_dir: { attrs: [], purpose: 'Create directory recursively (inline content = path).' },
|
|
28
|
+
remove_dir: { attrs: [], purpose: 'Remove directory recursively (inline content = path).' },
|
|
29
|
+
move_file: { attrs: ['src', 'dst'], purpose: 'Move or rename a file.' },
|
|
30
|
+
copy_file: { attrs: ['src', 'dst'], purpose: 'Copy a file.' },
|
|
31
|
+
file_stat: { attrs: [], purpose: 'Stat a file (inline content = path).' },
|
|
32
|
+
edit_file: { attrs: ['path', 'line'], purpose: 'Replace a single line in a file (inline content = new line).' },
|
|
33
|
+
search_files: { attrs: ['pattern?', 'dir?'], purpose: 'Find files by glob pattern.' },
|
|
34
|
+
search_in_file: { attrs: ['path'], purpose: 'Regex search inside a file (inline content = pattern).' },
|
|
35
|
+
replace_in_file: { attrs: ['path', 'search', 'replace'], purpose: 'Regex replace inside a file.' },
|
|
36
|
+
get_env: { attrs: [], purpose: 'Read an env var (inline content = name).' },
|
|
37
|
+
set_env: { attrs: ['name', 'value'], purpose: 'Set an env var for this process.' },
|
|
38
|
+
download: { attrs: [], purpose: 'HTTP download to the CWD (inline content = URL).' },
|
|
39
|
+
upload: { attrs: ['path'], purpose: 'Write base64-encoded content to file.' },
|
|
40
|
+
http_get: { attrs: ['url'], purpose: 'HTTP GET; returns the response body (truncated to a byte cap with an explicit notice when oversized).' },
|
|
41
|
+
ask_user: { attrs: ['question'], purpose: 'Ask the user a question and receive an answer.' },
|
|
42
|
+
store_memory: { attrs: ['key'], purpose: 'Persist a key/value to local memory (inline content = value).' },
|
|
43
|
+
recall_memory: { attrs: ['key'], purpose: 'Read a key from local memory.' },
|
|
44
|
+
list_memories: { attrs: [], purpose: 'List memory keys.' },
|
|
45
|
+
system_info: { attrs: [], purpose: 'Return platform, arch, host, memory, node version, cwd.' },
|
|
46
|
+
};
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
function buildTagInventory() {
|
|
49
|
+
const lines = [];
|
|
50
|
+
for (const tag of Object.keys(TAG_REGISTRY)) {
|
|
51
|
+
const entry = TAG_REGISTRY[tag];
|
|
52
|
+
if (entry.type !== 'tool') continue;
|
|
53
|
+
if (WRAPPER_NAMES.has(tag)) continue;
|
|
54
|
+
const spec = TOOL_TAG_SPECS[tag] || { attrs: [], purpose: '' };
|
|
55
|
+
const attrPart = spec.attrs.length
|
|
56
|
+
? ` [attrs: ${spec.attrs.join(', ')}]`
|
|
57
|
+
: '';
|
|
58
|
+
lines.push(`- <${tag}>${attrPart} — ${spec.purpose}`);
|
|
59
|
+
}
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
|
48
62
|
|
|
49
|
-
|
|
50
|
-
<edit_file path="/path/to/file" line="42">replacement line content</edit_file>
|
|
63
|
+
const TAG_INVENTORY = buildTagInventory();
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
<http_get url="https://example.com/api/data"></http_get>
|
|
65
|
+
const SYSTEM_PROMPT_TEMPLATE = `You are Semalt.AI, an expert AI coding assistant running in the user's terminal. You have the ability to execute shell commands and file operations.
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
<http_get url="https://example.com/" raw="true"></http_get>
|
|
67
|
+
## Available tool tags:
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
<http_get_next key="https://example.com/api/data"/>
|
|
69
|
+
${TAG_INVENTORY}
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
<download>https://example.com/file.zip</download>
|
|
71
|
+
## Tool call syntax — use EXACTLY these forms:
|
|
63
72
|
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
- Single-value inline-content tags: put the value directly between the tags as plain text. **Do NOT nest pseudo-tags like \`<path>\`, \`<command>\`, \`<url>\`, \`<key>\`, \`<name>\`, \`<pattern>\`, or \`<question>\` inside the body** — the parser treats the body as the literal value.
|
|
74
|
+
- Correct: \`<list_dir>/tmp/foo</list_dir>\`, \`<shell>ls -la</shell>\`, \`<read_file>/etc/hosts</read_file>\`, \`<download>https://x.com/f.zip</download>\`
|
|
75
|
+
- Wrong: \`<list_dir><path>/tmp/foo</path></list_dir>\`, \`<shell><command>ls</command></shell>\`
|
|
76
|
+
- Attribute tags: parameters go as quoted attributes on the opening tag; the body is either empty (self-closed) or real payload content (e.g. file bytes for \`write_file\`).
|
|
77
|
+
- Correct: \`<write_file path="/tmp/a.txt">actual file contents here</write_file>\`, \`<http_get url="https://example.com"/>\`, \`<move_file src="/a" dst="/b"/>\`
|
|
78
|
+
- Wrong: \`<write_file><path>/tmp/a.txt</path><content>...</content></write_file>\`
|
|
66
79
|
|
|
67
|
-
|
|
68
|
-
<ask_user question="Which directory should I use?"></ask_user>
|
|
80
|
+
## Reasoning vs planning — IMPORTANT:
|
|
69
81
|
|
|
70
|
-
|
|
71
|
-
|
|
82
|
+
- Your internal chain-of-thought reasoning uses your native \`<think>...</think>\` block. Use it normally for deliberation. Do NOT treat \`<think>\` as a user-facing tool and do NOT try to emit \`<think>\` as an action — it is reserved for your own reasoning and is handled by the runtime.
|
|
83
|
+
- When you need to explicitly record a short plan that the agent framework can see (for logging or hand-off between steps), use \`<plan>...</plan>\` instead. \`<plan>\` is a tool tag; \`<think>\` is not.
|
|
84
|
+
- Never emit \`<think>\` as an action. The valid action tags are the ones listed above.
|
|
72
85
|
|
|
73
|
-
|
|
74
|
-
<recall_memory key="project_name"></recall_memory>
|
|
86
|
+
## STRICT RULES — follow exactly:
|
|
75
87
|
|
|
76
|
-
|
|
77
|
-
|
|
88
|
+
1. Emit tool tags as needed. If a step depends on a prior tool's result, wait for the result before the next call. Independent tool calls may be batched in a single response.
|
|
89
|
+
2. **Always end your response with exactly one action tag** whenever the task is not yet complete. Do NOT end a response with only prose like "Let me create the files…" — that prose must be immediately followed by the actual tool tag in the same response. Preamble without a tool tag is a bug.
|
|
90
|
+
3. Only use the exact tag names listed above. Unknown tag names will not be executed.
|
|
91
|
+
4. When the user asks you to do something on their system, USE the tools — do NOT just print instructions.
|
|
92
|
+
5. Each action will be shown to the user for approval before execution.
|
|
93
|
+
6. After execution you will receive the result and can proceed with the next action in a new response.
|
|
94
|
+
7. Be concise. Provide working solutions. Use markdown for code blocks in explanations.
|
|
95
|
+
8. Current working directory: __CWD__
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
97
|
+
Response contract (strict):
|
|
98
|
+
Every response must end with exactly one of:
|
|
99
|
+
(a) a tool call, using one of the XML tags listed above; OR
|
|
100
|
+
(b) <final_answer>...</final_answer> containing your answer to the user.
|
|
101
|
+
A response containing neither is invalid and will be rejected.
|
|
102
|
+
Do not describe actions in prose outside these two forms. Either call the tool, or wrap your final reply in <final_answer>.`;
|
|
81
103
|
|
|
82
|
-
|
|
83
|
-
<get_env>VARIABLE_NAME</get_env>
|
|
104
|
+
const NATIVE_SYSTEM_PROMPT_TEMPLATE = `You are Semalt.AI, an expert AI coding assistant running in the user's terminal. Use the provided tools to execute shell commands and file operations; do not just print instructions. Each call is approved by the user before execution, and the result is returned to you for the next step.
|
|
84
105
|
|
|
85
|
-
|
|
86
|
-
<set_env name="VARIABLE_NAME" value="value"/>
|
|
106
|
+
Use \`<think>...</think>\` for internal reasoning (runtime-handled; never emit as an action). Use \`<plan>...</plan>\` to record a short plan for the agent framework.
|
|
87
107
|
|
|
88
|
-
|
|
89
|
-
<think>your reasoning here</think>
|
|
108
|
+
Be concise. Use markdown for code blocks in explanations. Current working directory: __CWD__
|
|
90
109
|
|
|
91
|
-
|
|
110
|
+
Response contract: every response must end with either (a) one or more tool calls, or (b) <final_answer>...</final_answer> containing your answer to the user. Prose outside those two forms is invalid.`;
|
|
92
111
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
5. After execution you will receive the result and can proceed.
|
|
98
|
-
6. Be concise. Provide working solutions. Use markdown for code blocks in explanations.
|
|
99
|
-
7. **Permission denied fallback:** If any tool action (write_file, delete_file, make_dir, etc.) is denied or returns a permission error, immediately retry the same operation using \`<exec>\` with an equivalent shell command (e.g. \`tee\`, \`rm\`, \`mkdir -p\`, \`cp\`, \`mv\`). Never give up after a single denial — the shell is always the fallback.
|
|
100
|
-
8. If a shell command itself also fails with a permission error, immediately retry it prefixed with \`sudo\` (e.g. \`sudo tee /path\`, \`sudo mkdir -p /path\`). Do not ask — just try. Only stop and explain if sudo itself fails.
|
|
101
|
-
9. Current working directory: ${process.cwd()}`;
|
|
112
|
+
function getSystemPrompt(nativeTools = false) {
|
|
113
|
+
const template = nativeTools ? NATIVE_SYSTEM_PROMPT_TEMPLATE : SYSTEM_PROMPT_TEMPLATE;
|
|
114
|
+
return template.replace('__CWD__', process.cwd());
|
|
115
|
+
}
|
|
102
116
|
|
|
103
117
|
module.exports = {
|
|
104
|
-
|
|
118
|
+
getSystemPrompt,
|
|
105
119
|
};
|