DEV Community

Jordan Huang
Jordan Huang

Posted on

FAQ: Five Myths About What Survives the Agent Session

Did the token die with the chat tab?

I keep seeing the same five confident claims. They show up after a successful agent run.

The chat looks confident, but the box disagrees. This FAQ is about persistence across agent sessions.

It is not about TTY tricks at all. It is not about which python binary answered.

Those are other problems for other days. This one is simpler, and it is meaner.

What still exists after the model stops talking?

Why this FAQ exists

Agents mix three stores. People treat them as one store.

  • The model's context window
  • The shell process environment
  • Files sitting on the server disk

Those three drift apart very fast. A remote box makes that drift obvious.

Your laptop PATH is not on that box. A model can narrate pip install in fluent prose.

Narration is not site-packages. A closed browser tab is not rm -rf.

Disclosure: This article was prepared as part of MonkeyCode's product outreach.

I use MonkeyCode when I want a free model and a free server in one loop. I do not name models, quotas, hardware, or how long that free option lasts. The checks below work on any POSIX box you can actually exec on.

The artifact: dump the three stores

Do not argue with the transcript. Print state instead.

I keep this script in the repo root. I paste it as a command, not as a question.

Label this as a workflow to run. It is not a benchmark I already ran.

#!/usr/bin/env bash
# session_audit.sh — proposed check, run on the remote box
set -euo pipefail

echo "=== identity ==="
whoami
id
hostname
pwd
date -u +%Y-%m-%dT%H:%M:%SZ

echo "=== process env (redacted) ==="
python3 - <<'PY'
import os, re
secret = re.compile(r"(KEY|TOKEN|SECRET|PASSWORD|PASSWD|CREDENTIAL|AUTH)", re.I)
for k, v in sorted(os.environ.items()):
    if secret.search(k) or secret.search(v or ""):
        print(f"{k}=<redacted len={len(v)}>")
    else:
        print(f"{k}={v}")
PY

echo "=== claimed vs installed (python) ==="
python3 - <<'PY'
import importlib.util, sys
print("executable:", sys.executable)
print("version:", sys.version.replace("\n", " "))
print("path:")
for p in sys.path:
    print(" ", p)
wanted = ["fastapi", "pytest", "requests", "numpy"]
for name in wanted:
    spec = importlib.util.find_spec(name)
    print(f"import {name}: {'FOUND' if spec else 'MISSING'}")
PY

echo "=== git identity on THIS box ==="
git config --show-origin --get-regexp 'user\.(name|email)' || true
git status -sb || true

echo "=== files the chat may have invented ==="
ls -la .env .env.local .netrc 2>/dev/null || true
test -f .env && echo "WARNING: .env exists on disk"
Enter fullscreen mode Exit fullscreen mode

Run it twice. Once now, once in a later turn.

Compare the two dumps yourself. Do not compare two chat summaries.

Myth 1: Closing the tab wipes the box

Does killing the browser kill the server disk?

Usually no. A tab is only a client.

A server is a machine with a filesystem. Files you wrote can still sit there.

So can .env. So can a stray key file. So can a venv the model built.

Corrected mental model

  • UI session is not a process
  • Process state is not disk state
  • Disk state is not model memory

Evidence check

stat -c '%n %y %s' .env credentials.json 2>/dev/null || echo "no secret files found"
find . -maxdepth 2 -name '*.pem' -o -name 'id_rsa' 2>/dev/null
Enter fullscreen mode Exit fullscreen mode

If stat prints a timestamp, the file survived. The chat did not need to stay open.

Myth 2: The model knows the library, so the box has it

The model can write FastAPI code from training. That is not pip show fastapi.

I ask a blunt question instead. Can this interpreter import it right now?

python3 -c "import fastapi, sys; print(sys.executable, fastapi.__file__)"
command -v pytest || echo "pytest not on PATH"
Enter fullscreen mode Exit fullscreen mode

If that fails, the essay still looks fine. The runtime is still empty.

Corrected mental model

  • Training data is not site-packages
  • A generated requirements.txt is not an installed venv
  • pip install in the transcript is not a finished install

Evidence is the import. Not the prose around it.

Did the install even target this interpreter? Pin it.

python3 -m pip --version
python3 -m pip show pytest || true
readlink -f "$(command -v python3)"
Enter fullscreen mode Exit fullscreen mode

Myth 3: export in turn 3 is still there in turn 9

Did you export DATABASE_URL in an earlier command?

That export lived in that shell process. A new tool call may be a new process.

Some agent runners reuse one shell. Some spawn bash -lc every time.

You cannot guess from the chat tone. Guessing is how tokens leak into stories.

Corrected mental model

  • export is process state only
  • A later tool call may spawn a fresh shell
  • Parent env is not a promise

Evidence check

Use a canary, not a memory.

# turn A
export SESSION_CANARY=alive-$$
echo "canary_pid=$$"

# turn B, later, no story — just print
python3 -c 'import os; print(os.environ.get("SESSION_CANARY", "MISSING"))'
Enter fullscreen mode Exit fullscreen mode

If turn B prints MISSING, stop trusting spoken env. Write a file, or use a real secret store.

Need the value in later commands? Do not export and pray.

umask 077
printf 'SESSION_CANARY=%s\n' "alive" > .session_canary
# later
set -a
# still do not source untrusted files blindly
python3 -c 'from pathlib import Path; print(Path(".session_canary").read_text())'
Enter fullscreen mode Exit fullscreen mode

Myth 4: A .env in the chat is the process environment

The agent often writes a .env example. Then it says the app is configured.

Who loaded it? Which process? With what parser?

dotenv is not automatic. Your framework may ignore the file until boot.

A fenced block in chat is not even a file yet. Ask test -f before you relax.

Corrected mental model

  • File on disk is not os.environ
  • A quoted block in chat is not a file
  • source .env breaks on spaces, quotes, and $

Evidence check

Two booleans. They often disagree.

python3 - <<'PY'
from pathlib import Path
import os
p = Path(".env")
print("dotenv_exists", p.is_file())
print("environ_has_DATABASE_URL", "DATABASE_URL" in os.environ)
if p.is_file():
    print("dotenv_bytes", p.stat().st_size)
    print("dotenv_mode", oct(p.stat().st_mode))
PY
Enter fullscreen mode Exit fullscreen mode

If the file exists and the key is missing, nobody loaded it. If the key exists and the file does not, it came from the process, not from .env.

Myth 5: git config on the box is your laptop identity

Did the agent commit as you?

Check user.email on the server. Then check author on the latest commit.

Those two strings can differ. Either one can be wrong.

git config --show-origin --get user.email || echo "no email configured"
git config --show-origin --get user.name || echo "no name configured"
git log -1 --format='%an <%ae>%n%cn <%ce>' 2>/dev/null || echo "no commits"
Enter fullscreen mode Exit fullscreen mode

A free box may have leftover global config. It may have none at all.

Your laptop ~/.gitconfig did not travel with the prompt. A forged author is still a git object.

Corrected mental model

  • Laptop git config stays on the laptop
  • Commit author is local to that user or that repo
  • git status in chat is not git status on disk

Do not push until the audit matches the person you want. Then check git remote -v too.

git remote -v
git config --get-regexp 'credential|insteadOf' || true
Enter fullscreen mode Exit fullscreen mode

Decision table I actually use

Claim in chat Store it lives in Command that falsifies it
"I installed pytest" disk / venv python3 -c "import pytest"
"TOKEN is set" process env python3 -c "import os; print('TOKEN' in os.environ)"
".env is loaded" file vs process file exists and key in os.environ
"we are on main" git objects git status -sb && git rev-parse --short HEAD
"that file is gone" disk stat path
"this is your email" git config git config --show-origin --get user.email
"install finished" process exit + disk echo $? then pip show

If the command disagrees, the chat is wrong. Keep the command output.

A tiny workflow for a free model plus a free server

I do not start with a feature request. I start with inventory.

  1. Open a remote shell on the free server.
  2. Run session_audit.sh before any install.
  3. Ask the model for a change set, not a story.
  4. Apply the change. Run the audit again.
  5. Diff the two dumps. Keep only proven state.

The free model is for proposing diffs. The free server is for executing checks.

Do not let the model grade its own persistence. It cannot see a process it no longer owns.

Diff without poetry:

./session_audit.sh | tee /tmp/audit-before.txt
# ... agent edits ...
./session_audit.sh | tee /tmp/audit-after.txt
diff -u /tmp/audit-before.txt /tmp/audit-after.txt || true
Enter fullscreen mode Exit fullscreen mode

The diff is the lesson. The victory message is not.

Limitations

This audit is local truth. It is not a security review.

It will not tell you if another tenant can read the disk. I have no evidence about isolation on any vendor box.

It will not prove a package stays after a rebuild. I did not measure lifetime, and I will not invent one.

Redaction by regex misses values without KEY in the name. Treat dumps as sensitive anyway.

python3 in the script may not be the app interpreter. Point it at the real venv when you have one.

# proposed, only if you already know the venv path
./.venv/bin/python -c "import sys; print(sys.executable)"
Enter fullscreen mode Exit fullscreen mode

Timezone in date -u is UTC. Your app logs may not be. find above is shallow on purpose.

This assumes POSIX bash, python3, and git. Windows shells need a different dump.

Who should not use this approach

Skip this if you must park production secrets on the box. A free remote server is the wrong place for those.

Skip this if you need an SLA on disk. I did not claim files last any number of days.

Skip this if you cannot run commands, only chat. Then you have no evidence at all.

Skip this if the box is shared and you cannot control umask and file modes.

What I want you to remember

The transcript is a story. The dump is a measurement.

Ask one question after every "done." Which store changed?

If you cannot name the store, you do not have the result. Context, process, and disk are not synonyms.

If you already have a remote shell, run the script today. If you are on MonkeyCode's free server, paste it there and keep the dump. Trust the second copy, not the victory message.

Top comments (0)