Further Agent Harness Fixes (write_file) and (create_directory) tools updates
Release rolling / release (push) Successful in 12m52s
Release rolling / release (push) Successful in 12m52s
This commit is contained in:
+206
-18
@@ -1193,6 +1193,59 @@ function bareAgentNormalizeAbsPath(absPath) {
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand ~ / $HOME / relative guest paths to an absolute VFS path.
|
||||
* @param {string} raw
|
||||
* @param {string} home
|
||||
* @param {string} [cwd]
|
||||
*/
|
||||
function bareAgentExpandGuestPath(raw, home, cwd) {
|
||||
let p = String(raw == null ? '' : raw).trim()
|
||||
if (!p) return ''
|
||||
p = p.replace(/\\/g, '/')
|
||||
const h = String(home || '').replace(/\/+$/, '') || '/home/guest'
|
||||
let c = String(cwd || h).replace(/\/+$/, '') || h
|
||||
if (c === '~') c = h
|
||||
else if (c.startsWith('~/')) c = h + c.slice(1)
|
||||
else if (c.startsWith('$HOME/')) c = h + c.slice(5)
|
||||
else if (c === '$HOME') c = h
|
||||
if (p === '~' || p === '$HOME') return h
|
||||
if (p.startsWith('~/')) {
|
||||
p = h + '/' + p.slice(2)
|
||||
} else if (p.startsWith('$HOME/')) {
|
||||
p = h + '/' + p.slice(6)
|
||||
} else if (!p.startsWith('/')) {
|
||||
p = (c === '/' ? '/' + p : c + '/' + p)
|
||||
}
|
||||
p = p.replace(/\/{2,}/g, '/')
|
||||
if (p.length > 1) p = p.replace(/\/+$/, '')
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a path-like argument from a tool-call blob (path / file_path / dir / …).
|
||||
* @param {Record<string, unknown>} args
|
||||
*/
|
||||
function bareAgentPickPathArg(args) {
|
||||
if (!args || typeof args !== 'object') return ''
|
||||
const keys = [
|
||||
'path',
|
||||
'file_path',
|
||||
'file',
|
||||
'filename',
|
||||
'dir',
|
||||
'directory',
|
||||
'folder',
|
||||
'from_path',
|
||||
'to_path'
|
||||
]
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const v = args[keys[i]]
|
||||
if (typeof v === 'string' && v.trim()) return v.trim()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} absPath
|
||||
* @param {unknown} prefixes
|
||||
@@ -6927,7 +6980,7 @@ function bareAgentToolDefinitions() {
|
||||
function: {
|
||||
name: 'read_file',
|
||||
description:
|
||||
'Read a UTF-8 text file from the VFS. Path must be absolute (e.g. /home/guest/...). Optional offset/limit return numbered line slices (Grok-style).',
|
||||
'Read a UTF-8 text file from the VFS. Path may be absolute or ~/... (tilde is expanded). Optional offset/limit return numbered line slices (Grok-style).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -6959,11 +7012,12 @@ function bareAgentToolDefinitions() {
|
||||
function: {
|
||||
name: 'write_file',
|
||||
description:
|
||||
'Create a new file or overwrite an existing one at any writable guest path. Parent directories are created as needed. Do not ask permission.',
|
||||
'Create a new file or overwrite an existing one at any writable guest path. Path may be absolute or ~/... (tilde is expanded to the session home). Parent directories are created as needed. Returns the expanded path — verify with list_directory or file_stat before claiming the file exists. Do not ask permission.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' },
|
||||
path: { type: 'string', description: 'Absolute path or ~/file' },
|
||||
file_path: { type: 'string', description: 'Alias for path' },
|
||||
content: { type: 'string', description: 'Full file contents' }
|
||||
},
|
||||
required: ['path', 'content']
|
||||
@@ -7003,11 +7057,13 @@ function bareAgentToolDefinitions() {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'create_directory',
|
||||
description: 'Create a directory (recursive).',
|
||||
description:
|
||||
'Create a directory (recursive). Path may be absolute or ~/... (tilde is expanded). Returns the expanded path — list_directory it before claiming it exists.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' }
|
||||
path: { type: 'string', description: 'Absolute path or ~/dir' },
|
||||
dir: { type: 'string', description: 'Alias for path' }
|
||||
},
|
||||
required: ['path']
|
||||
}
|
||||
@@ -8582,6 +8638,54 @@ async function bareAgentDispatchTool(o) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_tool_arguments_json' })
|
||||
}
|
||||
const cfgNow = configRef.current || {}
|
||||
const resolvedHome =
|
||||
String(home || '').trim() ||
|
||||
(typeof bareAgentResolveHome === 'function' ? bareAgentResolveHome(ctx) : '') ||
|
||||
'/home/guest'
|
||||
if (typeof bareAgentExpandGuestPath === 'function') {
|
||||
const PATH_KEYS = [
|
||||
'path',
|
||||
'file_path',
|
||||
'file',
|
||||
'filename',
|
||||
'dir',
|
||||
'directory',
|
||||
'folder',
|
||||
'root',
|
||||
'cwd',
|
||||
'from_path',
|
||||
'to_path',
|
||||
'from',
|
||||
'to',
|
||||
'src',
|
||||
'source',
|
||||
'dest',
|
||||
'destination',
|
||||
'target',
|
||||
'old_path',
|
||||
'new_path'
|
||||
]
|
||||
let cwdForExpand = resolvedHome
|
||||
if (paths && paths.lastCwd && typeof bareAgentReadTextFile === 'function') {
|
||||
try {
|
||||
const t = String(await bareAgentReadTextFile(ctx, paths.lastCwd) || '').trim()
|
||||
if (t) cwdForExpand = t
|
||||
} catch {
|
||||
/* optional */
|
||||
}
|
||||
}
|
||||
if (typeof args.cwd === 'string' && args.cwd.trim()) {
|
||||
args.cwd = bareAgentExpandGuestPath(args.cwd, resolvedHome, resolvedHome)
|
||||
cwdForExpand = String(args.cwd || cwdForExpand)
|
||||
}
|
||||
for (let i = 0; i < PATH_KEYS.length; i++) {
|
||||
const k = PATH_KEYS[i]
|
||||
if (k === 'cwd') continue
|
||||
if (typeof args[k] === 'string' && args[k].trim()) {
|
||||
args[k] = bareAgentExpandGuestPath(args[k], resolvedHome, cwdForExpand)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
!internalGate &&
|
||||
cfgNow.autonomous_active &&
|
||||
@@ -9335,13 +9439,37 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'write_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
const path =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.file_path === 'string' && args.file_path.trim()) ||
|
||||
(typeof args.file === 'string' && args.file.trim()) ||
|
||||
(typeof bareAgentPickPathArg === 'function' ? bareAgentPickPathArg(args) : '') ||
|
||||
''
|
||||
if (!enforceAutonomousPath(path, 'mutate')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed', path })
|
||||
}
|
||||
const content = typeof args.content === 'string' ? args.content : ''
|
||||
const content =
|
||||
typeof args.content === 'string'
|
||||
? args.content
|
||||
: typeof args.contents === 'string'
|
||||
? args.contents
|
||||
: typeof args.text === 'string'
|
||||
? args.text
|
||||
: typeof args.body === 'string'
|
||||
? args.body
|
||||
: ''
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'bad_path',
|
||||
path,
|
||||
hint:
|
||||
'Use an absolute path or ~/... (tilde expands to ' +
|
||||
resolvedHome +
|
||||
'). Example: ' +
|
||||
resolvedHome +
|
||||
'/test_work/hello.js'
|
||||
})
|
||||
}
|
||||
if (!vfs || typeof vfs.writeFile !== 'function' || typeof vfs.mkdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
@@ -9362,7 +9490,26 @@ async function bareAgentDispatchTool(o) {
|
||||
? ctx.b4a.from(content)
|
||||
: new TextEncoder().encode(content)
|
||||
await vfs.writeFile(path, body)
|
||||
return bareAgentJsonResult({ ok: true, bytes: body.length })
|
||||
let verified = false
|
||||
try {
|
||||
if (typeof vfs.lstat === 'function') verified = Boolean(await vfs.lstat(path))
|
||||
else if (typeof vfs.stat === 'function') verified = Boolean(await vfs.stat(path))
|
||||
else if (typeof vfs.readFile === 'function') {
|
||||
const got = await vfs.readFile(path)
|
||||
verified = got != null
|
||||
} else verified = true
|
||||
} catch {
|
||||
verified = false
|
||||
}
|
||||
if (!verified) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'write_not_visible',
|
||||
path,
|
||||
hint: 'write_file returned but the path is not visible in the VFS. Retry with the expanded absolute path.'
|
||||
})
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, path, bytes: body.length, verified: true })
|
||||
}
|
||||
|
||||
if (toolName === 'edit_file' || toolName === 'search_replace') {
|
||||
@@ -9459,19 +9606,54 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'create_directory') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
const path =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.dir === 'string' && args.dir.trim()) ||
|
||||
(typeof args.directory === 'string' && args.directory.trim()) ||
|
||||
(typeof args.folder === 'string' && args.folder.trim()) ||
|
||||
(typeof bareAgentPickPathArg === 'function' ? bareAgentPickPathArg(args) : '') ||
|
||||
''
|
||||
if (!enforceAutonomousPath(path, 'mutate')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed', path })
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'bad_path',
|
||||
path,
|
||||
hint:
|
||||
'Use an absolute path or ~/... (tilde expands to ' +
|
||||
resolvedHome +
|
||||
'). Example: ' +
|
||||
resolvedHome +
|
||||
'/test_work'
|
||||
})
|
||||
}
|
||||
if (!vfs || typeof vfs.mkdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
appendProgress('mkdir ' + path)
|
||||
await vfs.mkdir(path, { recursive: true })
|
||||
return bareAgentJsonResult({ ok: true })
|
||||
let verified = false
|
||||
try {
|
||||
if (typeof vfs.lstat === 'function') verified = Boolean(await vfs.lstat(path))
|
||||
else if (typeof vfs.stat === 'function') verified = Boolean(await vfs.stat(path))
|
||||
else if (typeof vfs.readdir === 'function') {
|
||||
await vfs.readdir(path)
|
||||
verified = true
|
||||
} else verified = true
|
||||
} catch {
|
||||
verified = false
|
||||
}
|
||||
if (!verified) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'mkdir_not_visible',
|
||||
path,
|
||||
hint: 'create_directory returned but the path is not visible. Retry with ' + path
|
||||
})
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, path, verified: true })
|
||||
}
|
||||
|
||||
if (toolName === 'search_files') {
|
||||
@@ -9733,7 +9915,10 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'list_directory') {
|
||||
const dir = typeof args.path === 'string' ? args.path : ''
|
||||
const dir =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.dir === 'string' && args.dir.trim()) ||
|
||||
resolvedHome
|
||||
const maxEnt =
|
||||
typeof args.max_entries === 'number' && Number.isFinite(args.max_entries)
|
||||
? Math.min(Math.floor(args.max_entries), 2000)
|
||||
@@ -13598,7 +13783,7 @@ WORK POLICY.
|
||||
|
||||
ACCESS (denylist, not allowlist). You already have full guest admin. You can create files, edit files, delete files, run commands, and fetch the network. NEVER ASK whether you may — just do it. Only refuse when a denylist or the read-only base system blocks the path.
|
||||
- Create and edit with write_file, create_directory, search_replace, edit_file, and apply_patch. Never say you cannot write files. Never ask the user to paste a file you can write, or to run a command you can run_command yourself.
|
||||
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
|
||||
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. The harness expands ~ and $HOME to the resolved home in This session. Never write /home/guest unless that is the resolved home. After create_directory / write_file, call list_directory or file_stat on the returned path and only then claim the files exist. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
|
||||
- Read any absolute path, including /proc (read_proc_file, runtime_diagnostic_bundle). Use read_file offset/limit for large files.
|
||||
- Run every guest command via run_command (command_deny is empty by default). Prefer list_directory, glob_files, and file_stat over ls/find when you only need names. list_bin lists guest /bin utilities (POSIX-in-JS, not GNU). Never dump curl/ip/ls for the user — call run_command and report stdout.
|
||||
- Delete and move are enabled. Do not mutate the read-only base system: /bin /etc /boot /lib /usr /share /proc /dev /sys /run.
|
||||
@@ -13712,7 +13897,10 @@ function bareAgentSessionHomeBlock(ctx, home, paths) {
|
||||
paths.config +
|
||||
'. Always expand ~ to ' +
|
||||
home +
|
||||
' when constructing absolute paths for tools.\n' +
|
||||
' when constructing absolute paths for tools. ~/test_work is ' +
|
||||
home +
|
||||
'/test_work. Never use /home/guest unless that is this resolved home.\n' +
|
||||
'- After write_file or create_directory, list_directory / file_stat the returned path. Do not claim files exist from the tool name alone.\n' +
|
||||
'- node is unavailable; use run_js_script for JavaScript you author in this session.\n'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -49,6 +49,59 @@ function bareAgentNormalizeAbsPath(absPath) {
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand ~ / $HOME / relative guest paths to an absolute VFS path.
|
||||
* @param {string} raw
|
||||
* @param {string} home
|
||||
* @param {string} [cwd]
|
||||
*/
|
||||
function bareAgentExpandGuestPath(raw, home, cwd) {
|
||||
let p = String(raw == null ? '' : raw).trim()
|
||||
if (!p) return ''
|
||||
p = p.replace(/\\/g, '/')
|
||||
const h = String(home || '').replace(/\/+$/, '') || '/home/guest'
|
||||
let c = String(cwd || h).replace(/\/+$/, '') || h
|
||||
if (c === '~') c = h
|
||||
else if (c.startsWith('~/')) c = h + c.slice(1)
|
||||
else if (c.startsWith('$HOME/')) c = h + c.slice(5)
|
||||
else if (c === '$HOME') c = h
|
||||
if (p === '~' || p === '$HOME') return h
|
||||
if (p.startsWith('~/')) {
|
||||
p = h + '/' + p.slice(2)
|
||||
} else if (p.startsWith('$HOME/')) {
|
||||
p = h + '/' + p.slice(6)
|
||||
} else if (!p.startsWith('/')) {
|
||||
p = (c === '/' ? '/' + p : c + '/' + p)
|
||||
}
|
||||
p = p.replace(/\/{2,}/g, '/')
|
||||
if (p.length > 1) p = p.replace(/\/+$/, '')
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a path-like argument from a tool-call blob (path / file_path / dir / …).
|
||||
* @param {Record<string, unknown>} args
|
||||
*/
|
||||
function bareAgentPickPathArg(args) {
|
||||
if (!args || typeof args !== 'object') return ''
|
||||
const keys = [
|
||||
'path',
|
||||
'file_path',
|
||||
'file',
|
||||
'filename',
|
||||
'dir',
|
||||
'directory',
|
||||
'folder',
|
||||
'from_path',
|
||||
'to_path'
|
||||
]
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const v = args[keys[i]]
|
||||
if (typeof v === 'string' && v.trim()) return v.trim()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} absPath
|
||||
* @param {unknown} prefixes
|
||||
|
||||
@@ -10,7 +10,7 @@ function bareAgentToolDefinitions() {
|
||||
function: {
|
||||
name: 'read_file',
|
||||
description:
|
||||
'Read a UTF-8 text file from the VFS. Path must be absolute (e.g. /home/guest/...). Optional offset/limit return numbered line slices (Grok-style).',
|
||||
'Read a UTF-8 text file from the VFS. Path may be absolute or ~/... (tilde is expanded). Optional offset/limit return numbered line slices (Grok-style).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -42,11 +42,12 @@ function bareAgentToolDefinitions() {
|
||||
function: {
|
||||
name: 'write_file',
|
||||
description:
|
||||
'Create a new file or overwrite an existing one at any writable guest path. Parent directories are created as needed. Do not ask permission.',
|
||||
'Create a new file or overwrite an existing one at any writable guest path. Path may be absolute or ~/... (tilde is expanded to the session home). Parent directories are created as needed. Returns the expanded path — verify with list_directory or file_stat before claiming the file exists. Do not ask permission.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' },
|
||||
path: { type: 'string', description: 'Absolute path or ~/file' },
|
||||
file_path: { type: 'string', description: 'Alias for path' },
|
||||
content: { type: 'string', description: 'Full file contents' }
|
||||
},
|
||||
required: ['path', 'content']
|
||||
@@ -86,11 +87,13 @@ function bareAgentToolDefinitions() {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'create_directory',
|
||||
description: 'Create a directory (recursive).',
|
||||
description:
|
||||
'Create a directory (recursive). Path may be absolute or ~/... (tilde is expanded). Returns the expanded path — list_directory it before claiming it exists.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' }
|
||||
path: { type: 'string', description: 'Absolute path or ~/dir' },
|
||||
dir: { type: 'string', description: 'Alias for path' }
|
||||
},
|
||||
required: ['path']
|
||||
}
|
||||
|
||||
@@ -37,6 +37,54 @@ async function bareAgentDispatchTool(o) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_tool_arguments_json' })
|
||||
}
|
||||
const cfgNow = configRef.current || {}
|
||||
const resolvedHome =
|
||||
String(home || '').trim() ||
|
||||
(typeof bareAgentResolveHome === 'function' ? bareAgentResolveHome(ctx) : '') ||
|
||||
'/home/guest'
|
||||
if (typeof bareAgentExpandGuestPath === 'function') {
|
||||
const PATH_KEYS = [
|
||||
'path',
|
||||
'file_path',
|
||||
'file',
|
||||
'filename',
|
||||
'dir',
|
||||
'directory',
|
||||
'folder',
|
||||
'root',
|
||||
'cwd',
|
||||
'from_path',
|
||||
'to_path',
|
||||
'from',
|
||||
'to',
|
||||
'src',
|
||||
'source',
|
||||
'dest',
|
||||
'destination',
|
||||
'target',
|
||||
'old_path',
|
||||
'new_path'
|
||||
]
|
||||
let cwdForExpand = resolvedHome
|
||||
if (paths && paths.lastCwd && typeof bareAgentReadTextFile === 'function') {
|
||||
try {
|
||||
const t = String(await bareAgentReadTextFile(ctx, paths.lastCwd) || '').trim()
|
||||
if (t) cwdForExpand = t
|
||||
} catch {
|
||||
/* optional */
|
||||
}
|
||||
}
|
||||
if (typeof args.cwd === 'string' && args.cwd.trim()) {
|
||||
args.cwd = bareAgentExpandGuestPath(args.cwd, resolvedHome, resolvedHome)
|
||||
cwdForExpand = String(args.cwd || cwdForExpand)
|
||||
}
|
||||
for (let i = 0; i < PATH_KEYS.length; i++) {
|
||||
const k = PATH_KEYS[i]
|
||||
if (k === 'cwd') continue
|
||||
if (typeof args[k] === 'string' && args[k].trim()) {
|
||||
args[k] = bareAgentExpandGuestPath(args[k], resolvedHome, cwdForExpand)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
!internalGate &&
|
||||
cfgNow.autonomous_active &&
|
||||
@@ -790,13 +838,37 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'write_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
const path =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.file_path === 'string' && args.file_path.trim()) ||
|
||||
(typeof args.file === 'string' && args.file.trim()) ||
|
||||
(typeof bareAgentPickPathArg === 'function' ? bareAgentPickPathArg(args) : '') ||
|
||||
''
|
||||
if (!enforceAutonomousPath(path, 'mutate')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed', path })
|
||||
}
|
||||
const content = typeof args.content === 'string' ? args.content : ''
|
||||
const content =
|
||||
typeof args.content === 'string'
|
||||
? args.content
|
||||
: typeof args.contents === 'string'
|
||||
? args.contents
|
||||
: typeof args.text === 'string'
|
||||
? args.text
|
||||
: typeof args.body === 'string'
|
||||
? args.body
|
||||
: ''
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'bad_path',
|
||||
path,
|
||||
hint:
|
||||
'Use an absolute path or ~/... (tilde expands to ' +
|
||||
resolvedHome +
|
||||
'). Example: ' +
|
||||
resolvedHome +
|
||||
'/test_work/hello.js'
|
||||
})
|
||||
}
|
||||
if (!vfs || typeof vfs.writeFile !== 'function' || typeof vfs.mkdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
@@ -817,7 +889,26 @@ async function bareAgentDispatchTool(o) {
|
||||
? ctx.b4a.from(content)
|
||||
: new TextEncoder().encode(content)
|
||||
await vfs.writeFile(path, body)
|
||||
return bareAgentJsonResult({ ok: true, bytes: body.length })
|
||||
let verified = false
|
||||
try {
|
||||
if (typeof vfs.lstat === 'function') verified = Boolean(await vfs.lstat(path))
|
||||
else if (typeof vfs.stat === 'function') verified = Boolean(await vfs.stat(path))
|
||||
else if (typeof vfs.readFile === 'function') {
|
||||
const got = await vfs.readFile(path)
|
||||
verified = got != null
|
||||
} else verified = true
|
||||
} catch {
|
||||
verified = false
|
||||
}
|
||||
if (!verified) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'write_not_visible',
|
||||
path,
|
||||
hint: 'write_file returned but the path is not visible in the VFS. Retry with the expanded absolute path.'
|
||||
})
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, path, bytes: body.length, verified: true })
|
||||
}
|
||||
|
||||
if (toolName === 'edit_file' || toolName === 'search_replace') {
|
||||
@@ -914,19 +1005,54 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'create_directory') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
const path =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.dir === 'string' && args.dir.trim()) ||
|
||||
(typeof args.directory === 'string' && args.directory.trim()) ||
|
||||
(typeof args.folder === 'string' && args.folder.trim()) ||
|
||||
(typeof bareAgentPickPathArg === 'function' ? bareAgentPickPathArg(args) : '') ||
|
||||
''
|
||||
if (!enforceAutonomousPath(path, 'mutate')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed', path })
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'bad_path',
|
||||
path,
|
||||
hint:
|
||||
'Use an absolute path or ~/... (tilde expands to ' +
|
||||
resolvedHome +
|
||||
'). Example: ' +
|
||||
resolvedHome +
|
||||
'/test_work'
|
||||
})
|
||||
}
|
||||
if (!vfs || typeof vfs.mkdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
appendProgress('mkdir ' + path)
|
||||
await vfs.mkdir(path, { recursive: true })
|
||||
return bareAgentJsonResult({ ok: true })
|
||||
let verified = false
|
||||
try {
|
||||
if (typeof vfs.lstat === 'function') verified = Boolean(await vfs.lstat(path))
|
||||
else if (typeof vfs.stat === 'function') verified = Boolean(await vfs.stat(path))
|
||||
else if (typeof vfs.readdir === 'function') {
|
||||
await vfs.readdir(path)
|
||||
verified = true
|
||||
} else verified = true
|
||||
} catch {
|
||||
verified = false
|
||||
}
|
||||
if (!verified) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'mkdir_not_visible',
|
||||
path,
|
||||
hint: 'create_directory returned but the path is not visible. Retry with ' + path
|
||||
})
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, path, verified: true })
|
||||
}
|
||||
|
||||
if (toolName === 'search_files') {
|
||||
@@ -1188,7 +1314,10 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'list_directory') {
|
||||
const dir = typeof args.path === 'string' ? args.path : ''
|
||||
const dir =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.dir === 'string' && args.dir.trim()) ||
|
||||
resolvedHome
|
||||
const maxEnt =
|
||||
typeof args.max_entries === 'number' && Number.isFinite(args.max_entries)
|
||||
? Math.min(Math.floor(args.max_entries), 2000)
|
||||
|
||||
@@ -260,7 +260,7 @@ WORK POLICY.
|
||||
|
||||
ACCESS (denylist, not allowlist). You already have full guest admin. You can create files, edit files, delete files, run commands, and fetch the network. NEVER ASK whether you may — just do it. Only refuse when a denylist or the read-only base system blocks the path.
|
||||
- Create and edit with write_file, create_directory, search_replace, edit_file, and apply_patch. Never say you cannot write files. Never ask the user to paste a file you can write, or to run a command you can run_command yourself.
|
||||
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
|
||||
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. The harness expands ~ and $HOME to the resolved home in This session. Never write /home/guest unless that is the resolved home. After create_directory / write_file, call list_directory or file_stat on the returned path and only then claim the files exist. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
|
||||
- Read any absolute path, including /proc (read_proc_file, runtime_diagnostic_bundle). Use read_file offset/limit for large files.
|
||||
- Run every guest command via run_command (command_deny is empty by default). Prefer list_directory, glob_files, and file_stat over ls/find when you only need names. list_bin lists guest /bin utilities (POSIX-in-JS, not GNU). Never dump curl/ip/ls for the user — call run_command and report stdout.
|
||||
- Delete and move are enabled. Do not mutate the read-only base system: /bin /etc /boot /lib /usr /share /proc /dev /sys /run.
|
||||
@@ -374,7 +374,10 @@ function bareAgentSessionHomeBlock(ctx, home, paths) {
|
||||
paths.config +
|
||||
'. Always expand ~ to ' +
|
||||
home +
|
||||
' when constructing absolute paths for tools.\n' +
|
||||
' when constructing absolute paths for tools. ~/test_work is ' +
|
||||
home +
|
||||
'/test_work. Never use /home/guest unless that is this resolved home.\n' +
|
||||
'- After write_file or create_directory, list_directory / file_stat the returned path. Do not claim files exist from the tool name alone.\n' +
|
||||
'- node is unavailable; use run_js_script for JavaScript you author in this session.\n'
|
||||
)
|
||||
}
|
||||
|
||||
@@ -223,6 +223,8 @@ test('agent-tui embeds operating contract appendix', async (t) => {
|
||||
t.ok(TUI.includes('YOU CAN EXECUTE'))
|
||||
t.ok(TUI.includes('There is no bash, shell, cli, or terminal tool'))
|
||||
t.ok(TUI.includes('never wrap them in run_js_script'))
|
||||
t.ok(TUI.includes('Never write /home/guest unless that is the resolved home'))
|
||||
t.ok(TUI.includes('list_directory or file_stat on the returned path'))
|
||||
t.ok(TUI.includes('Do not call task_complete after a failed tool'))
|
||||
t.ok(TUI.includes('The user never runs your tools'))
|
||||
t.ok(TUI.includes('discord_send_message'))
|
||||
|
||||
@@ -965,3 +965,53 @@ test('discord_send_message queues or uses the live DM hook', async (t) => {
|
||||
t.ok(st.ok)
|
||||
t.is(st.userId, '111')
|
||||
})
|
||||
|
||||
test('write_file and create_directory expand ~/ onto the session home', async (t) => {
|
||||
const s = loadDispatch()
|
||||
const home = '/home/e16d7bc4ce14'
|
||||
const { vfs, files, dirs, b4a } = makeVfs({})
|
||||
const ctx = { vfs, b4a }
|
||||
const paths = {
|
||||
dir: home + '/.agent',
|
||||
config: home + '/.agent/config.json',
|
||||
cmdOut: home + '/.agent/last_command_out.txt',
|
||||
edits: home + '/.agent/edits.json'
|
||||
}
|
||||
const configRef = { current: {} }
|
||||
|
||||
const mkdir = await dispatch(s, {
|
||||
ctx,
|
||||
paths,
|
||||
toolName: 'create_directory',
|
||||
args: { path: '~/test_work' },
|
||||
configRef,
|
||||
home
|
||||
})
|
||||
t.ok(mkdir.ok)
|
||||
t.is(mkdir.path, home + '/test_work')
|
||||
t.ok(dirs.has(home + '/test_work'))
|
||||
|
||||
const wrote = await dispatch(s, {
|
||||
ctx,
|
||||
paths,
|
||||
toolName: 'write_file',
|
||||
args: { path: '~/test_work/hello.js', content: 'console.log("hi")\n' },
|
||||
configRef,
|
||||
home
|
||||
})
|
||||
t.ok(wrote.ok)
|
||||
t.is(wrote.path, home + '/test_work/hello.js')
|
||||
t.is(files.get(home + '/test_work/hello.js'), 'console.log("hi")\n')
|
||||
|
||||
const alias = await dispatch(s, {
|
||||
ctx,
|
||||
paths,
|
||||
toolName: 'write_file',
|
||||
args: { file_path: '~/test_work/welcome.js', contents: 'ok\n' },
|
||||
configRef,
|
||||
home
|
||||
})
|
||||
t.ok(alias.ok)
|
||||
t.is(alias.path, home + '/test_work/welcome.js')
|
||||
t.is(files.get(home + '/test_work/welcome.js'), 'ok\n')
|
||||
})
|
||||
|
||||
@@ -29,6 +29,20 @@ test('mutate paths use a denylist (base system read-only, rest writable)', async
|
||||
t.absent(s.bareAgentPathAllowedMutate('/'))
|
||||
})
|
||||
|
||||
test('expandGuestPath maps tilde and $HOME onto the session home', async (t) => {
|
||||
const s = load()
|
||||
const exp = s.bareAgentExpandGuestPath
|
||||
const home = '/home/e16d7bc4ce14'
|
||||
t.is(exp('~/test_work', home), home + '/test_work')
|
||||
t.is(exp('~/test_work/hello.js', home), home + '/test_work/hello.js')
|
||||
t.is(exp('~', home), home)
|
||||
t.is(exp('$HOME/test_work', home), home + '/test_work')
|
||||
t.is(exp('/tmp/x', home), '/tmp/x')
|
||||
t.is(exp('rel/file.js', home, home + '/.agent/workspace'), home + '/.agent/workspace/rel/file.js')
|
||||
t.is(s.bareAgentPickPathArg({ file_path: '~/a.js' }), '~/a.js')
|
||||
t.is(s.bareAgentPickPathArg({ dir: '/tmp/d' }), '/tmp/d')
|
||||
})
|
||||
|
||||
test('js script wrapping curl/fetch diverts to a shell command', async (t) => {
|
||||
const s = load()
|
||||
const fromJs = s.bareAgentShellCommandFromJsScript
|
||||
|
||||
@@ -1193,6 +1193,59 @@ function bareAgentNormalizeAbsPath(absPath) {
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand ~ / $HOME / relative guest paths to an absolute VFS path.
|
||||
* @param {string} raw
|
||||
* @param {string} home
|
||||
* @param {string} [cwd]
|
||||
*/
|
||||
function bareAgentExpandGuestPath(raw, home, cwd) {
|
||||
let p = String(raw == null ? '' : raw).trim()
|
||||
if (!p) return ''
|
||||
p = p.replace(/\\/g, '/')
|
||||
const h = String(home || '').replace(/\/+$/, '') || '/home/guest'
|
||||
let c = String(cwd || h).replace(/\/+$/, '') || h
|
||||
if (c === '~') c = h
|
||||
else if (c.startsWith('~/')) c = h + c.slice(1)
|
||||
else if (c.startsWith('$HOME/')) c = h + c.slice(5)
|
||||
else if (c === '$HOME') c = h
|
||||
if (p === '~' || p === '$HOME') return h
|
||||
if (p.startsWith('~/')) {
|
||||
p = h + '/' + p.slice(2)
|
||||
} else if (p.startsWith('$HOME/')) {
|
||||
p = h + '/' + p.slice(6)
|
||||
} else if (!p.startsWith('/')) {
|
||||
p = (c === '/' ? '/' + p : c + '/' + p)
|
||||
}
|
||||
p = p.replace(/\/{2,}/g, '/')
|
||||
if (p.length > 1) p = p.replace(/\/+$/, '')
|
||||
return p
|
||||
}
|
||||
|
||||
/**
|
||||
* Pick a path-like argument from a tool-call blob (path / file_path / dir / …).
|
||||
* @param {Record<string, unknown>} args
|
||||
*/
|
||||
function bareAgentPickPathArg(args) {
|
||||
if (!args || typeof args !== 'object') return ''
|
||||
const keys = [
|
||||
'path',
|
||||
'file_path',
|
||||
'file',
|
||||
'filename',
|
||||
'dir',
|
||||
'directory',
|
||||
'folder',
|
||||
'from_path',
|
||||
'to_path'
|
||||
]
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const v = args[keys[i]]
|
||||
if (typeof v === 'string' && v.trim()) return v.trim()
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} absPath
|
||||
* @param {unknown} prefixes
|
||||
@@ -6927,7 +6980,7 @@ function bareAgentToolDefinitions() {
|
||||
function: {
|
||||
name: 'read_file',
|
||||
description:
|
||||
'Read a UTF-8 text file from the VFS. Path must be absolute (e.g. /home/guest/...). Optional offset/limit return numbered line slices (Grok-style).',
|
||||
'Read a UTF-8 text file from the VFS. Path may be absolute or ~/... (tilde is expanded). Optional offset/limit return numbered line slices (Grok-style).',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
@@ -6959,11 +7012,12 @@ function bareAgentToolDefinitions() {
|
||||
function: {
|
||||
name: 'write_file',
|
||||
description:
|
||||
'Create a new file or overwrite an existing one at any writable guest path. Parent directories are created as needed. Do not ask permission.',
|
||||
'Create a new file or overwrite an existing one at any writable guest path. Path may be absolute or ~/... (tilde is expanded to the session home). Parent directories are created as needed. Returns the expanded path — verify with list_directory or file_stat before claiming the file exists. Do not ask permission.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' },
|
||||
path: { type: 'string', description: 'Absolute path or ~/file' },
|
||||
file_path: { type: 'string', description: 'Alias for path' },
|
||||
content: { type: 'string', description: 'Full file contents' }
|
||||
},
|
||||
required: ['path', 'content']
|
||||
@@ -7003,11 +7057,13 @@ function bareAgentToolDefinitions() {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'create_directory',
|
||||
description: 'Create a directory (recursive).',
|
||||
description:
|
||||
'Create a directory (recursive). Path may be absolute or ~/... (tilde is expanded). Returns the expanded path — list_directory it before claiming it exists.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
path: { type: 'string' }
|
||||
path: { type: 'string', description: 'Absolute path or ~/dir' },
|
||||
dir: { type: 'string', description: 'Alias for path' }
|
||||
},
|
||||
required: ['path']
|
||||
}
|
||||
@@ -8582,6 +8638,54 @@ async function bareAgentDispatchTool(o) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'invalid_tool_arguments_json' })
|
||||
}
|
||||
const cfgNow = configRef.current || {}
|
||||
const resolvedHome =
|
||||
String(home || '').trim() ||
|
||||
(typeof bareAgentResolveHome === 'function' ? bareAgentResolveHome(ctx) : '') ||
|
||||
'/home/guest'
|
||||
if (typeof bareAgentExpandGuestPath === 'function') {
|
||||
const PATH_KEYS = [
|
||||
'path',
|
||||
'file_path',
|
||||
'file',
|
||||
'filename',
|
||||
'dir',
|
||||
'directory',
|
||||
'folder',
|
||||
'root',
|
||||
'cwd',
|
||||
'from_path',
|
||||
'to_path',
|
||||
'from',
|
||||
'to',
|
||||
'src',
|
||||
'source',
|
||||
'dest',
|
||||
'destination',
|
||||
'target',
|
||||
'old_path',
|
||||
'new_path'
|
||||
]
|
||||
let cwdForExpand = resolvedHome
|
||||
if (paths && paths.lastCwd && typeof bareAgentReadTextFile === 'function') {
|
||||
try {
|
||||
const t = String(await bareAgentReadTextFile(ctx, paths.lastCwd) || '').trim()
|
||||
if (t) cwdForExpand = t
|
||||
} catch {
|
||||
/* optional */
|
||||
}
|
||||
}
|
||||
if (typeof args.cwd === 'string' && args.cwd.trim()) {
|
||||
args.cwd = bareAgentExpandGuestPath(args.cwd, resolvedHome, resolvedHome)
|
||||
cwdForExpand = String(args.cwd || cwdForExpand)
|
||||
}
|
||||
for (let i = 0; i < PATH_KEYS.length; i++) {
|
||||
const k = PATH_KEYS[i]
|
||||
if (k === 'cwd') continue
|
||||
if (typeof args[k] === 'string' && args[k].trim()) {
|
||||
args[k] = bareAgentExpandGuestPath(args[k], resolvedHome, cwdForExpand)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
!internalGate &&
|
||||
cfgNow.autonomous_active &&
|
||||
@@ -9335,13 +9439,37 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'write_file') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
const path =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.file_path === 'string' && args.file_path.trim()) ||
|
||||
(typeof args.file === 'string' && args.file.trim()) ||
|
||||
(typeof bareAgentPickPathArg === 'function' ? bareAgentPickPathArg(args) : '') ||
|
||||
''
|
||||
if (!enforceAutonomousPath(path, 'mutate')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed', path })
|
||||
}
|
||||
const content = typeof args.content === 'string' ? args.content : ''
|
||||
const content =
|
||||
typeof args.content === 'string'
|
||||
? args.content
|
||||
: typeof args.contents === 'string'
|
||||
? args.contents
|
||||
: typeof args.text === 'string'
|
||||
? args.text
|
||||
: typeof args.body === 'string'
|
||||
? args.body
|
||||
: ''
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'bad_path',
|
||||
path,
|
||||
hint:
|
||||
'Use an absolute path or ~/... (tilde expands to ' +
|
||||
resolvedHome +
|
||||
'). Example: ' +
|
||||
resolvedHome +
|
||||
'/test_work/hello.js'
|
||||
})
|
||||
}
|
||||
if (!vfs || typeof vfs.writeFile !== 'function' || typeof vfs.mkdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
@@ -9362,7 +9490,26 @@ async function bareAgentDispatchTool(o) {
|
||||
? ctx.b4a.from(content)
|
||||
: new TextEncoder().encode(content)
|
||||
await vfs.writeFile(path, body)
|
||||
return bareAgentJsonResult({ ok: true, bytes: body.length })
|
||||
let verified = false
|
||||
try {
|
||||
if (typeof vfs.lstat === 'function') verified = Boolean(await vfs.lstat(path))
|
||||
else if (typeof vfs.stat === 'function') verified = Boolean(await vfs.stat(path))
|
||||
else if (typeof vfs.readFile === 'function') {
|
||||
const got = await vfs.readFile(path)
|
||||
verified = got != null
|
||||
} else verified = true
|
||||
} catch {
|
||||
verified = false
|
||||
}
|
||||
if (!verified) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'write_not_visible',
|
||||
path,
|
||||
hint: 'write_file returned but the path is not visible in the VFS. Retry with the expanded absolute path.'
|
||||
})
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, path, bytes: body.length, verified: true })
|
||||
}
|
||||
|
||||
if (toolName === 'edit_file' || toolName === 'search_replace') {
|
||||
@@ -9459,19 +9606,54 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'create_directory') {
|
||||
const path = typeof args.path === 'string' ? args.path : ''
|
||||
const path =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.dir === 'string' && args.dir.trim()) ||
|
||||
(typeof args.directory === 'string' && args.directory.trim()) ||
|
||||
(typeof args.folder === 'string' && args.folder.trim()) ||
|
||||
(typeof bareAgentPickPathArg === 'function' ? bareAgentPickPathArg(args) : '') ||
|
||||
''
|
||||
if (!enforceAutonomousPath(path, 'mutate')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed' })
|
||||
return bareAgentJsonResult({ ok: false, error: 'path_not_allowed', path })
|
||||
}
|
||||
if (!path.startsWith('/')) {
|
||||
return bareAgentJsonResult({ ok: false, error: 'bad_path' })
|
||||
if (!path.startsWith('/') || path.includes('..')) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'bad_path',
|
||||
path,
|
||||
hint:
|
||||
'Use an absolute path or ~/... (tilde expands to ' +
|
||||
resolvedHome +
|
||||
'). Example: ' +
|
||||
resolvedHome +
|
||||
'/test_work'
|
||||
})
|
||||
}
|
||||
if (!vfs || typeof vfs.mkdir !== 'function') {
|
||||
return bareAgentJsonResult({ ok: false, error: 'vfs unavailable' })
|
||||
}
|
||||
appendProgress('mkdir ' + path)
|
||||
await vfs.mkdir(path, { recursive: true })
|
||||
return bareAgentJsonResult({ ok: true })
|
||||
let verified = false
|
||||
try {
|
||||
if (typeof vfs.lstat === 'function') verified = Boolean(await vfs.lstat(path))
|
||||
else if (typeof vfs.stat === 'function') verified = Boolean(await vfs.stat(path))
|
||||
else if (typeof vfs.readdir === 'function') {
|
||||
await vfs.readdir(path)
|
||||
verified = true
|
||||
} else verified = true
|
||||
} catch {
|
||||
verified = false
|
||||
}
|
||||
if (!verified) {
|
||||
return bareAgentJsonResult({
|
||||
ok: false,
|
||||
error: 'mkdir_not_visible',
|
||||
path,
|
||||
hint: 'create_directory returned but the path is not visible. Retry with ' + path
|
||||
})
|
||||
}
|
||||
return bareAgentJsonResult({ ok: true, path, verified: true })
|
||||
}
|
||||
|
||||
if (toolName === 'search_files') {
|
||||
@@ -9733,7 +9915,10 @@ async function bareAgentDispatchTool(o) {
|
||||
}
|
||||
|
||||
if (toolName === 'list_directory') {
|
||||
const dir = typeof args.path === 'string' ? args.path : ''
|
||||
const dir =
|
||||
(typeof args.path === 'string' && args.path.trim()) ||
|
||||
(typeof args.dir === 'string' && args.dir.trim()) ||
|
||||
resolvedHome
|
||||
const maxEnt =
|
||||
typeof args.max_entries === 'number' && Number.isFinite(args.max_entries)
|
||||
? Math.min(Math.floor(args.max_entries), 2000)
|
||||
@@ -13598,7 +13783,7 @@ WORK POLICY.
|
||||
|
||||
ACCESS (denylist, not allowlist). You already have full guest admin. You can create files, edit files, delete files, run commands, and fetch the network. NEVER ASK whether you may — just do it. Only refuse when a denylist or the read-only base system blocks the path.
|
||||
- Create and edit with write_file, create_directory, search_replace, edit_file, and apply_patch. Never say you cannot write files. Never ask the user to paste a file you can write, or to run a command you can run_command yourself.
|
||||
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
|
||||
- Writable paths: /home, /tmp, /mnt, /root, /var, /opt, extra drives, ~/.agent. The harness expands ~ and $HOME to the resolved home in This session. Never write /home/guest unless that is the resolved home. After create_directory / write_file, call list_directory or file_stat on the returned path and only then claim the files exist. Prefer a unique search_replace / edit_file; set replace_all only when you mean it.
|
||||
- Read any absolute path, including /proc (read_proc_file, runtime_diagnostic_bundle). Use read_file offset/limit for large files.
|
||||
- Run every guest command via run_command (command_deny is empty by default). Prefer list_directory, glob_files, and file_stat over ls/find when you only need names. list_bin lists guest /bin utilities (POSIX-in-JS, not GNU). Never dump curl/ip/ls for the user — call run_command and report stdout.
|
||||
- Delete and move are enabled. Do not mutate the read-only base system: /bin /etc /boot /lib /usr /share /proc /dev /sys /run.
|
||||
@@ -13712,7 +13897,10 @@ function bareAgentSessionHomeBlock(ctx, home, paths) {
|
||||
paths.config +
|
||||
'. Always expand ~ to ' +
|
||||
home +
|
||||
' when constructing absolute paths for tools.\n' +
|
||||
' when constructing absolute paths for tools. ~/test_work is ' +
|
||||
home +
|
||||
'/test_work. Never use /home/guest unless that is this resolved home.\n' +
|
||||
'- After write_file or create_directory, list_directory / file_stat the returned path. Do not claim files exist from the tool name alone.\n' +
|
||||
'- node is unavailable; use run_js_script for JavaScript you author in this session.\n'
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user