lampson 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,117 +1,117 @@
1
- ---
2
- name: lampson
3
- description: How this harness works — tools, workspace mount, permissions, agents, sessions. Load when asked about Lampson itself or when a tool behaves unexpectedly.
4
- ---
5
-
6
- # Lampson (the harness you are running in)
7
-
8
- ## Where you are
9
- - Your tools only reach `workspace/` — the user's project, mounted as a junction/symlink by
10
- `lampson.ps1 <path>` / `lampson.sh <path>`. All paths you pass are relative to that root.
11
- - Anything outside (absolute paths, `..`, sibling dirs) fails with `Capability not granted`.
12
- That is by design (Synsema deny-by-default + `call_tool` least-privilege). Do not try to work
13
- around it; tell the user if you genuinely need something outside the workspace.
14
- - `bash` runs with cwd = workspace root and a hard timeout (default 120 s): when it expires the command AND
15
- its child processes are killed. Never run servers/watchers/REPLs in `bash`; use the `process` tool:
16
- `process(start, name="web", command="npm run dev")`, then `process(logs, name="web")`, and
17
- `process(stop, name="web")` when done. New log lines of every managed process are appended to each
18
- `bash` result automatically — you SEE the server's console (errors, requests, crashes) without asking.
19
- The user sees the same logs live in the web UI ("Procesos" panel). Its child process is NOT confined by Synsema; dangerous
20
- commands are screened by `permission.syn` (hardline = always denied; dangerous = needs the user's
21
- approval in `ask` mode, denied in `strict`, allowed in `yolo`).
22
-
23
- ## Tool behavior worth knowing
24
- - `edit` needs an exact, unique `old_string`; if it fails, `read` the region again and retry with
25
- more context. `replace_all=true` for intentional multi-replace.
26
- - `read` returns up to 2000 lines; use `offset`/`limit` for big files. Outputs > 30k chars are truncated.
27
- - `find` is a simple glob (`*.ts`, `test_*`, `*config*`), `grep` is regex by default (`regex=false` for literals).
28
- - `lsp(op=symbols, path)` is the cheapest way to understand a big file: every function/class/variable
29
- with its line range — then `read` only the range you need. `definition` / `references` / `hover` /
30
- `implementation` take 1-based `line` + `character` ON the identifier. If no server is configured for
31
- that extension, propose `lsp(op=add, server=<preset>)` (typescript, python, rust, go, css, html) — it
32
- always asks the user and needs nothing installed (`npx` fetches it); `op=list` shows what is configured.
33
- Do not install language servers with bash yourself.
34
- - Calling the same tool with identical args 3 times in a row is blocked (doom loop) — change approach.
35
- - After 8 tool errors in a turn the harness asks you to stop and report.
36
- - `delegate(tasks=[{agent, brief, context}…])` runs sub-agents (`explore` / `plan` / `review` /
37
- `worker`) IN PARALLEL, each with a fresh context and a restricted toolset, and returns their reports
38
- consolidated. `background=true` returns ids at once and each report arrives later as a message in
39
- your context (do not poll); `action=list|steer|stop|result` manages them. Give a self-contained
40
- brief — a child does not see this conversation, cannot ask the user and cannot delegate. Reports are
41
- self-reports: verify before claiming success. Live logs: `.lampson/agents/<id>.log`.
42
-
43
- ## When you need the USER to run something
44
- The user can run any command themselves from the chat by prefixing it with `!` — e.g. `!npm run dev`,
45
- `!cat .env`, `!git push`. Their command runs without the permission policy and its output is added to
46
- your context automatically. In the terminal REPL, `!` runs inside a real pseudo-terminal: prompts
47
- (`y/N`, passwords, `npm init`) work — the user answers them inline. In the web UI there is also a
48
- **full interactive terminal** (header button `>_ terminal`, a shell with cwd = workspace, opens in the
49
- center pane like a file). Offer these when: a command needs an interactive terminal (logins, TUIs,
50
- watching a dev server), when something is denied for you (secrets, dangerous commands), or when they
51
- should verify a result with their own eyes. Say exactly what to type, e.g. "run `!npm run dev` (or open
52
- the terminal button and run `npm run dev`), then tell me the URL".
53
-
54
- ## Provider, model, keys, images
55
- - Provider/model/API keys live in `lampson/.lampson/config.json` (local); the user changes them with
56
- `/setup`, `/provider`, or the `provider · model` pill in the web header. Never ask the user to paste a
57
- key into the chat; point them there. Keys are sealed secrets: you cannot read or print them.
58
- - The user can paste images in the web UI, or in the terminal with `/paste` (clipboard) and
59
- `/image <path>`. If they say "look at this screenshot" in the terminal, tell them to copy it and type
60
- `/paste`, then send their message. If your model has no vision, the image arrives as a text
61
- note `[Image N WxHpx attached, but this model does not accept image input …]` — say so and suggest
62
- a vision model instead of guessing what the image shows.
63
- - Sessions can be deleted (`/delete <id>`, or ✕ in the web sidebar).
64
-
65
- ## Network exposure
66
- - The web server listens on all interfaces but every `/api/*` route (and the terminal socket) only
67
- accepts loopback clients; others get 401 unless they present `LAMPSON_WEB_TOKEN`. If the user asks
68
- to use Lampson from another machine, point them to that token — never suggest removing the check.
69
-
70
- ## Agents / modes
71
- - `build` (default): all tools. `plan`: read-only, produce a numbered plan. `review`: read + run
72
- tests, never edit. `explore`: read-only search. The user switches with `/agent <name>`.
73
-
74
- ## Project memory (persistent notes)
75
- - `memory(write, name, content)` saves a Markdown note about THIS project in Lampson's `memory/<project>/`
76
- folder (outside the repo). The system prompt lists your notes; `memory(read, name)` loads one.
77
- - Save what you would otherwise rediscover: how to run/test, env quirks, decisions, where things live,
78
- root causes of bugs. Update notes instead of contradicting them. The user can read and edit them.
79
-
80
- ## Sessions and memory
81
- - The whole history — including every tool result, error and DENIED message — is persisted in
82
- `.lampson/sessions/<id>.json` and resent to you each turn, so you know exactly what happened.
83
- - When the context grows past the compaction threshold, older turns are replaced by a summary that
84
- keeps goals, files touched, current state, pending work and errors seen.
85
-
86
- ## Updating Lampson
87
- - Lampson is installed as a git clone (`~/lampson`), so updating is a fast-forward pull. Both UIs
88
- check `origin/main` on start and show a notice when a newer version exists (terminal banner, web
89
- header button). The user updates with **`lampson --update`** in any terminal, `/update` in the REPL,
90
- or the header button in the web UI — then restarts Lampson. Re-running the installer does the same.
91
- - If asked "how do I update Lampson?", answer exactly that. If the pull fails because of local edits
92
- in the lampson folder, suggest `git -C ~/lampson stash` first. (When the distribution changes —
93
- desktop app, single binary — this section is the place that gets rewritten.)
94
-
95
- ## Extending the harness (if the user asks you to)
96
- - One tool = one file `lib/tools/<name>.syn` exporting `task tool(...)` (its `require` lines at the
97
- top of the body) and `let SPEC` (JSON Schema). Register it in `lib/tools.syn` (`use`, `registry()`,
98
- `CATALOG`) and in the profiles of `lib/agents.syn`. `synsema check chat.syn` validates everything.
99
- - Providers are raw HTTP (`lib/provider.syn`): OpenAI wire (`/chat/completions`) and Anthropic wire
100
- (`/messages`). Config via `.env` (`LAMPSON_PROVIDER`, `LAMPSON_API_KEY`, `LAMPSON_MODEL`, `LAMPSON_BASE_URL`).
101
- - Skills: `skills/<name>/SKILL.md` (harness), `workspace/skills/<name>/SKILL.md` (project),
102
- `workspace/.lampson/skills/<name>/SKILL.md` (local), plus external ones installed with `npx skills add`
103
- (`workspace/.agents/skills`, `workspace/.claude/skills`, and the global `~/.agents/skills` / `~/.claude/skills`
104
- mounted as `.lampson/skills-global` / `.lampson/skills-claude`). Frontmatter `name:` + `description:`.
105
- `skill(action=install, source=owner/repo, name=x, scope=global|project)` installs one (always asks the user).
106
- - **Lamps** = tool plugins you can create for this project without touching the harness:
107
- `lamp(action=create, name, manifest, files={"lamp.syn": "…"})` writes `workspace/.lampson/lamps/<name>/`,
108
- validates the manifest and runs `synsema check` on a syn entry (it does not run or enable anything —
109
- like dsh's cordis_define). Then `lamp(action=enable, name)` — the user must approve (a lamp is off until
110
- a human turns it on). Build one when a task needs a reusable project-specific tool. Manifest:
111
- `{"name", "description", "kind": "syn"|"exec", "entry": "lamp.syn" (syn) | "command": "python lamp.py" (exec),
112
- "caps": "file.read=workspace/*" (syn, optional extra ceiling over stdout,time,env=LAMP_*), "timeout": 60,
113
- "tools": [{"name", "description", "parameters": {JSON Schema}, "readonly": bool}]}`. Lamp names: letters,
114
- digits, `-` (no `_`). Each call runs the lamp as ONE child process: `synsema run --cap-set <ceiling> entry`
115
- for `syn`, the command for `exec`. Inside, read `LAMP_TOOL` and `LAMP_ARGS` (JSON) from env
116
- (`require env("LAMP_*")` in a .syn) and print the result to stdout. A syn lamp cannot use more than its
117
- manifest's `caps`; ask only for what the tool needs. Once on, its tools are `lamp_<name>_<tool>`.
1
+ ---
2
+ name: lampson
3
+ description: How this harness works — tools, workspace mount, permissions, agents, sessions. Load when asked about Lampson itself or when a tool behaves unexpectedly.
4
+ ---
5
+
6
+ # Lampson (the harness you are running in)
7
+
8
+ ## Where you are
9
+ - Your tools only reach `workspace/` — the user's project, mounted as a junction/symlink by
10
+ `lampson.ps1 <path>` / `lampson.sh <path>`. All paths you pass are relative to that root.
11
+ - Anything outside (absolute paths, `..`, sibling dirs) fails with `Capability not granted`.
12
+ That is by design (Synsema deny-by-default + `call_tool` least-privilege). Do not try to work
13
+ around it; tell the user if you genuinely need something outside the workspace.
14
+ - `bash` runs with cwd = workspace root and a hard timeout (default 120 s): when it expires the command AND
15
+ its child processes are killed. Never run servers/watchers/REPLs in `bash`; use the `process` tool:
16
+ `process(start, name="web", command="npm run dev")`, then `process(logs, name="web")`, and
17
+ `process(stop, name="web")` when done. New log lines of every managed process are appended to each
18
+ `bash` result automatically — you SEE the server's console (errors, requests, crashes) without asking.
19
+ The user sees the same logs live in the web UI ("Procesos" panel). Its child process is NOT confined by Synsema; dangerous
20
+ commands are screened by `permission.syn` (hardline = always denied; dangerous = needs the user's
21
+ approval in `ask` mode, denied in `strict`, allowed in `yolo`).
22
+
23
+ ## Tool behavior worth knowing
24
+ - `edit` needs an exact, unique `old_string`; if it fails, `read` the region again and retry with
25
+ more context. `replace_all=true` for intentional multi-replace.
26
+ - `read` returns up to 2000 lines; use `offset`/`limit` for big files. Outputs > 30k chars are truncated.
27
+ - `find` is a simple glob (`*.ts`, `test_*`, `*config*`), `grep` is regex by default (`regex=false` for literals).
28
+ - `lsp(op=symbols, path)` is the cheapest way to understand a big file: every function/class/variable
29
+ with its line range — then `read` only the range you need. `definition` / `references` / `hover` /
30
+ `implementation` take 1-based `line` + `character` ON the identifier. If no server is configured for
31
+ that extension, propose `lsp(op=add, server=<preset>)` (typescript, python, rust, go, css, html) — it
32
+ always asks the user and needs nothing installed (`npx` fetches it); `op=list` shows what is configured.
33
+ Do not install language servers with bash yourself.
34
+ - Calling the same tool with identical args 3 times in a row is blocked (doom loop) — change approach.
35
+ - After 8 tool errors in a turn the harness asks you to stop and report.
36
+ - `delegate(tasks=[{agent, brief, context}…])` runs sub-agents (`explore` / `plan` / `review` /
37
+ `worker`) IN PARALLEL, each with a fresh context and a restricted toolset, and returns their reports
38
+ consolidated. `background=true` returns ids at once and each report arrives later as a message in
39
+ your context (do not poll); `action=list|steer|stop|result` manages them. Give a self-contained
40
+ brief — a child does not see this conversation, cannot ask the user and cannot delegate. Reports are
41
+ self-reports: verify before claiming success. Live logs: `.lampson/agents/<id>.log`.
42
+
43
+ ## When you need the USER to run something
44
+ The user can run any command themselves from the chat by prefixing it with `!` — e.g. `!npm run dev`,
45
+ `!cat .env`, `!git push`. Their command runs without the permission policy and its output is added to
46
+ your context automatically. In the terminal REPL, `!` runs inside a real pseudo-terminal: prompts
47
+ (`y/N`, passwords, `npm init`) work — the user answers them inline. In the web UI there is also a
48
+ **full interactive terminal** (header button `>_ terminal`, a shell with cwd = workspace, opens in the
49
+ center pane like a file). Offer these when: a command needs an interactive terminal (logins, TUIs,
50
+ watching a dev server), when something is denied for you (secrets, dangerous commands), or when they
51
+ should verify a result with their own eyes. Say exactly what to type, e.g. "run `!npm run dev` (or open
52
+ the terminal button and run `npm run dev`), then tell me the URL".
53
+
54
+ ## Provider, model, keys, images
55
+ - Provider/model/API keys live in `lampson/.lampson/config.json` (local); the user changes them with
56
+ `/setup`, `/provider`, or the `provider · model` pill in the web header. Never ask the user to paste a
57
+ key into the chat; point them there. Keys are sealed secrets: you cannot read or print them.
58
+ - The user can paste images in the web UI, or in the terminal with `/paste` (clipboard) and
59
+ `/image <path>`. If they say "look at this screenshot" in the terminal, tell them to copy it and type
60
+ `/paste`, then send their message. If your model has no vision, the image arrives as a text
61
+ note `[Image N WxHpx attached, but this model does not accept image input …]` — say so and suggest
62
+ a vision model instead of guessing what the image shows.
63
+ - Sessions can be deleted (`/delete <id>`, or ✕ in the web sidebar).
64
+
65
+ ## Network exposure
66
+ - The web server listens on all interfaces but every `/api/*` route (and the terminal socket) only
67
+ accepts loopback clients; others get 401 unless they present `LAMPSON_WEB_TOKEN`. If the user asks
68
+ to use Lampson from another machine, point them to that token — never suggest removing the check.
69
+
70
+ ## Agents / modes
71
+ - `build` (default): all tools. `plan`: read-only, produce a numbered plan. `review`: read + run
72
+ tests, never edit. `explore`: read-only search. The user switches with `/agent <name>`.
73
+
74
+ ## Project memory (persistent notes)
75
+ - `memory(write, name, content)` saves a Markdown note about THIS project in Lampson's `memory/<project>/`
76
+ folder (outside the repo). The system prompt lists your notes; `memory(read, name)` loads one.
77
+ - Save what you would otherwise rediscover: how to run/test, env quirks, decisions, where things live,
78
+ root causes of bugs. Update notes instead of contradicting them. The user can read and edit them.
79
+
80
+ ## Sessions and memory
81
+ - The whole history — including every tool result, error and DENIED message — is persisted in
82
+ `.lampson/sessions/<id>.json` and resent to you each turn, so you know exactly what happened.
83
+ - When the context grows past the compaction threshold, older turns are replaced by a summary that
84
+ keeps goals, files touched, current state, pending work and errors seen.
85
+
86
+ ## Updating Lampson
87
+ - Lampson is installed as a git clone (`~/lampson`), so updating is a fast-forward pull. Both UIs
88
+ check `origin/main` on start and show a notice when a newer version exists (terminal banner, web
89
+ header button). The user updates with **`lampson --update`** in any terminal, `/update` in the REPL,
90
+ or the header button in the web UI — then restarts Lampson. Re-running the installer does the same.
91
+ - If asked "how do I update Lampson?", answer exactly that. If the pull fails because of local edits
92
+ in the lampson folder, suggest `git -C ~/lampson stash` first. (When the distribution changes —
93
+ desktop app, single binary — this section is the place that gets rewritten.)
94
+
95
+ ## Extending the harness (if the user asks you to)
96
+ - One tool = one file `lib/tools/<name>.syn` exporting `task tool(...)` (its `require` lines at the
97
+ top of the body) and `let SPEC` (JSON Schema). Register it in `lib/tools.syn` (`use`, `registry()`,
98
+ `CATALOG`) and in the profiles of `lib/agents.syn`. `synsema check chat.syn` validates everything.
99
+ - Providers are raw HTTP (`lib/provider.syn`): OpenAI wire (`/chat/completions`) and Anthropic wire
100
+ (`/messages`). Config via `.env` (`LAMPSON_PROVIDER`, `LAMPSON_API_KEY`, `LAMPSON_MODEL`, `LAMPSON_BASE_URL`).
101
+ - Skills: `skills/<name>/SKILL.md` (harness), `workspace/skills/<name>/SKILL.md` (project),
102
+ `workspace/.lampson/skills/<name>/SKILL.md` (local), plus external ones installed with `npx skills add`
103
+ (`workspace/.agents/skills`, `workspace/.claude/skills`, and the global `~/.agents/skills` / `~/.claude/skills`
104
+ mounted as `.lampson/skills-global` / `.lampson/skills-claude`). Frontmatter `name:` + `description:`.
105
+ `skill(action=install, source=owner/repo, name=x, scope=global|project)` installs one (always asks the user).
106
+ - **Lamps** = tool plugins you can create for this project without touching the harness:
107
+ `lamp(action=create, name, manifest, files={"lamp.syn": "…"})` writes `workspace/.lampson/lamps/<name>/`,
108
+ validates the manifest and runs `synsema check` on a syn entry (it does not run or enable anything —
109
+ like dsh's cordis_define). Then `lamp(action=enable, name)` — the user must approve (a lamp is off until
110
+ a human turns it on). Build one when a task needs a reusable project-specific tool. Manifest:
111
+ `{"name", "description", "kind": "syn"|"exec", "entry": "lamp.syn" (syn) | "command": "python lamp.py" (exec),
112
+ "caps": "file.read=workspace/*" (syn, optional extra ceiling over stdout,time,env=LAMP_*), "timeout": 60,
113
+ "tools": [{"name", "description", "parameters": {JSON Schema}, "readonly": bool}]}`. Lamp names: letters,
114
+ digits, `-` (no `_`). Each call runs the lamp as ONE child process: `synsema run --cap-set <ceiling> entry`
115
+ for `syn`, the command for `exec`. Inside, read `LAMP_TOOL` and `LAMP_ARGS` (JSON) from env
116
+ (`require env("LAMP_*")` in a .syn) and print the result to stdout. A syn lamp cannot use more than its
117
+ manifest's `caps`; ask only for what the tool needs. Once on, its tools are `lamp_<name>_<tool>`.
@@ -3,7 +3,7 @@ name: synsema
3
3
  description: Writing, checking, running and testing Synsema (.syn) code — syntax reflexes, capabilities, live processes / pseudo-terminals, and the runtime traps that cost hours. Load before touching any .syn file.
4
4
  ---
5
5
 
6
- # Synsema quick reference (v0.6.10)
6
+ # Synsema quick reference (v0.6.11)
7
7
 
8
8
  > Curated 10 KB summary for the agent (the full reference is ~450 KB and lives in the user's editor
9
9
  > skill). Kept in sync by hand with each `synsema update`; if `synsema --version` is newer than the
@@ -22,6 +22,11 @@ description: Writing, checking, running and testing Synsema (.syn) code — synt
22
22
  - `try` … `recover err` (recover SWALLOWS; `raise(err)` to re-throw) · `raise("msg")`
23
23
  - `contains(xs, x)` (on maps checks KEYS) · `append(xs, x)` returns a NEW list → `set xs to append(xs, x)`
24
24
  - `apply(f, xs)`, `where(xs, p)`, `sort_by(xs, f)`, `slice(xs, a, b)`, `split/join/trim/lower/upper`
25
+ - Text/regex: `replace_text(t, old, new)` (literal), `replace_re(t, re, rep)` (`\1` backrefs), `capture(t, re)`
26
+ (first match; with groups → list), `find_all`, `matches` (**full match** only — not a search).
27
+ There is NO `replace`, `regex_replace`, `chars`, `repeat`. Rust regex: no lookahead/lookbehind.
28
+ In `"..."` strings backslashes are literal: write `"\d"`, `"\*"` (`"\\d"` is a literal `\d`).
29
+ Anonymous functions: `(x) => expr` only (no inline `task(x)`).
25
30
  - `json_encode / json_decode` · `length` · `text(x)` · `number(s)` (ALWAYS float → `floor()` for ints)
26
31
  - Modules: `use "./m.syn" as m` (local only, never `../`), `export task/let`. A module cannot have
27
32
  top-level `require` or `serve` — the ENTRY file grants capabilities.
@@ -50,6 +55,12 @@ description: Writing, checking, running and testing Synsema (.syn) code — synt
50
55
  `proc_spawn` in a handler is gone when the handler returns. A process that must outlive requests lives
51
56
  inside an `agent` spawned from the handler (own lifecycle; blackboard `share/observe` + `bus_*` are shared
52
57
  with handlers). That is how lampson's `process` tool works (`lib/tools/proc.syn`).
58
+ - **Own terminal / raw keys (v0.6.11+)**: `let h be term_open({"ctrl_c": "exit"})` → `nothing` without a
59
+ TTY / under `test`/`serve` (fall back to `read_line`); `term_recv(h, secs)` → `{type: "key", key, text,
60
+ ctrl, alt, shift}` (`key` = `"char"|"enter"|"tab"|"backspace"|"up"|…`), `paste`, `resize`, `eof`;
61
+ draw with `term_write(h, ansi)` (**`print` stays buffered**); `term_size(h)`; `term_close(h)`. Alt+Enter
62
+ always arrives (Shift+Enter needs kitty protocol). `ask/approve` still work while open. Lampson's line
63
+ editor is `lib/line.syn`; drive a terminal UI in tests via `proc_spawn(…, {pty: true})` (`tty_test.syn`).
53
64
  - **File watch (v0.6.9+)**: `let w be watch("src", {"interval": 0.2, "ignore": ["*.tmp"]})` → events
54
65
  `{type: "create"|"modify"|"delete", path, is_dir}` via `watch_recv(w, secs)` or `select`; polling with a
55
66
  snapshot (latency = interval), `watch_close(w)`. Gate: `file("src")` + `file("src/*")`.
@@ -67,7 +78,9 @@ description: Writing, checking, running and testing Synsema (.syn) code — synt
67
78
  - `localhost` resolves to IPv6; use `127.0.0.1`.
68
79
  - `run("bash", ...)` hangs on Windows (WSL bash) → `C:\Program Files\Git\bin\bash.exe` or `cmd /c`.
69
80
  - A task named `run` shadows the builtin `run` (infinite recursion).
70
- - Reserved words that break variable/param names: `reason task ask stop decide analyze generate show approve confirm`.
81
+ - Reserved words that break variable/param names: `reason task ask stop type run decide analyze generate show approve confirm`.
82
+ - Reading a MISSING map key is a runtime error (`Map has no key 'x'`), not `nothing` → `contains(m, "x")` first.
83
+ - Maps are passed by reference: `set m["k"] to v` inside a task IS visible to the caller (lists via `append` are not — it returns a new list).
71
84
  - `and`/`or` do NOT short-circuit → nest `when` before indexing.
72
85
  - No `merge`: add a key with `set m["k"] to v`. No `append_file`: read + write (atomic; parents created).
73
86
  - Runtime error messages are Capitalized (`Not a directory: …`) and `contains` is case-sensitive → compare `lower(text(err))`.