Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions config/clawbox-workspace-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,21 @@ Edge cases:
- **Overwrite on rename.** If they later say "actually, call me X instead", call `preferences_set` again with the new value.
- **Clear on anonymity request.** If they ask not to be named, or want it reset, `preferences_set('{"ui_user_name": ""}')`.
- **Don't echo back the stored name as confirmation** unless they explicitly ask. Silently doing the right thing is the vibe.

---

<!-- clawbox:ai-model-limits -->
## What ClawBox AI can actually hold

When you run on ClawBox AI (`deepseek-v4-flash` or `deepseek-v4-pro`), these are the real limits of the session you are in:

- **Context window: 1,000,000 tokens.**
- **Output: up to 393,216 tokens** in a single reply.
- **Text in, text out.** Image attachments are rejected upstream.

**Do not quote a smaller number if the user asks how much context you have.** The model weights were trained before this window existed, so asked cold you will tend to answer "128K" — that is a memory of an older DeepSeek, not a fact about this device. The numbers above are what the gateway configures and what the proxy enforces; verified on hardware with a 902,116-token prompt.

Two things follow from a window this large:

- **You rarely need to summarise to survive.** Reading a long file or a whole conversation in full is usually cheaper and more accurate than compacting it early.
- **Keep the start of the context stable.** Upstream caches on prefix, so an unchanged opening is billed at roughly a tenth of the price and returns much faster — a 902K-token prompt drops from ~31s to ~6s on a cache hit. Reordering or rewriting early context throws that away for the entire remainder.
30 changes: 30 additions & 0 deletions scripts/gateway-pre-start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,36 @@ if [ -d "$CLAWBOX_WORKSPACE" ] && [ -f "$CLAWBOX_GUIDE_SRC" ]; then
# already mention CLAWBOX.md, so the agent loads our guide as part of
# its session-start context without us having to overwrite AGENTS.md
# (which the agent may have personalized).
# --- clawbox-md-ai-limits (extracted verbatim by tests) ---
# A CLAWBOX.md seeded before this section existed has no statement of what
# the ClawBox AI window actually is, and the model will not supply one: asked
# cold, V4 answers "128K" from its own training data, which is wrong by a
# factor of eight and reads to a customer like a spec sheet. Seeding is
# seed-if-missing, so those boxes would never get the correction. Append it
# to an existing file instead, guarded by the marker so a second gateway
# start is a no-op and anything the owner wrote is left where it is.
if [ -f "$CLAWBOX_GUIDE_DST" ] && ! grep -qF "clawbox:ai-model-limits" "$CLAWBOX_GUIDE_DST"; then
cat >> "$CLAWBOX_GUIDE_DST" <<'CLAWBOX_MD_AI_LIMITS'

---

<!-- clawbox:ai-model-limits -->
## What ClawBox AI can actually hold

When you run on ClawBox AI (`deepseek-v4-flash` or `deepseek-v4-pro`), these are the real limits of the session you are in:

- **Context window: 1000000 tokens.**
- **Output: up to 393216 tokens** in a single reply.
- **Text in, text out.** Image attachments are rejected upstream.

**Do not quote a smaller number if the user asks how much context you have.** The model weights were trained before this window existed, so asked cold you will tend to answer "128K" — that is a memory of an older DeepSeek, not a fact about this device.

Keep the start of the context stable: upstream caches on prefix, so an unchanged opening is billed at roughly a tenth of the price and returns much faster. Rewriting early context throws that away for the entire remainder.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
CLAWBOX_MD_AI_LIMITS
echo " Appended AI model limits to CLAWBOX.md"
fi
# --- end clawbox-md-ai-limits ---

CLAWBOX_AGENTS_MD="$CLAWBOX_WORKSPACE/AGENTS.md"
if [ -f "$CLAWBOX_AGENTS_MD" ] && ! grep -qF "CLAWBOX.md" "$CLAWBOX_AGENTS_MD"; then
printf '\n\n## ClawBox integration\n\nSee `CLAWBOX.md` for device-specific conventions: where user-installed skills live, how to control the desktop Chromium via `browser_*` tools, and how to install/uninstall skills through the App Store.\n' >> "$CLAWBOX_AGENTS_MD"
Expand Down
102 changes: 102 additions & 0 deletions src/tests/unit/gateway-pre-start-clawbox-md.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import { mkdtempSync, rmSync, writeFileSync, readFileSync, existsSync } from "node:fs";
import { execFileSync, spawnSync } from "node:child_process";
import { tmpdir } from "node:os";
import path from "node:path";

// Asked how much context it has, V4 answers "128K" — a memory of an older
// DeepSeek, not a fact about this device, and eight times smaller than the
// window the box actually configures. Observed on hardware on 2026-08-18: the
// same box that had just answered a 902,116-token prompt said "128K tokens" in
// the chat app. Nothing in the prompt told it otherwise, so CLAWBOX.md now
// does — and because CLAWBOX.md is seeded only when absent, boxes already in
// the field need the section appended rather than seeded.
//
// This runs the append block out of the shipped .sh, not a copy, so the test
// fails if the real script drifts.

const SCRIPT = path.resolve(process.cwd(), "scripts/gateway-pre-start.sh");
const GUIDE = path.resolve(process.cwd(), "config/clawbox-workspace-guide.md");
const hasBash = spawnSync("bash", ["--version"], { stdio: "ignore" }).status === 0;

const MARKER = "clawbox:ai-model-limits";

/** Pull the CLAWBOX.md append block out of the .sh verbatim. */
function extractAppendBlock(): string {
const src = readFileSync(SCRIPT, "utf-8");
const start = src.indexOf(" # --- clawbox-md-ai-limits (extracted verbatim by tests) ---");
const end = src.indexOf(" # --- end clawbox-md-ai-limits ---", start);
if (start < 0 || end < 0) throw new Error("clawbox-md-ai-limits block not found");
return src.slice(start, end);
}

const BLOCK = hasBash ? extractAppendBlock() : "";

let dir: string;
let guide: string;
beforeEach(() => {
dir = mkdtempSync(path.join(tmpdir(), "clawbox-md-"));
guide = path.join(dir, "CLAWBOX.md");
});
afterEach(() => rmSync(dir, { recursive: true, force: true }));

/** Run the extracted block against a CLAWBOX.md in a temp dir. */
function run(): string {
const program = `set -eu\nCLAWBOX_GUIDE_DST="${guide}"\n${BLOCK}\n`;
execFileSync("bash", ["-c", program], { encoding: "utf-8" });
return existsSync(guide) ? readFileSync(guide, "utf-8") : "";
}

describe.skipIf(!hasBash)("gateway-pre-start.sh CLAWBOX.md model limits", () => {
it("appends the section to a guide that predates it", () => {
writeFileSync(guide, "# ClawBox Integration Guide\n\nolder content\n");
const out = run();
expect(out).toContain(MARKER);
expect(out).toContain("1000000");
expect(out).toContain("393216");
});

it("keeps what the owner already wrote", () => {
writeFileSync(guide, "# ClawBox Integration Guide\n\nmy own note about the office printer\n");
expect(run()).toContain("my own note about the office printer");
});

it("is idempotent — a second gateway start appends nothing", () => {
writeFileSync(guide, "# ClawBox Integration Guide\n");
const once = run();
const twice = run();
expect(twice).toBe(once);
expect(twice.match(new RegExp(MARKER, "g"))).toHaveLength(1);
});

it("does not create the file when the box has no guide at all", () => {
// Seeding is the other half of the script's job and owns that case; this
// block must not conjure a CLAWBOX.md outside a workspace that exists.
expect(run()).toBe("");
expect(existsSync(guide)).toBe(false);
});

it("tells the agent not to repeat the 128K it will otherwise guess", () => {
writeFileSync(guide, "# ClawBox Integration Guide\n");
expect(run()).toContain("128K");
});

it("states the same numbers the provider migration writes", () => {
// One drifting without the other is how the picker came to claim 128K in
// the first place: two places holding the same fact, only one maintained.
const src = readFileSync(SCRIPT, "utf-8");
expect(src).toContain('model["contextWindow"] = 1000000');
expect(src).toContain('model["maxTokens"] = 393216');
writeFileSync(guide, "# ClawBox Integration Guide\n");
const out = run();
expect(out).toContain("1000000");
expect(out).toContain("393216");
});

it("ships the same section in the seeded template, so fresh boxes match upgraded ones", () => {
const template = readFileSync(GUIDE, "utf-8");
expect(template).toContain(MARKER);
expect(template).toContain("1,000,000");
expect(template).toContain("393,216");
});
});
Loading