Files
bare-operating-system/packages/bare-os-coreutils/lib/agent/agent-state.js
T
2026-08-18 18:11:28 -04:00

804 lines
24 KiB
JavaScript

/** ~/.agent paths, config, history trim, man digest (preamble for /bin/agent). */
/**
* @param {Record<string, unknown>} ctx
* @returns {string}
*/
function bareAgentResolveHome(ctx) {
const env =
ctx && typeof ctx === 'object' && ctx.env && typeof ctx.env === 'object'
? /** @type {Record<string, string>} */ (ctx.env)
: {}
const vfsHome =
ctx &&
typeof ctx === 'object' &&
ctx.vfs &&
typeof ctx.vfs === 'object' &&
typeof /** @type {{ home?: string }} */ (ctx.vfs).home === 'string'
? String(/** @type {{ home?: string }} */ (ctx.vfs).home)
: ''
const h = env.HOME || vfsHome || '/home/guest'
return h.replace(/\/+$/, '') || '/home/guest'
}
/**
* @param {string} home
*/
function bareAgentPaths(home) {
const base = home + '/.agent'
return {
dir: base,
config: base + '/config.json',
history: base + '/history.json',
progress: base + '/progress.txt',
instructions: base + '/instructions.md',
context: base + '/context.md',
compact: base + '/compact.md',
cmdOut: base + '/last_command_out.txt',
workspace: base + '/workspace',
workspaceMemory: base + '/workspace/memory',
workspaceSkills: base + '/workspace/skills',
skillsGlobal: base + '/skills',
todos: base + '/todos.json',
plan: base + '/plan.md',
hooks: base + '/hooks',
ask: base + '/ask.json',
edits: base + '/edits.json',
lastCwd: base + '/last_cwd'
}
}
function bareAgentDefaultConfig() {
return {
backend: 'qvac',
rest_base_url: 'https://api.groq.com/openai/v1',
rest_api_key: '',
model: 'QWEN3_1_7B_INST_Q4',
qvac_model: 'QWEN3_1_7B_INST_Q4',
qvac_profile: 'recommended',
qvac_ctx_size: 0,
qvac_device: '',
qvac_main_gpu: 'auto',
qvac_gpu_layers: -1,
max_tokens: 4096,
temperature: 0.7,
provider: 'qvac',
max_iterations: 64,
stream: true,
tool_parallelism: 1,
request_timeout_ms: 120000,
extra_headers: /** @type {Record<string, string>} */ ({}),
access_policy: 'full',
allow_delete: true,
require_confirm_token: '',
owner_name: '',
agent_label: '',
show_reasoning: false,
reasoning_mode: 'off',
reasoning_max_chars: 4000,
reasoning_include_tools: true,
allow_bridge_mutations: true,
allow_host_notifications: true,
allow_host_actions: true,
emergency_stop_mutations: false,
autonomous_mode_enabled: true,
autonomous_max_runtime_ms: 1800000,
autonomous_completion_required_checks: [],
autonomous_allow_paths: ['*'],
autonomous_deny_ops: [],
command_deny: [],
mutate_deny_prefixes: [
'/bin',
'/etc',
'/boot',
'/lib',
'/usr',
'/share',
'/proc',
'/dev',
'/sys',
'/run'
],
autonomous_active: false,
autonomous_started_at_ms: 0,
autonomous_stop_requested: false,
autonomous_goal: '',
autonomous_status: 'idle',
autonomous_last_error: '',
context_compaction: 'auto',
compaction_keep_recent: 8,
compaction_tool_chars: 1600,
plan_mode_active: false,
todo_nudge_enabled: true
}
}
/**
* @param {unknown} v
* @returns {v is Record<string, unknown>}
*/
function bareAgentIsPlainObject(v) {
return v != null && typeof v === 'object' && !Array.isArray(v)
}
/**
* Shallow merge known keys from src into defaults.
* @param {Record<string, unknown>} defaults
* @param {Record<string, unknown>} src
*/
function bareAgentMergeConfig(defaults, src) {
const out = { ...defaults }
const known = new Set([
'backend',
'rest_base_url',
'rest_api_key',
'model',
'qvac_model',
'qvac_profile',
'qvac_ctx_size',
'qvac_device',
'qvac_main_gpu',
'qvac_gpu_layers',
'max_tokens',
'temperature',
'provider',
'max_iterations',
'stream',
'tool_parallelism',
'request_timeout_ms',
'extra_headers',
'access_policy',
'allow_delete',
'require_confirm_token',
'command_deny',
'mutate_deny_prefixes',
'owner_name',
'agent_label',
'show_reasoning',
'reasoning_mode',
'reasoning_max_chars',
'reasoning_include_tools',
'allow_bridge_mutations',
'allow_host_notifications',
'allow_host_actions',
'emergency_stop_mutations',
'autonomous_mode_enabled',
'autonomous_max_runtime_ms',
'autonomous_completion_required_checks',
'autonomous_allow_paths',
'autonomous_deny_ops',
'autonomous_active',
'autonomous_started_at_ms',
'autonomous_stop_requested',
'autonomous_goal',
'autonomous_status',
'autonomous_last_error',
'context_compaction',
'compaction_keep_recent',
'compaction_tool_chars',
'plan_mode_active',
'todo_nudge_enabled'
])
for (const k of Object.keys(src)) {
if (k.startsWith('x-')) continue
if (!known.has(k)) continue
const val = src[k]
if (k === 'extra_headers' && bareAgentIsPlainObject(val)) {
out.extra_headers = /** @type {Record<string, string>} */ ({ ...val })
continue
}
if (k === 'backend') {
const b = String(val ?? '').trim().toLowerCase()
out.backend = b === 'rest' || b === 'openai' || b === 'http' ? 'rest' : 'qvac'
continue
}
if (
k === 'rest_base_url' ||
k === 'rest_api_key' ||
k === 'model' ||
k === 'qvac_model' ||
k === 'qvac_profile' ||
k === 'provider' ||
k === 'owner_name' ||
k === 'agent_label' ||
k === 'autonomous_goal' ||
k === 'autonomous_status' ||
k === 'autonomous_last_error' ||
k === 'qvac_device' ||
k === 'qvac_main_gpu' ||
k === 'require_confirm_token'
) {
out[k] = String(val ?? '')
continue
}
if (k === 'context_compaction') {
const mode = String(val ?? '').trim().toLowerCase()
out.context_compaction =
mode === 'off' || mode === 'aggressive' ? mode : 'auto'
continue
}
if (k === 'reasoning_mode') {
const mode = String(val ?? '').trim().toLowerCase()
out.reasoning_mode =
mode === 'summary' || mode === 'trace' ? mode : 'off'
continue
}
if (
k === 'max_tokens' ||
k === 'temperature' ||
k === 'max_iterations' ||
k === 'tool_parallelism' ||
k === 'request_timeout_ms' ||
k === 'reasoning_max_chars' ||
k === 'qvac_ctx_size' ||
k === 'qvac_gpu_layers' ||
k === 'autonomous_max_runtime_ms' ||
k === 'autonomous_started_at_ms' ||
k === 'compaction_keep_recent' ||
k === 'compaction_tool_chars'
) {
const n = Number(val)
out[k] = Number.isFinite(n) ? n : defaults[k]
continue
}
if (
k === 'autonomous_completion_required_checks' ||
k === 'autonomous_allow_paths' ||
k === 'autonomous_deny_ops' ||
k === 'command_deny' ||
k === 'mutate_deny_prefixes'
) {
out[k] = Array.isArray(val) ? val.map((x) => String(x ?? '')).filter(Boolean) : defaults[k]
continue
}
if (
k === 'stream' ||
k === 'allow_delete' ||
k === 'show_reasoning' ||
k === 'reasoning_include_tools' ||
k === 'allow_bridge_mutations' ||
k === 'allow_host_notifications' ||
k === 'allow_host_actions' ||
k === 'emergency_stop_mutations' ||
k === 'autonomous_mode_enabled' ||
k === 'autonomous_active' ||
k === 'autonomous_stop_requested' ||
k === 'plan_mode_active' ||
k === 'todo_nudge_enabled'
) {
out[k] = Boolean(val)
continue
}
if (k === 'require_confirm_token') {
out.require_confirm_token = String(val ?? '')
continue
}
if (k === 'access_policy') {
const pol = String(val ?? '').trim().toLowerCase()
out.access_policy = pol === 'restricted' ? 'restricted' : 'full'
continue
}
}
return out
}
/**
* Old configs persisted restrictive defaults. Missing access_policy means
* upgrade onto full guest admin (denylist-only) so existing homes match.
* @param {Record<string, unknown>} raw
* @param {Record<string, unknown>} merged
*/
function bareAgentApplyAccessPolicyUpgrade(raw, merged) {
const src = bareAgentIsPlainObject(raw) ? raw : {}
if (Object.prototype.hasOwnProperty.call(src, 'access_policy')) {
return { config: merged, upgraded: false }
}
const next = { ...merged }
next.access_policy = 'full'
next.allow_delete = true
next.require_confirm_token = ''
next.allow_bridge_mutations = true
next.allow_host_notifications = true
next.allow_host_actions = true
next.emergency_stop_mutations = false
next.autonomous_deny_ops = []
next.autonomous_allow_paths = ['*']
next.command_deny = Array.isArray(next.command_deny) ? next.command_deny : []
next.autonomous_mode_enabled = true
if (!Array.isArray(next.mutate_deny_prefixes) || !next.mutate_deny_prefixes.length) {
next.mutate_deny_prefixes = bareAgentDefaultConfig().mutate_deny_prefixes
}
return { config: next, upgraded: true }
}
/**
* @param {Record<string, unknown>} raw
*/
function bareAgentValidateConfigShape(raw) {
if (!bareAgentIsPlainObject(raw)) throw new Error('config must be a JSON object')
for (const k of Object.keys(raw)) {
if (k.startsWith('x-')) continue
const known = [
'backend',
'rest_base_url',
'rest_api_key',
'model',
'qvac_model',
'qvac_profile',
'qvac_ctx_size',
'qvac_device',
'qvac_main_gpu',
'qvac_gpu_layers',
'max_tokens',
'temperature',
'provider',
'max_iterations',
'stream',
'tool_parallelism',
'request_timeout_ms',
'extra_headers',
'access_policy',
'allow_delete',
'require_confirm_token',
'command_deny',
'mutate_deny_prefixes',
'owner_name',
'agent_label',
'show_reasoning',
'reasoning_mode',
'reasoning_max_chars',
'reasoning_include_tools',
'allow_bridge_mutations',
'allow_host_notifications',
'allow_host_actions',
'emergency_stop_mutations',
'autonomous_mode_enabled',
'autonomous_max_runtime_ms',
'autonomous_completion_required_checks',
'autonomous_allow_paths',
'autonomous_deny_ops',
'autonomous_active',
'autonomous_started_at_ms',
'autonomous_stop_requested',
'autonomous_goal',
'autonomous_status',
'autonomous_last_error',
'context_compaction',
'compaction_keep_recent',
'compaction_tool_chars',
'plan_mode_active',
'todo_nudge_enabled'
]
if (!known.includes(k)) {
throw new Error('unknown config key: ' + k)
}
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {{ dir: string, config: string }} paths
* @returns {Promise<{ config: ReturnType<typeof bareAgentDefaultConfig>, created: boolean }>}
*/
async function bareAgentLoadOrCreateConfig(ctx, paths) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.mkdir !== 'function')
throw new Error('agent: vfs.mkdir unavailable')
await vfs.mkdir(paths.dir, { recursive: true })
let missingOrEmpty = false
/** @type {Record<string, unknown>} */
let raw = {}
try {
if (typeof vfs.readFile === 'function') {
const buf = await vfs.readFile(paths.config)
if (!buf || !buf.length) missingOrEmpty = true
else {
const t =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
? ctx.b4a.toString(buf)
: String(new TextDecoder().decode(buf))
raw = JSON.parse(t)
}
} else missingOrEmpty = true
} catch {
missingOrEmpty = true
raw = {}
}
if (!bareAgentIsPlainObject(raw)) raw = {}
if (missingOrEmpty || Object.keys(raw).length === 0) {
raw = bareAgentDefaultConfig()
if (typeof bareAgentSanitizeConfigForBackend === 'function') {
raw = bareAgentSanitizeConfigForBackend(raw)
}
await bareAgentSaveConfig(ctx, paths, /** @type {any} */ (raw))
return { config: /** @type {any} */ (raw), created: true }
}
bareAgentValidateConfigShape(raw)
const merged = bareAgentMergeConfig(bareAgentDefaultConfig(), raw)
const applied = bareAgentApplyAccessPolicyUpgrade(raw, merged)
let next = applied.config
if (typeof bareAgentSanitizeConfigForBackend === 'function') {
next = bareAgentSanitizeConfigForBackend(next)
}
const dirty =
applied.upgraded ||
JSON.stringify(next) !== JSON.stringify(merged)
if (dirty) {
try {
await bareAgentSaveConfig(ctx, paths, /** @type {any} */ (next))
} catch {
/* keep upgraded in-memory even if persist fails */
}
}
return { config: /** @type {any} */ (next), created: false }
}
/**
* @param {Record<string, unknown>} ctx
* @param {{ config: string }} paths
* @param {Record<string, unknown>} config
*/
async function bareAgentSaveConfig(ctx, paths, config) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.writeFile !== 'function')
throw new Error('agent: vfs.writeFile unavailable')
let next = config
if (typeof bareAgentSanitizeConfigForBackend === 'function') {
next = bareAgentSanitizeConfigForBackend(config)
if (config && typeof config === 'object' && config !== next) {
for (const k of Object.keys(config)) {
if (!Object.prototype.hasOwnProperty.call(next, k)) delete config[k]
}
Object.assign(config, next)
}
}
bareAgentValidateConfigShape(next)
const json = JSON.stringify(next, null, 2) + '\n'
const body =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from(json)
: new TextEncoder().encode(json)
await vfs.writeFile(paths.config, body)
}
/**
* Drop middle messages until JSON size fits (keep first system + recent tail).
* @param {unknown[]} msgs
* @param {number} maxBytes
*/
function bareAgentTrimMessages(msgs, maxBytes) {
if (!Array.isArray(msgs) || maxBytes <= 0) return []
/** @type {unknown[]} */
let out = msgs.slice()
while (JSON.stringify(out).length > maxBytes && out.length > 3) {
out.splice(2, 1)
}
return out
}
/**
* Rough token estimate (chars/4). Good enough for local ctx budgeting.
* @param {unknown} value
*/
function bareAgentEstimateTokens(value) {
try {
const n = JSON.stringify(value == null ? '' : value).length
return Math.max(0, Math.ceil(n / 4))
} catch {
return 0
}
}
/**
* Trim history + shrink system content so prompt fits a QVAC ctx window.
* Reserves room for completion and optional tools JSON.
* @param {unknown[]} msgs
* @param {number} ctxSize
* @param {{ tools?: unknown[], reserveCompletion?: number }} [opts]
*/
function bareAgentTrimMessagesForCtx(msgs, ctxSize, opts) {
const ctx = Math.max(2048, Math.floor(Number(ctxSize) || 4096))
const reserve =
opts && Number.isFinite(Number(opts.reserveCompletion))
? Math.max(256, Math.floor(Number(opts.reserveCompletion)))
: Math.min(1024, Math.max(256, Math.floor(ctx * 0.15)))
const toolsTok = bareAgentEstimateTokens(
opts && Array.isArray(opts.tools) && opts.tools.length ? opts.tools : []
)
const budget = Math.max(512, ctx - reserve - toolsTok)
/** @type {unknown[]} */
let out = Array.isArray(msgs) ? msgs.slice() : []
// Drop middle turns first (keep system + recent).
while (bareAgentEstimateTokens(out) > budget && out.length > 3) {
out.splice(2, 1)
}
// Shrink system blob if still over (workspace/man/skills dominate).
if (bareAgentEstimateTokens(out) > budget && out[0] && typeof out[0] === 'object') {
const sys = /** @type {Record<string, unknown>} */ (out[0])
if (sys.role === 'system' && typeof sys.content === 'string') {
let content = sys.content
let guard = 0
while (
bareAgentEstimateTokens(out) > budget &&
content.length > 800 &&
guard < 24
) {
content =
content.slice(0, Math.floor(content.length * 0.82)) + '\n… truncated'
out[0] = { ...sys, content }
guard++
}
}
}
return out
}
/**
* Best-effort load instruction files.
* @param {Record<string, unknown>} ctx
* @param {{ instructions: string, context: string }} paths
*/
async function bareAgentLoadInstructionFiles(ctx, paths) {
const vfs = ctx.vfs
const parts = []
if (!vfs || typeof vfs.readFile !== 'function') return ''
try {
const b = await vfs.readFile(paths.instructions)
if (b && b.length) {
const t =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
? ctx.b4a.toString(b)
: String(new TextDecoder().decode(b))
if (t.trim()) parts.push('## User instructions (~/.agent/instructions.md)\n' + t.trim())
}
} catch {
/* ignore */
}
try {
const b = await vfs.readFile(paths.context)
if (b && b.length) {
const t =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
? ctx.b4a.toString(b)
: String(new TextDecoder().decode(b))
if (t.trim()) parts.push('## Agent context (~/.agent/context.md)\n' + t.trim())
}
} catch {
/* ignore */
}
return parts.join('\n\n')
}
/**
* One-time compact index from man.json for system prompt.
* @param {Record<string, unknown>} ctx
*/
async function bareAgentManDigest(ctx) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.readFile !== 'function') return ''
try {
const buf = await vfs.readFile('/share/man/man.json')
if (!buf || !buf.length) return ''
const t =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
? ctx.b4a.toString(buf)
: String(new TextDecoder().decode(buf))
const j = JSON.parse(t)
const pages = Array.isArray(j.pages) ? j.pages : []
/** @type {string[]} */
const lines = []
const max = Math.min(pages.length, 400)
for (let i = 0; i < max; i++) {
const p = pages[i]
if (!p || typeof p !== 'object') continue
const name = typeof p.name === 'string' ? p.name : ''
const title = typeof p.title === 'string' ? p.title : ''
if (name) lines.push('- ' + name + (title ? ': ' + title : ''))
}
let s = lines.join('\n')
if (s.length > 24000) s = s.slice(0, 24000) + '\n…'
return (
'Manual page index (see `man <name>` for full text). Sample entries:\n' + s
)
} catch {
return '(man digest unavailable)'
}
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} path
*/
async function bareAgentAppendProgress(ctx, path, line) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.readFile !== 'function' || typeof vfs.writeFile !== 'function')
return
let prev = ''
try {
const b = await vfs.readFile(path)
if (b && b.length) {
prev =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
? ctx.b4a.toString(b)
: String(new TextDecoder().decode(b))
}
} catch {
/* ignore */
}
const chunk =
new Date().toISOString() +
' ' +
line.replace(/\r?\n/g, ' ') +
'\n'
const maxKeep = 120_000
let next = prev + chunk
if (next.length > maxKeep) next = next.slice(-maxKeep)
const body =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from(next)
: new TextEncoder().encode(next)
await vfs.writeFile(path, body)
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} path
* @param {unknown[]} messages
*/
async function bareAgentSaveHistory(ctx, path, messages) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.writeFile !== 'function') return
const compacted =
typeof bareAgentCompactMessagesForDisk === 'function'
? bareAgentCompactMessagesForDisk(messages, 500_000, { keepRecent: 16 })
: bareAgentTrimMessages(messages, 500_000)
const trimmed =
typeof bareAgentTrimMessages === 'function'
? bareAgentTrimMessages(compacted, 500_000)
: compacted
const json = JSON.stringify(trimmed, null, 2) + '\n'
const body =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from(json)
: new TextEncoder().encode(json)
await vfs.writeFile(path, body)
}
/**
* @param {Record<string, unknown>} ctx
* @param {string} path
* @returns {Promise<unknown[]>}
*/
async function bareAgentLoadHistory(ctx, path) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.readFile !== 'function') return []
try {
const buf = await vfs.readFile(path)
if (!buf || !buf.length) return []
const t =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.toString === 'function'
? ctx.b4a.toString(buf)
: String(new TextDecoder().decode(buf))
const j = JSON.parse(t)
return Array.isArray(j) ? j : []
} catch {
return []
}
}
/**
* Start an autonomous run on a config object (does not persist).
* @param {Record<string, unknown>} cfg
* @param {{ goal: string, maxRuntimeMs?: number, requiredChecks?: string[], scopePath?: string }} opts
*/
function bareAgentBeginAutonomousRun(cfg, opts) {
const goal = String((opts && opts.goal) || '').trim()
const maxRuntimeMs = Math.min(
Math.max(Math.floor(Number((opts && opts.maxRuntimeMs) || cfg.autonomous_max_runtime_ms) || 0), 60000),
7_200_000
)
const requiredChecks = Array.isArray(opts && opts.requiredChecks)
? opts.requiredChecks.map((x) => String(x || '').trim()).filter(Boolean)
: Array.isArray(cfg.autonomous_completion_required_checks)
? cfg.autonomous_completion_required_checks.map((x) => String(x || '').trim()).filter(Boolean)
: []
return {
...cfg,
autonomous_mode_enabled: true,
autonomous_active: true,
autonomous_stop_requested: false,
autonomous_started_at_ms: Date.now(),
autonomous_goal: goal,
autonomous_status: 'running',
autonomous_last_error: '',
autonomous_max_runtime_ms: maxRuntimeMs,
autonomous_completion_required_checks: requiredChecks
}
}
/**
* @param {Record<string, unknown>} cfg
* @param {string} [reason]
*/
function bareAgentStopAutonomousRun(cfg, reason) {
return {
...cfg,
autonomous_stop_requested: true,
autonomous_status: 'stopping',
autonomous_last_error: reason || String(cfg.autonomous_last_error || '')
}
}
/**
* @param {Record<string, unknown>} cfg
* @param {{ remainingMs?: number, lastError?: string }} [extra]
*/
function bareAgentAutonomousContinuationPrompt(cfg, extra) {
const goal = String(cfg.autonomous_goal || '').trim() || '(goal unset)'
const remain =
extra && typeof extra.remainingMs === 'number'
? Math.max(0, Math.round(extra.remainingMs / 1000))
: 0
return (
'AUTONOMOUS RUN still active. Do not stop with a plan — take the next concrete tool action.\n' +
'Goal: ' +
goal +
'\n' +
(remain > 0 ? 'Time remaining: ' + String(remain) + 's.\n' : '') +
(extra && extra.lastError ? 'Last gate error: ' + extra.lastError + '\n' : '') +
'Call task_complete(summary) only when the goal is actually finished and verified.'
)
}
/**
* @param {Record<string, unknown>} cfg
* @param {{ completed?: boolean, hasTools?: boolean, stopRequested?: boolean }} state
*/
function bareAgentAutonomousShouldContinue(cfg, state) {
if (!cfg || !cfg.autonomous_active) return false
if (state && state.stopRequested) return false
if (state && state.completed) return false
if (state && state.hasTools) return false
return true
}
async function bareAgentResetChatSession(ctx, paths, argv0) {
const vfs = ctx.vfs
if (!vfs || typeof vfs.mkdir !== 'function' || typeof vfs.writeFile !== 'function') {
throw new Error('agent reset: vfs unavailable')
}
await vfs.mkdir(paths.dir, { recursive: true })
const emptyHist =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from('[]\n')
: new TextEncoder().encode('[]\n')
await vfs.writeFile(paths.history, emptyHist)
const stamp = new Date().toISOString() + ' chat session reset\n'
const progBody =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from(stamp)
: new TextEncoder().encode(stamp)
await vfs.writeFile(paths.progress, progBody)
try {
const z =
typeof ctx.b4a !== 'undefined' && ctx.b4a && typeof ctx.b4a.from === 'function'
? ctx.b4a.from('')
: new TextEncoder().encode('')
await vfs.writeFile(paths.cmdOut, z)
} catch {
/* ignore */
}
try {
ctx.console.log(
argv0 + ': chat session cleared (' + paths.history + ', ' + paths.progress + ')'
)
} catch {
/* ignore */
}
}