small gleam coding and persistent agent daemon with a detachable cli and a canvas ui forked from okami.mom/albedo
albedo robot-docs direnv.md
2.7 kB · 47 lines
Markdown

direnv #

a workspace's .envrc reaches the model's run() jobs the way direnv loads it into the person's shell, and direnv's own allow list decides: a file they allowed loads, a blocked or denied one does not. albedo keeps no state of its own about it.

in the kernel #

priv/python/albedo_direnv.py runs before each run() (run.py) and brings os.environ up to date with direnv export json, as direnv's shell hook does before each prompt. the kernel is the shell: its folder is os.getcwd(), so os.chdir into another project loads that one's .envrc, and leaving it unloads it. jobs inherit os.environ, so everything the kernel starts afterwards (pipes, files searches) sees the same environment. it runs wherever the kernel does, remote hosts included.

  • direnv is looked up once per kernel on its PATH; without it nothing runs.
  • with no .envrc in the folder or above it, and nothing this kernel loaded to take back, it costs a few stats. an environment the daemon inherited from the shell that started it is left alone.
  • an unchanged file answers nothing in a few milliseconds; an edited one (or a file it watches) reloads by itself. a first evaluation (use flake building a dev shell) may take up to 600 s and holds that run().
  • what direnv says (direnv: loading …, direnv: export +FOO ~PATH, direnv: unloading) prints into the cell, so the model sees why its environment changed. direnv's blocked message is reworded so it does not tell the model to run direnv allow: allowing is the person's call. If a previously loaded file changes and is blocked, the kernel unloads the old environment before starting the job without it; a blocked reload never keeps stale values from the old file. direnv watches file mtimes at one-second resolution, so an .envrc rewritten within the same second as its last load is not noticed until it changes again.
  • it never raises: a failure starts the job without the environment.

in the tui #

when a chat opens, and after /cd, the tui runs direnv status --json in the workspace (chat_direnv.go), only when the daemon and the workspace are both on this machine: direnv allow is the person's own act there. for a file direnv blocks (new, or changed since it was allowed) it shows a question above the composer. while the composer is empty the question owns the letter keys: y runs direnv allow <file> and the next run() loads it, n (or esc) puts it away until the chat opens again, as direnv repeats its blocked message at the next cd. every other letter is held back, so the first letter of a prompt typed without reading never answers it; a draft already in the composer keeps typing normally. denied files never ask.