Context L
Release rolling / release (push) Successful in 8m34s

This commit is contained in:
Raven Scott
2026-07-31 19:17:19 -04:00
parent 33c8a9f197
commit f919d0b085
17 changed files with 632 additions and 71 deletions
+189 -17
View File
@@ -1404,6 +1404,64 @@ function bareAgentTrimMessages(msgs, maxBytes) {
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
@@ -1597,6 +1655,16 @@ async function bareAgentResetChatSession(ctx, paths, argv0) {
/** @typedef {'lite'|'recommended'|'strong'|'tool-tiny'} BareAgentQvacProfileId */
/**
* Native context windows from upstream model cards (not conservative laptop defaults).
* Qwen3 dense (0.6B/1.7B/4B): https://huggingface.co/Qwen/Qwen3-0.6B — 32,768
* Llama 3.2 1B (tool-calling finetune base): Meta Llama 3.2 — 128,000
* Absolute ceiling for overrides / host clamp.
*/
const BARE_AGENT_QVAC_CTX_QWEN3 = 32768
const BARE_AGENT_QVAC_CTX_LLAMA32_1B = 131072
const BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX = 131072
/**
* @typedef {{
* id: BareAgentQvacProfileId,
@@ -1617,50 +1685,69 @@ const BARE_AGENT_QVAC_PROFILES = {
id: 'lite',
label: 'Lite',
description:
'Smallest download. Good for weak machines / ~4 GB VRAM; limited tool use.',
'Smallest download (Qwen3-0.6B). Model-card context 32k; limited tool use.',
chatModel: 'QWEN3_600M_INST_Q4',
tools: false,
minRamGb: 4,
minDiskGb: 2,
approxDownloadGb: 0.5,
ctxSize: 4096
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
recommended: {
id: 'recommended',
label: 'Recommended',
description:
'Best balance for Bare OS agent tool calling (fits ~48 GB VRAM at ctx 8k).',
'Best balance for Bare OS agent tool calling (Qwen3-1.7B, model-card context 32k).',
chatModel: 'QWEN3_1_7B_INST_Q4',
tools: true,
minRamGb: 8,
minDiskGb: 5,
approxDownloadGb: 2.5,
ctxSize: 8192
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
strong: {
id: 'strong',
label: 'Strong',
description: 'Better reasoning. Needs more RAM/VRAM/disk.',
description:
'Better reasoning (Qwen3-4B). Model-card context 32k (131k with YaRN not enabled).',
chatModel: 'QWEN3_4B_INST_Q4_K_M',
tools: true,
minRamGb: 16,
minDiskGb: 8,
approxDownloadGb: 3.5,
ctxSize: 8192
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
'tool-tiny': {
id: 'tool-tiny',
label: 'Tool-tiny',
description: 'Llama tool-calling 1B fallback if Qwen tools misbehave.',
description:
'Llama 3.2 1B tool-calling fallback (model-card context 128k).',
chatModel: 'LLAMA_TOOL_CALLING_1B_INST_Q4_K',
tools: true,
minRamGb: 6,
minDiskGb: 3,
approxDownloadGb: 1,
ctxSize: 4096
ctxSize: BARE_AGENT_QVAC_CTX_LLAMA32_1B
}
}
/**
* Model-card / train context for a QVAC registry id (fallback 32k).
* @param {string} [modelId]
* @returns {number}
*/
function bareAgentQvacModelCardCtxSize(modelId) {
const id = String(modelId || '')
.trim()
.toUpperCase()
if (!id) return BARE_AGENT_QVAC_CTX_QWEN3
if (id.includes('LLAMA') || id.includes('LLAMA_TOOL')) {
return BARE_AGENT_QVAC_CTX_LLAMA32_1B
}
if (id.includes('QWEN3')) return BARE_AGENT_QVAC_CTX_QWEN3
return BARE_AGENT_QVAC_CTX_QWEN3
}
/** @returns {BareAgentQvacProfile[]} */
function bareAgentQvacProfileList() {
return Object.values(BARE_AGENT_QVAC_PROFILES)
@@ -1749,17 +1836,33 @@ function bareAgentResolveBackend(config) {
}
/**
* Resolve context window for QVAC load (profile default or config override).
* Resolve context window for QVAC load (profile/model-card default or config override).
* Tool schemas alone are ~4.5k tokens, so tool-enabled profiles need ≥8192.
* @param {Record<string, unknown>} config
* @param {BareAgentQvacProfile} profile
*/
function bareAgentQvacResolveCtxSize(config, profile) {
const modelId = String(
(config && (config.qvac_model || config.model)) ||
(profile && profile.chatModel) ||
''
)
const cardCtx = bareAgentQvacModelCardCtxSize(modelId)
const profileCtx = Math.max(
2048,
Number(profile && profile.ctxSize) || cardCtx
)
const raw = Number(config && config.qvac_ctx_size)
let ctx
if (Number.isFinite(raw) && raw >= 2048) {
// Cap high overrides; 32k+ KV often OOMs laptop GPUs (~4 GB VRAM).
return Math.min(32768, Math.floor(raw))
ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.floor(raw))
} else {
// Prefer explicit profile ctx (already model-card sized); else card for model id.
ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.max(profileCtx, cardCtx))
}
return Math.max(2048, Number(profile.ctxSize) || 8192)
// Full Bare OS tool list ≈ 4.5k tokens; leave room for system + reply.
if (profile && profile.tools !== false && ctx < 8192) ctx = 8192
return ctx
}
/**
@@ -6846,11 +6949,36 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
if (needWizard) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
const backendForPrompt = bareAgentResolveBackend(config)
const promptProfile = bareAgentQvacGetProfile(
String(config.qvac_profile || config.profile || '')
)
const promptCtxSize =
backendForPrompt === 'qvac'
? bareAgentQvacResolveCtxSize(config, promptProfile)
: 32768
// Local QVAC models have a hard ctx window; keep the system prompt lean.
const workspaceBudget = backendForPrompt === 'qvac' ? 8000 : 24000
const manBudget = backendForPrompt === 'qvac' ? 4000 : 12000
const instructionsBudget = backendForPrompt === 'qvac' ? 3000 : 8000
const skillsBudget = backendForPrompt === 'qvac' ? 2000 : 4000
// Scale char budgets from ctx (chars ≈ tokens*4); leave room for tools/reply.
const toolsOn = backendForPrompt === 'qvac' && promptProfile.tools !== false
const promptCharBudget = Math.max(
4000,
Math.floor(promptCtxSize * 4 * (toolsOn ? 0.35 : 0.7))
)
const workspaceBudget =
backendForPrompt === 'qvac'
? Math.min(8000, Math.floor(promptCharBudget * 0.35))
: 24000
const manBudget =
backendForPrompt === 'qvac'
? Math.min(4000, Math.floor(promptCharBudget * 0.2))
: 12000
const instructionsBudget =
backendForPrompt === 'qvac'
? Math.min(3000, Math.floor(promptCharBudget * 0.2))
: 8000
const skillsBudget =
backendForPrompt === 'qvac'
? Math.min(2000, Math.floor(promptCharBudget * 0.15))
: 4000
const workspacePromptBlock = await bareAgentLoadWorkspacePrompt(
ctx,
@@ -7005,6 +7133,27 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const backendIter = bareAgentResolveBackend(configRef.current)
/** @type {Record<string, unknown>} */
const providerNow = String(configRef.current.provider || '').trim().toLowerCase()
let qvacToolsForTurn = /** @type {unknown[]} */ ([])
if (backendIter === 'qvac') {
const profileEarly = bareAgentQvacGetProfile(
String(configRef.current.qvac_profile || 'recommended')
)
const ctxEarly = bareAgentQvacResolveCtxSize(
configRef.current,
profileEarly
)
qvacToolsForTurn =
profileEarly.tools !== false
? bareAgentFlattenToolsForQvac(tools)
: []
messages = bareAgentTrimMessagesForCtx(messages, ctxEarly, {
tools: qvacToolsForTurn,
reserveCompletion: Math.min(
1024,
Number(configRef.current.max_tokens) || 512
)
})
}
if (reasoningSettings.enabled && reasoningSettings.mode === 'trace') {
bareAgentWriteOut(
ctx,
@@ -7095,6 +7244,29 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
EDIT_ANSI_RESET +
'\n'
)
try {
const st =
typeof ctx.bareOsQvacStatus === 'function'
? ctx.bareOsQvacStatus()
: null
if (st && (st.cacheDir || st.hdmsPath || st.backendsDir)) {
bareAgentWriteOut(
ctx,
stdout,
bareEditSgr('dim', useColor) +
'[qvac] cache=' +
String(st.cacheDir || '') +
' hdms=' +
String(st.hdmsPath || '/mnt/models') +
' backends=' +
String(st.backendsDir || '(unresolved)') +
EDIT_ANSI_RESET +
'\n'
)
}
} catch {
/* ignore */
}
if (typeof ctx.bareOsQvacLoadModel === 'function') {
const loaded = await ctx.bareOsQvacLoadModel({
modelSrc,
@@ -7145,7 +7317,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
await ctx.bareOsQvacComplete({
history: messages,
tools: bareAgentFlattenToolsForQvac(tools),
tools: qvacToolsForTurn,
stream: Boolean(configRef.current.stream !== false),
captureThinking: reasoningSettings.enabled,
modelSrc,
+1 -1
View File
@@ -1,7 +1,7 @@
{
"schema": 2,
"profileId": "bare-os-posix-like",
"generatedAt": "2026-07-31T21:35:54.625Z",
"generatedAt": "2026-07-31T23:16:21.588Z",
"note": "Sparse POSIX Issue 7 coverage hints; commandIndex is generated each coreutils build.",
"commandIndex": [
{
+1 -1
View File
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1785533754625,
"atMs": 1785539781587,
"commands": [
"agent",
"appctl",
+8 -1
View File
@@ -30,10 +30,17 @@ After **`agent --config`** / **`--setup`** (or changing **`owner_name`** / **`ag
Default backend is **QVAC** (QuantumVerse Automatic Computer) — local in-process LLM via the booter **`ctx.bareOsQvac*`** bridge (`@qvac/sdk`). Weights download on first use after you pick a profile in **`agent --config`**.
Models are stored for durability on HDMS label **`models`** (guest path **`/mnt/models/`**) and materialize into a host cache for llama.cpp load:
- Host cache: **`$BARE_OS_HOST_DATA/qvac/models`** (default **`~/.bare-os/qvac/models`**)
- Runtime config: **`$BARE_OS_HOST_DATA/qvac/qvac.config.json`** (`QVAC_CONFIG_PATH`, absolute `cacheDirectory`)
First load downloads via the QVAC registry into the **host cache**, then mirrors into HDMS. If HDMS already has the GGUF, Bare OS materializes it to the host cache before load. If auto-create fails, run **`hdms create models`** after unlock.
- `backend``qvac` (default) or `rest`
- `qvac_profile``lite` / `recommended` / `strong` / `tool-tiny`
- `qvac_model` — registry id (e.g. `QWEN3_1_7B_INST_Q4`)
- `qvac_ctx_size` — optional context window override (profile defaults: lite/tool-tiny 4096, recommended/strong 8192; max 32768)
- `qvac_ctx_size` — optional context window override. Profile defaults match model cards: Qwen3 profiles **32768**, tool-tiny (Llama 3.2 1B) **131072** (max override 131072). Tool-enabled profiles floor at 8192 (tool schemas alone are ~4.5k tokens). Cap further with host env `BARE_OS_QVAC_MAX_CTX` if VRAM is tight.
- `qvac_device``gpu` (default) or `cpu` (bypass Vulkan; slower)
- `qvac_main_gpu``auto` (default: pick highest-VRAM Vulkan GPU, else CPU), `dedicated`, `integrated`, or device index (`0`, `1`, …)
- `qvac_gpu_layers` — optional layer offload count (`0` = CPU weights)
File diff suppressed because one or more lines are too long
@@ -491,13 +491,14 @@ export function createBareOsQvacBridge(opts = {}) {
if (!key) throw new Error('bareOsQvacLoadModel: modelSrc required')
const toolsEnabled = o.tools !== false
// Respect caller/profile ctx; clamp default max to 8k (old agents sent 16k/32k).
// Allow model-card windows (Qwen3 32k, Llama 3.2 1B 128k). Override with
// BARE_OS_QVAC_MAX_CTX to cap KV on small GPUs.
const maxCtxRaw = Number(env.BARE_OS_QVAC_MAX_CTX)
const maxCtx =
Number.isFinite(maxCtxRaw) && maxCtxRaw >= 2048
? Math.min(32768, Math.floor(maxCtxRaw))
: 8192
const ctxSize = Math.max(2048, Math.min(maxCtx, Number(o.ctxSize) || 4096))
? Math.min(131072, Math.floor(maxCtxRaw))
: 131072
const ctxSize = Math.max(2048, Math.min(maxCtx, Number(o.ctxSize) || 32768))
const resolved = bareOsQvacResolveDeviceConfig(o, env)
const verbEnv = Number(env.BARE_OS_QVAC_VERBOSITY)
const verbosity =
@@ -847,7 +848,10 @@ export function createBareOsQvacBridge(opts = {}) {
const s = await ensureApi()
const onEvent =
typeof o.onEvent === 'function' ? o.onEvent : () => {}
const flatTools = bareOsQvacFlattenTools(o.tools || [])
const toolsEnabled = o.toolsEnabled !== false
const flatTools = toolsEnabled
? bareOsQvacFlattenTools(o.tools || [])
: []
const history = Array.isArray(o.history) ? o.history : []
const run = s.completion({
+57 -12
View File
@@ -4,6 +4,16 @@
/** @typedef {'lite'|'recommended'|'strong'|'tool-tiny'} BareAgentQvacProfileId */
/**
* Native context windows from upstream model cards (not conservative laptop defaults).
* Qwen3 dense (0.6B/1.7B/4B): https://huggingface.co/Qwen/Qwen3-0.6B — 32,768
* Llama 3.2 1B (tool-calling finetune base): Meta Llama 3.2 — 128,000
* Absolute ceiling for overrides / host clamp.
*/
const BARE_AGENT_QVAC_CTX_QWEN3 = 32768
const BARE_AGENT_QVAC_CTX_LLAMA32_1B = 131072
const BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX = 131072
/**
* @typedef {{
* id: BareAgentQvacProfileId,
@@ -24,50 +34,69 @@ const BARE_AGENT_QVAC_PROFILES = {
id: 'lite',
label: 'Lite',
description:
'Smallest download. Good for weak machines / ~4 GB VRAM; limited tool use.',
'Smallest download (Qwen3-0.6B). Model-card context 32k; limited tool use.',
chatModel: 'QWEN3_600M_INST_Q4',
tools: false,
minRamGb: 4,
minDiskGb: 2,
approxDownloadGb: 0.5,
ctxSize: 4096
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
recommended: {
id: 'recommended',
label: 'Recommended',
description:
'Best balance for Bare OS agent tool calling (fits ~48 GB VRAM at ctx 8k).',
'Best balance for Bare OS agent tool calling (Qwen3-1.7B, model-card context 32k).',
chatModel: 'QWEN3_1_7B_INST_Q4',
tools: true,
minRamGb: 8,
minDiskGb: 5,
approxDownloadGb: 2.5,
ctxSize: 8192
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
strong: {
id: 'strong',
label: 'Strong',
description: 'Better reasoning. Needs more RAM/VRAM/disk.',
description:
'Better reasoning (Qwen3-4B). Model-card context 32k (131k with YaRN not enabled).',
chatModel: 'QWEN3_4B_INST_Q4_K_M',
tools: true,
minRamGb: 16,
minDiskGb: 8,
approxDownloadGb: 3.5,
ctxSize: 8192
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
'tool-tiny': {
id: 'tool-tiny',
label: 'Tool-tiny',
description: 'Llama tool-calling 1B fallback if Qwen tools misbehave.',
description:
'Llama 3.2 1B tool-calling fallback (model-card context 128k).',
chatModel: 'LLAMA_TOOL_CALLING_1B_INST_Q4_K',
tools: true,
minRamGb: 6,
minDiskGb: 3,
approxDownloadGb: 1,
ctxSize: 4096
ctxSize: BARE_AGENT_QVAC_CTX_LLAMA32_1B
}
}
/**
* Model-card / train context for a QVAC registry id (fallback 32k).
* @param {string} [modelId]
* @returns {number}
*/
function bareAgentQvacModelCardCtxSize(modelId) {
const id = String(modelId || '')
.trim()
.toUpperCase()
if (!id) return BARE_AGENT_QVAC_CTX_QWEN3
if (id.includes('LLAMA') || id.includes('LLAMA_TOOL')) {
return BARE_AGENT_QVAC_CTX_LLAMA32_1B
}
if (id.includes('QWEN3')) return BARE_AGENT_QVAC_CTX_QWEN3
return BARE_AGENT_QVAC_CTX_QWEN3
}
/** @returns {BareAgentQvacProfile[]} */
function bareAgentQvacProfileList() {
return Object.values(BARE_AGENT_QVAC_PROFILES)
@@ -156,17 +185,33 @@ function bareAgentResolveBackend(config) {
}
/**
* Resolve context window for QVAC load (profile default or config override).
* Resolve context window for QVAC load (profile/model-card default or config override).
* Tool schemas alone are ~4.5k tokens, so tool-enabled profiles need ≥8192.
* @param {Record<string, unknown>} config
* @param {BareAgentQvacProfile} profile
*/
function bareAgentQvacResolveCtxSize(config, profile) {
const modelId = String(
(config && (config.qvac_model || config.model)) ||
(profile && profile.chatModel) ||
''
)
const cardCtx = bareAgentQvacModelCardCtxSize(modelId)
const profileCtx = Math.max(
2048,
Number(profile && profile.ctxSize) || cardCtx
)
const raw = Number(config && config.qvac_ctx_size)
let ctx
if (Number.isFinite(raw) && raw >= 2048) {
// Cap high overrides; 32k+ KV often OOMs laptop GPUs (~4 GB VRAM).
return Math.min(32768, Math.floor(raw))
ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.floor(raw))
} else {
// Prefer explicit profile ctx (already model-card sized); else card for model id.
ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.max(profileCtx, cardCtx))
}
return Math.max(2048, Number(profile.ctxSize) || 8192)
// Full Bare OS tool list ≈ 4.5k tokens; leave room for system + reply.
if (profile && profile.tools !== false && ctx < 8192) ctx = 8192
return ctx
}
/**
@@ -369,6 +369,64 @@ function bareAgentTrimMessages(msgs, maxBytes) {
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
+51 -5
View File
@@ -900,11 +900,36 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
if (needWizard) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
const backendForPrompt = bareAgentResolveBackend(config)
const promptProfile = bareAgentQvacGetProfile(
String(config.qvac_profile || config.profile || '')
)
const promptCtxSize =
backendForPrompt === 'qvac'
? bareAgentQvacResolveCtxSize(config, promptProfile)
: 32768
// Local QVAC models have a hard ctx window; keep the system prompt lean.
const workspaceBudget = backendForPrompt === 'qvac' ? 8000 : 24000
const manBudget = backendForPrompt === 'qvac' ? 4000 : 12000
const instructionsBudget = backendForPrompt === 'qvac' ? 3000 : 8000
const skillsBudget = backendForPrompt === 'qvac' ? 2000 : 4000
// Scale char budgets from ctx (chars ≈ tokens*4); leave room for tools/reply.
const toolsOn = backendForPrompt === 'qvac' && promptProfile.tools !== false
const promptCharBudget = Math.max(
4000,
Math.floor(promptCtxSize * 4 * (toolsOn ? 0.35 : 0.7))
)
const workspaceBudget =
backendForPrompt === 'qvac'
? Math.min(8000, Math.floor(promptCharBudget * 0.35))
: 24000
const manBudget =
backendForPrompt === 'qvac'
? Math.min(4000, Math.floor(promptCharBudget * 0.2))
: 12000
const instructionsBudget =
backendForPrompt === 'qvac'
? Math.min(3000, Math.floor(promptCharBudget * 0.2))
: 8000
const skillsBudget =
backendForPrompt === 'qvac'
? Math.min(2000, Math.floor(promptCharBudget * 0.15))
: 4000
const workspacePromptBlock = await bareAgentLoadWorkspacePrompt(
ctx,
@@ -1059,6 +1084,27 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const backendIter = bareAgentResolveBackend(configRef.current)
/** @type {Record<string, unknown>} */
const providerNow = String(configRef.current.provider || '').trim().toLowerCase()
let qvacToolsForTurn = /** @type {unknown[]} */ ([])
if (backendIter === 'qvac') {
const profileEarly = bareAgentQvacGetProfile(
String(configRef.current.qvac_profile || 'recommended')
)
const ctxEarly = bareAgentQvacResolveCtxSize(
configRef.current,
profileEarly
)
qvacToolsForTurn =
profileEarly.tools !== false
? bareAgentFlattenToolsForQvac(tools)
: []
messages = bareAgentTrimMessagesForCtx(messages, ctxEarly, {
tools: qvacToolsForTurn,
reserveCompletion: Math.min(
1024,
Number(configRef.current.max_tokens) || 512
)
})
}
if (reasoningSettings.enabled && reasoningSettings.mode === 'trace') {
bareAgentWriteOut(
ctx,
@@ -1222,7 +1268,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
await ctx.bareOsQvacComplete({
history: messages,
tools: bareAgentFlattenToolsForQvac(tools),
tools: qvacToolsForTurn,
stream: Boolean(configRef.current.stream !== false),
captureThinking: reasoningSettings.enabled,
modelSrc,
@@ -40,7 +40,7 @@ First load downloads via the QVAC registry into the **host cache**, then mirrors
- `backend``qvac` (default) or `rest`
- `qvac_profile``lite` / `recommended` / `strong` / `tool-tiny`
- `qvac_model` — registry id (e.g. `QWEN3_1_7B_INST_Q4`)
- `qvac_ctx_size` — optional context window override (profile defaults: lite/tool-tiny 4096, recommended/strong 8192; max 32768)
- `qvac_ctx_size` — optional context window override. Profile defaults match model cards: Qwen3 profiles **32768**, tool-tiny (Llama 3.2 1B) **131072** (max override 131072). Tool-enabled profiles floor at 8192 (tool schemas alone are ~4.5k tokens). Cap further with host env `BARE_OS_QVAC_MAX_CTX` if VRAM is tight.
- `qvac_device``gpu` (default) or `cpu` (bypass Vulkan; slower)
- `qvac_main_gpu``auto` (default: pick highest-VRAM Vulkan GPU, else CPU), `dedicated`, `integrated`, or device index (`0`, `1`, …)
- `qvac_gpu_layers` — optional layer offload count (`0` = CPU weights)
@@ -10,7 +10,7 @@ function loadAgentQvacHelpers() {
vm.createContext(sandbox)
vm.runInContext(
QVAC_SRC +
'\n;this.__exports = { bareAgentFlattenToolsForQvac, bareAgentQvacGetProfile, bareAgentQvacProfileList, bareAgentResolveBackend, bareAgentQvacBridgeAvailable, bareAgentQvacResolveCtxSize, bareAgentQvacResolveDeviceOpts }',
'\n;this.__exports = { bareAgentFlattenToolsForQvac, bareAgentQvacGetProfile, bareAgentQvacProfileList, bareAgentResolveBackend, bareAgentQvacBridgeAvailable, bareAgentQvacResolveCtxSize, bareAgentQvacResolveDeviceOpts, bareAgentQvacModelCardCtxSize, BARE_AGENT_QVAC_CTX_QWEN3, BARE_AGENT_QVAC_CTX_LLAMA32_1B }',
sandbox
)
return sandbox.__exports
@@ -40,18 +40,33 @@ test('qvac profiles include recommended + lite', async (t) => {
const list = bareAgentQvacProfileList()
t.ok(list.length >= 4)
t.is(bareAgentQvacGetProfile('recommended').chatModel, 'QWEN3_1_7B_INST_Q4')
t.is(bareAgentQvacGetProfile('recommended').ctxSize, 8192)
t.is(bareAgentQvacGetProfile('recommended').ctxSize, 32768)
t.is(bareAgentQvacGetProfile('lite').id, 'lite')
t.is(bareAgentQvacGetProfile('lite').ctxSize, 4096)
t.is(bareAgentQvacGetProfile('lite').ctxSize, 32768)
t.is(bareAgentQvacGetProfile('tool-tiny').ctxSize, 131072)
t.is(bareAgentQvacGetProfile('strong').ctxSize, 32768)
t.is(bareAgentQvacGetProfile('nope').id, 'recommended')
})
test('model-card ctx sizes match upstream cards', async (t) => {
const { bareAgentQvacModelCardCtxSize } = loadAgentQvacHelpers()
t.is(bareAgentQvacModelCardCtxSize('QWEN3_600M_INST_Q4'), 32768)
t.is(bareAgentQvacModelCardCtxSize('QWEN3_1_7B_INST_Q4'), 32768)
t.is(bareAgentQvacModelCardCtxSize('QWEN3_4B_INST_Q4_K_M'), 32768)
t.is(bareAgentQvacModelCardCtxSize('LLAMA_TOOL_CALLING_1B_INST_Q4_K'), 131072)
})
test('resolve qvac ctx size from profile or config override', async (t) => {
const { bareAgentQvacGetProfile, bareAgentQvacResolveCtxSize } = loadAgentQvacHelpers()
const rec = bareAgentQvacGetProfile('recommended')
t.is(bareAgentQvacResolveCtxSize({}, rec), 8192)
t.is(bareAgentQvacResolveCtxSize({ qvac_ctx_size: 4096 }, rec), 4096)
t.is(bareAgentQvacResolveCtxSize({ qvac_ctx_size: 999999 }, rec), 32768)
const lite = bareAgentQvacGetProfile('lite')
const tiny = bareAgentQvacGetProfile('tool-tiny')
t.is(bareAgentQvacResolveCtxSize({}, rec), 32768)
t.is(bareAgentQvacResolveCtxSize({ qvac_ctx_size: 4096 }, rec), 8192) // tools need ≥8k
t.is(bareAgentQvacResolveCtxSize({ qvac_ctx_size: 4096 }, lite), 4096) // lite tools=false
t.is(bareAgentQvacResolveCtxSize({ qvac_ctx_size: 999999 }, rec), 131072)
t.is(bareAgentQvacResolveCtxSize({}, lite), 32768)
t.is(bareAgentQvacResolveCtxSize({}, tiny), 131072)
})
test('resolve qvac device opts', async (t) => {
@@ -0,0 +1,35 @@
import test from 'brittle'
import { readFileSync } from 'node:fs'
import vm from 'node:vm'
const STATE_SRC = readFileSync(new URL('../lib/agent-state.js', import.meta.url), 'utf8')
function loadTrimHelpers() {
const sandbox = { console, TextEncoder, TextDecoder }
vm.createContext(sandbox)
vm.runInContext(
STATE_SRC +
'\n;this.__exports = { bareAgentTrimMessages, bareAgentTrimMessagesForCtx, bareAgentEstimateTokens }',
sandbox
)
return sandbox.__exports
}
test('trim for ctx shrinks system when tools consume budget', async (t) => {
const { bareAgentTrimMessagesForCtx, bareAgentEstimateTokens } = loadTrimHelpers()
const tools = [{ name: 'x', description: 'y'.repeat(8000), parameters: {} }]
const msgs = [
{ role: 'system', content: 'S'.repeat(20_000) },
{ role: 'user', content: 'hello' }
]
const out = bareAgentTrimMessagesForCtx(msgs, 4096, {
tools,
reserveCompletion: 512
})
t.is(out.length, 2)
t.ok(String(/** @type {any} */ (out[0]).content).length < 20_000)
t.ok(
bareAgentEstimateTokens(out) + bareAgentEstimateTokens(tools) + 512 <=
4096 + 200
)
})
+189 -17
View File
@@ -1404,6 +1404,64 @@ function bareAgentTrimMessages(msgs, maxBytes) {
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
@@ -1597,6 +1655,16 @@ async function bareAgentResetChatSession(ctx, paths, argv0) {
/** @typedef {'lite'|'recommended'|'strong'|'tool-tiny'} BareAgentQvacProfileId */
/**
* Native context windows from upstream model cards (not conservative laptop defaults).
* Qwen3 dense (0.6B/1.7B/4B): https://huggingface.co/Qwen/Qwen3-0.6B — 32,768
* Llama 3.2 1B (tool-calling finetune base): Meta Llama 3.2 — 128,000
* Absolute ceiling for overrides / host clamp.
*/
const BARE_AGENT_QVAC_CTX_QWEN3 = 32768
const BARE_AGENT_QVAC_CTX_LLAMA32_1B = 131072
const BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX = 131072
/**
* @typedef {{
* id: BareAgentQvacProfileId,
@@ -1617,50 +1685,69 @@ const BARE_AGENT_QVAC_PROFILES = {
id: 'lite',
label: 'Lite',
description:
'Smallest download. Good for weak machines / ~4 GB VRAM; limited tool use.',
'Smallest download (Qwen3-0.6B). Model-card context 32k; limited tool use.',
chatModel: 'QWEN3_600M_INST_Q4',
tools: false,
minRamGb: 4,
minDiskGb: 2,
approxDownloadGb: 0.5,
ctxSize: 4096
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
recommended: {
id: 'recommended',
label: 'Recommended',
description:
'Best balance for Bare OS agent tool calling (fits ~48 GB VRAM at ctx 8k).',
'Best balance for Bare OS agent tool calling (Qwen3-1.7B, model-card context 32k).',
chatModel: 'QWEN3_1_7B_INST_Q4',
tools: true,
minRamGb: 8,
minDiskGb: 5,
approxDownloadGb: 2.5,
ctxSize: 8192
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
strong: {
id: 'strong',
label: 'Strong',
description: 'Better reasoning. Needs more RAM/VRAM/disk.',
description:
'Better reasoning (Qwen3-4B). Model-card context 32k (131k with YaRN not enabled).',
chatModel: 'QWEN3_4B_INST_Q4_K_M',
tools: true,
minRamGb: 16,
minDiskGb: 8,
approxDownloadGb: 3.5,
ctxSize: 8192
ctxSize: BARE_AGENT_QVAC_CTX_QWEN3
},
'tool-tiny': {
id: 'tool-tiny',
label: 'Tool-tiny',
description: 'Llama tool-calling 1B fallback if Qwen tools misbehave.',
description:
'Llama 3.2 1B tool-calling fallback (model-card context 128k).',
chatModel: 'LLAMA_TOOL_CALLING_1B_INST_Q4_K',
tools: true,
minRamGb: 6,
minDiskGb: 3,
approxDownloadGb: 1,
ctxSize: 4096
ctxSize: BARE_AGENT_QVAC_CTX_LLAMA32_1B
}
}
/**
* Model-card / train context for a QVAC registry id (fallback 32k).
* @param {string} [modelId]
* @returns {number}
*/
function bareAgentQvacModelCardCtxSize(modelId) {
const id = String(modelId || '')
.trim()
.toUpperCase()
if (!id) return BARE_AGENT_QVAC_CTX_QWEN3
if (id.includes('LLAMA') || id.includes('LLAMA_TOOL')) {
return BARE_AGENT_QVAC_CTX_LLAMA32_1B
}
if (id.includes('QWEN3')) return BARE_AGENT_QVAC_CTX_QWEN3
return BARE_AGENT_QVAC_CTX_QWEN3
}
/** @returns {BareAgentQvacProfile[]} */
function bareAgentQvacProfileList() {
return Object.values(BARE_AGENT_QVAC_PROFILES)
@@ -1749,17 +1836,33 @@ function bareAgentResolveBackend(config) {
}
/**
* Resolve context window for QVAC load (profile default or config override).
* Resolve context window for QVAC load (profile/model-card default or config override).
* Tool schemas alone are ~4.5k tokens, so tool-enabled profiles need ≥8192.
* @param {Record<string, unknown>} config
* @param {BareAgentQvacProfile} profile
*/
function bareAgentQvacResolveCtxSize(config, profile) {
const modelId = String(
(config && (config.qvac_model || config.model)) ||
(profile && profile.chatModel) ||
''
)
const cardCtx = bareAgentQvacModelCardCtxSize(modelId)
const profileCtx = Math.max(
2048,
Number(profile && profile.ctxSize) || cardCtx
)
const raw = Number(config && config.qvac_ctx_size)
let ctx
if (Number.isFinite(raw) && raw >= 2048) {
// Cap high overrides; 32k+ KV often OOMs laptop GPUs (~4 GB VRAM).
return Math.min(32768, Math.floor(raw))
ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.floor(raw))
} else {
// Prefer explicit profile ctx (already model-card sized); else card for model id.
ctx = Math.min(BARE_AGENT_QVAC_CTX_ABSOLUTE_MAX, Math.max(profileCtx, cardCtx))
}
return Math.max(2048, Number(profile.ctxSize) || 8192)
// Full Bare OS tool list ≈ 4.5k tokens; leave room for system + reply.
if (profile && profile.tools !== false && ctx < 8192) ctx = 8192
return ctx
}
/**
@@ -6846,11 +6949,36 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
if (needWizard) await bareAgentSyncWorkspaceFromConfig(ctx, paths, config)
const backendForPrompt = bareAgentResolveBackend(config)
const promptProfile = bareAgentQvacGetProfile(
String(config.qvac_profile || config.profile || '')
)
const promptCtxSize =
backendForPrompt === 'qvac'
? bareAgentQvacResolveCtxSize(config, promptProfile)
: 32768
// Local QVAC models have a hard ctx window; keep the system prompt lean.
const workspaceBudget = backendForPrompt === 'qvac' ? 8000 : 24000
const manBudget = backendForPrompt === 'qvac' ? 4000 : 12000
const instructionsBudget = backendForPrompt === 'qvac' ? 3000 : 8000
const skillsBudget = backendForPrompt === 'qvac' ? 2000 : 4000
// Scale char budgets from ctx (chars ≈ tokens*4); leave room for tools/reply.
const toolsOn = backendForPrompt === 'qvac' && promptProfile.tools !== false
const promptCharBudget = Math.max(
4000,
Math.floor(promptCtxSize * 4 * (toolsOn ? 0.35 : 0.7))
)
const workspaceBudget =
backendForPrompt === 'qvac'
? Math.min(8000, Math.floor(promptCharBudget * 0.35))
: 24000
const manBudget =
backendForPrompt === 'qvac'
? Math.min(4000, Math.floor(promptCharBudget * 0.2))
: 12000
const instructionsBudget =
backendForPrompt === 'qvac'
? Math.min(3000, Math.floor(promptCharBudget * 0.2))
: 8000
const skillsBudget =
backendForPrompt === 'qvac'
? Math.min(2000, Math.floor(promptCharBudget * 0.15))
: 4000
const workspacePromptBlock = await bareAgentLoadWorkspacePrompt(
ctx,
@@ -7005,6 +7133,27 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
const backendIter = bareAgentResolveBackend(configRef.current)
/** @type {Record<string, unknown>} */
const providerNow = String(configRef.current.provider || '').trim().toLowerCase()
let qvacToolsForTurn = /** @type {unknown[]} */ ([])
if (backendIter === 'qvac') {
const profileEarly = bareAgentQvacGetProfile(
String(configRef.current.qvac_profile || 'recommended')
)
const ctxEarly = bareAgentQvacResolveCtxSize(
configRef.current,
profileEarly
)
qvacToolsForTurn =
profileEarly.tools !== false
? bareAgentFlattenToolsForQvac(tools)
: []
messages = bareAgentTrimMessagesForCtx(messages, ctxEarly, {
tools: qvacToolsForTurn,
reserveCompletion: Math.min(
1024,
Number(configRef.current.max_tokens) || 512
)
})
}
if (reasoningSettings.enabled && reasoningSettings.mode === 'trace') {
bareAgentWriteOut(
ctx,
@@ -7095,6 +7244,29 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
EDIT_ANSI_RESET +
'\n'
)
try {
const st =
typeof ctx.bareOsQvacStatus === 'function'
? ctx.bareOsQvacStatus()
: null
if (st && (st.cacheDir || st.hdmsPath || st.backendsDir)) {
bareAgentWriteOut(
ctx,
stdout,
bareEditSgr('dim', useColor) +
'[qvac] cache=' +
String(st.cacheDir || '') +
' hdms=' +
String(st.hdmsPath || '/mnt/models') +
' backends=' +
String(st.backendsDir || '(unresolved)') +
EDIT_ANSI_RESET +
'\n'
)
}
} catch {
/* ignore */
}
if (typeof ctx.bareOsQvacLoadModel === 'function') {
const loaded = await ctx.bareOsQvacLoadModel({
modelSrc,
@@ -7145,7 +7317,7 @@ async function bareOsRunAgentSession(ctx, argv0, task, runOpts) {
}
await ctx.bareOsQvacComplete({
history: messages,
tools: bareAgentFlattenToolsForQvac(tools),
tools: qvacToolsForTurn,
stream: Boolean(configRef.current.stream !== false),
captureThinking: reasoningSettings.enabled,
modelSrc,
@@ -1,7 +1,7 @@
{
"schema": 2,
"profileId": "bare-os-posix-like",
"generatedAt": "2026-07-31T21:35:54.625Z",
"generatedAt": "2026-07-31T23:16:21.588Z",
"note": "Sparse POSIX Issue 7 coverage hints; commandIndex is generated each coreutils build.",
"commandIndex": [
{
@@ -1,6 +1,6 @@
{
"schema": 1,
"atMs": 1785533754625,
"atMs": 1785539781587,
"commands": [
"agent",
"appctl",
@@ -30,10 +30,17 @@ After **`agent --config`** / **`--setup`** (or changing **`owner_name`** / **`ag
Default backend is **QVAC** (QuantumVerse Automatic Computer) — local in-process LLM via the booter **`ctx.bareOsQvac*`** bridge (`@qvac/sdk`). Weights download on first use after you pick a profile in **`agent --config`**.
Models are stored for durability on HDMS label **`models`** (guest path **`/mnt/models/`**) and materialize into a host cache for llama.cpp load:
- Host cache: **`$BARE_OS_HOST_DATA/qvac/models`** (default **`~/.bare-os/qvac/models`**)
- Runtime config: **`$BARE_OS_HOST_DATA/qvac/qvac.config.json`** (`QVAC_CONFIG_PATH`, absolute `cacheDirectory`)
First load downloads via the QVAC registry into the **host cache**, then mirrors into HDMS. If HDMS already has the GGUF, Bare OS materializes it to the host cache before load. If auto-create fails, run **`hdms create models`** after unlock.
- `backend``qvac` (default) or `rest`
- `qvac_profile``lite` / `recommended` / `strong` / `tool-tiny`
- `qvac_model` — registry id (e.g. `QWEN3_1_7B_INST_Q4`)
- `qvac_ctx_size` — optional context window override (profile defaults: lite/tool-tiny 4096, recommended/strong 8192; max 32768)
- `qvac_ctx_size` — optional context window override. Profile defaults match model cards: Qwen3 profiles **32768**, tool-tiny (Llama 3.2 1B) **131072** (max override 131072). Tool-enabled profiles floor at 8192 (tool schemas alone are ~4.5k tokens). Cap further with host env `BARE_OS_QVAC_MAX_CTX` if VRAM is tight.
- `qvac_device``gpu` (default) or `cpu` (bypass Vulkan; slower)
- `qvac_main_gpu``auto` (default: pick highest-VRAM Vulkan GPU, else CPU), `dedicated`, `integrated`, or device index (`0`, `1`, …)
- `qvac_gpu_layers` — optional layer offload count (`0` = CPU weights)
File diff suppressed because one or more lines are too long