diff --git a/.gitleaks.toml b/.gitleaks.toml index 8037adab4b..d4c134bbb2 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -80,6 +80,10 @@ commits = [ # every open reference, which is a real price for no gain in secrecy. The # per-push gate never reaches these commits; this entry is only so the # whole-history fallback scan stays honest instead of red-by-default. + # The hero scene once contained a fixed throwaway signing digest so the + # recording could display a realistic signing command. It authorized no + # service and was replaced by a value generated for each recording. + "6bbf5c92bc52224b8ca4e67129c0541299b53983", "6dbf3350c2e8a6e4cfe3aa13c311e6d09a30f33a", "0ed9680e8d9fea6ee2ace4938bbf9edfa391b2b0", ] diff --git a/docs/handbook/book/404.html b/docs/handbook/book/404.html index 8d4b1d650e..0ec5310884 100644 --- a/docs/handbook/book/404.html +++ b/docs/handbook/book/404.html @@ -51,7 +51,7 @@ const path_to_root = ""; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "searchindex-74060183.js"; + window.path_to_searchindex_js = "searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/acknowledgements.html b/docs/handbook/book/acknowledgements.html index f289fb2279..a945c1055d 100644 --- a/docs/handbook/book/acknowledgements.html +++ b/docs/handbook/book/acknowledgements.html @@ -50,7 +50,7 @@ const path_to_root = ""; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "searchindex-74060183.js"; + window.path_to_searchindex_js = "searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/appendix/glossary.html b/docs/handbook/book/appendix/glossary.html index 690d9c13fc..c053286342 100644 --- a/docs/handbook/book/appendix/glossary.html +++ b/docs/handbook/book/appendix/glossary.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/compaction.html b/docs/handbook/book/architecture/compaction.html index d343b6ed4e..17864a04b2 100644 --- a/docs/handbook/book/architecture/compaction.html +++ b/docs/handbook/book/architecture/compaction.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/config.html b/docs/handbook/book/architecture/config.html index 3bffac0e03..4d40e49ecc 100644 --- a/docs/handbook/book/architecture/config.html +++ b/docs/handbook/book/architecture/config.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/mcp.html b/docs/handbook/book/architecture/mcp.html index 85ad85b92b..a6011c454c 100644 --- a/docs/handbook/book/architecture/mcp.html +++ b/docs/handbook/book/architecture/mcp.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/memory.html b/docs/handbook/book/architecture/memory.html index 84df7560c2..a4f517c54a 100644 --- a/docs/handbook/book/architecture/memory.html +++ b/docs/handbook/book/architecture/memory.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/overview.html b/docs/handbook/book/architecture/overview.html index 85c6c08c04..b3b5ad2fce 100644 --- a/docs/handbook/book/architecture/overview.html +++ b/docs/handbook/book/architecture/overview.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/providers.html b/docs/handbook/book/architecture/providers.html index 03abdfd620..b20a02ad84 100644 --- a/docs/handbook/book/architecture/providers.html +++ b/docs/handbook/book/architecture/providers.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/sandbox.html b/docs/handbook/book/architecture/sandbox.html index 1ad78d022a..1cc155ce7b 100644 --- a/docs/handbook/book/architecture/sandbox.html +++ b/docs/handbook/book/architecture/sandbox.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/secrets.html b/docs/handbook/book/architecture/secrets.html index d880c076fc..a72f8128df 100644 --- a/docs/handbook/book/architecture/secrets.html +++ b/docs/handbook/book/architecture/secrets.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/session-turn.html b/docs/handbook/book/architecture/session-turn.html index 8b0825e507..bcaf874098 100644 --- a/docs/handbook/book/architecture/session-turn.html +++ b/docs/handbook/book/architecture/session-turn.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/architecture/tui.html b/docs/handbook/book/architecture/tui.html index 53d3ead1b8..57a1e0933f 100644 --- a/docs/handbook/book/architecture/tui.html +++ b/docs/handbook/book/architecture/tui.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/concepts/index.html b/docs/handbook/book/concepts/index.html index 3acde5ad5a..7fb639c574 100644 --- a/docs/handbook/book/concepts/index.html +++ b/docs/handbook/book/concepts/index.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/concepts/model-contract.html b/docs/handbook/book/concepts/model-contract.html index 90b2c87c87..c8034db0ae 100644 --- a/docs/handbook/book/concepts/model-contract.html +++ b/docs/handbook/book/concepts/model-contract.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/concepts/permission-model.html b/docs/handbook/book/concepts/permission-model.html index 3ac0009234..58725ddde7 100644 --- a/docs/handbook/book/concepts/permission-model.html +++ b/docs/handbook/book/concepts/permission-model.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/concepts/sessions-turns-threads.html b/docs/handbook/book/concepts/sessions-turns-threads.html index dbde437cfe..1995136869 100644 --- a/docs/handbook/book/concepts/sessions-turns-threads.html +++ b/docs/handbook/book/concepts/sessions-turns-threads.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/context/compaction-memory.html b/docs/handbook/book/context/compaction-memory.html index b5f295d83b..4c7e942642 100644 --- a/docs/handbook/book/context/compaction-memory.html +++ b/docs/handbook/book/context/compaction-memory.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/context/context-files.html b/docs/handbook/book/context/context-files.html index 92a47ee914..138aca609e 100644 --- a/docs/handbook/book/context/context-files.html +++ b/docs/handbook/book/context/context-files.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/context/goal-state.html b/docs/handbook/book/context/goal-state.html index 394319e2e4..3fb55848eb 100644 --- a/docs/handbook/book/context/goal-state.html +++ b/docs/handbook/book/context/goal-state.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/context/reads-search.html b/docs/handbook/book/context/reads-search.html index b0cb90a803..71cb7a0a58 100644 --- a/docs/handbook/book/context/reads-search.html +++ b/docs/handbook/book/context/reads-search.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/edit/edit-repair.html b/docs/handbook/book/edit/edit-repair.html index fd2cb7f340..59686fc814 100644 --- a/docs/handbook/book/edit/edit-repair.html +++ b/docs/handbook/book/edit/edit-repair.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/edit/engine.html b/docs/handbook/book/edit/engine.html index 819082ca2d..fae021b95c 100644 --- a/docs/handbook/book/edit/engine.html +++ b/docs/handbook/book/edit/engine.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/edit/roadmap.html b/docs/handbook/book/edit/roadmap.html index d4db7570d8..4b3329703c 100644 --- a/docs/handbook/book/edit/roadmap.html +++ b/docs/handbook/book/edit/roadmap.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/advisor.html b/docs/handbook/book/features/advisor.html index c256c4ebe6..d95e5bc5ce 100644 --- a/docs/handbook/book/features/advisor.html +++ b/docs/handbook/book/features/advisor.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/branching.html b/docs/handbook/book/features/branching.html index f7a77973a8..272f1cf052 100644 --- a/docs/handbook/book/features/branching.html +++ b/docs/handbook/book/features/branching.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/cockpit.html b/docs/handbook/book/features/cockpit.html index 08d924540a..d82af74a42 100644 --- a/docs/handbook/book/features/cockpit.html +++ b/docs/handbook/book/features/cockpit.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/collab.html b/docs/handbook/book/features/collab.html index 26a5a07d6a..717d83813c 100644 --- a/docs/handbook/book/features/collab.html +++ b/docs/handbook/book/features/collab.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/connectors.html b/docs/handbook/book/features/connectors.html index c356345bd4..5fe3d88281 100644 --- a/docs/handbook/book/features/connectors.html +++ b/docs/handbook/book/features/connectors.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/cpu-limit.html b/docs/handbook/book/features/cpu-limit.html index 0f746cb004..d17231179e 100644 --- a/docs/handbook/book/features/cpu-limit.html +++ b/docs/handbook/book/features/cpu-limit.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/doctor.html b/docs/handbook/book/features/doctor.html index fd68df6a89..705dab2d09 100644 --- a/docs/handbook/book/features/doctor.html +++ b/docs/handbook/book/features/doctor.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/exec.html b/docs/handbook/book/features/exec.html index cff88b8dfe..70a104c872 100644 --- a/docs/handbook/book/features/exec.html +++ b/docs/handbook/book/features/exec.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/export-import.html b/docs/handbook/book/features/export-import.html index 8eae6f64f7..d788957afa 100644 --- a/docs/handbook/book/features/export-import.html +++ b/docs/handbook/book/features/export-import.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/extensions-authoring.html b/docs/handbook/book/features/extensions-authoring.html index 5625b0abfd..b1bc27d95d 100644 --- a/docs/handbook/book/features/extensions-authoring.html +++ b/docs/handbook/book/features/extensions-authoring.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/extensions.html b/docs/handbook/book/features/extensions.html index 8493c3ec41..f59419d404 100644 --- a/docs/handbook/book/features/extensions.html +++ b/docs/handbook/book/features/extensions.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/feature-flags.html b/docs/handbook/book/features/feature-flags.html index 31d7004d06..b2c3fb4873 100644 --- a/docs/handbook/book/features/feature-flags.html +++ b/docs/handbook/book/features/feature-flags.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/hooks-authoring.html b/docs/handbook/book/features/hooks-authoring.html index 837ed6eecc..44b15330e6 100644 --- a/docs/handbook/book/features/hooks-authoring.html +++ b/docs/handbook/book/features/hooks-authoring.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/hooks.html b/docs/handbook/book/features/hooks.html index 99492fc276..8d56a006ab 100644 --- a/docs/handbook/book/features/hooks.html +++ b/docs/handbook/book/features/hooks.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/index.html b/docs/handbook/book/features/index.html index 30d01c80f6..6a5356fdb5 100644 --- a/docs/handbook/book/features/index.html +++ b/docs/handbook/book/features/index.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/keybindings.html b/docs/handbook/book/features/keybindings.html index 1c8c9b95a9..f412109cfa 100644 --- a/docs/handbook/book/features/keybindings.html +++ b/docs/handbook/book/features/keybindings.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/lsp.html b/docs/handbook/book/features/lsp.html index 22037b14c8..a60abd6a9c 100644 --- a/docs/handbook/book/features/lsp.html +++ b/docs/handbook/book/features/lsp.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/marketplace-authoring.html b/docs/handbook/book/features/marketplace-authoring.html index 88e5b921bd..034763cb16 100644 --- a/docs/handbook/book/features/marketplace-authoring.html +++ b/docs/handbook/book/features/marketplace-authoring.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/marketplace.html b/docs/handbook/book/features/marketplace.html index b88f554f9d..1f887f9bc9 100644 --- a/docs/handbook/book/features/marketplace.html +++ b/docs/handbook/book/features/marketplace.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/mcp.html b/docs/handbook/book/features/mcp.html index dba2cd8e98..db2ede0da2 100644 --- a/docs/handbook/book/features/mcp.html +++ b/docs/handbook/book/features/mcp.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/memory.html b/docs/handbook/book/features/memory.html index 84f36efd9a..7547f064eb 100644 --- a/docs/handbook/book/features/memory.html +++ b/docs/handbook/book/features/memory.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/personalities.html b/docs/handbook/book/features/personalities.html index b0227b3536..5f31241ca6 100644 --- a/docs/handbook/book/features/personalities.html +++ b/docs/handbook/book/features/personalities.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/plan-mode.html b/docs/handbook/book/features/plan-mode.html index b405e7d498..4e89ddc9a5 100644 --- a/docs/handbook/book/features/plan-mode.html +++ b/docs/handbook/book/features/plan-mode.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/plugins.html b/docs/handbook/book/features/plugins.html index 0b6926cef3..d28f9b4145 100644 --- a/docs/handbook/book/features/plugins.html +++ b/docs/handbook/book/features/plugins.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/profiles.html b/docs/handbook/book/features/profiles.html index bdd8eefdfd..8b31054a33 100644 --- a/docs/handbook/book/features/profiles.html +++ b/docs/handbook/book/features/profiles.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/python-repl.html b/docs/handbook/book/features/python-repl.html index d61daee90c..a19b01638e 100644 --- a/docs/handbook/book/features/python-repl.html +++ b/docs/handbook/book/features/python-repl.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/review.html b/docs/handbook/book/features/review.html index 008d7624d4..fff31c90fa 100644 --- a/docs/handbook/book/features/review.html +++ b/docs/handbook/book/features/review.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/sandbox.html b/docs/handbook/book/features/sandbox.html index 3c0fe48606..caf531a765 100644 --- a/docs/handbook/book/features/sandbox.html +++ b/docs/handbook/book/features/sandbox.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/secrets.html b/docs/handbook/book/features/secrets.html index 89423b01a2..04a7f1d8c4 100644 --- a/docs/handbook/book/features/secrets.html +++ b/docs/handbook/book/features/secrets.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/skills-authoring.html b/docs/handbook/book/features/skills-authoring.html index 0e24a3c9db..253b822aa0 100644 --- a/docs/handbook/book/features/skills-authoring.html +++ b/docs/handbook/book/features/skills-authoring.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/skills.html b/docs/handbook/book/features/skills.html index a9185a3d47..415157b250 100644 --- a/docs/handbook/book/features/skills.html +++ b/docs/handbook/book/features/skills.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/speech.html b/docs/handbook/book/features/speech.html index 198a06038b..67a645cc89 100644 --- a/docs/handbook/book/features/speech.html +++ b/docs/handbook/book/features/speech.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/subagents.html b/docs/handbook/book/features/subagents.html index b93a1de1f8..45691f5a22 100644 --- a/docs/handbook/book/features/subagents.html +++ b/docs/handbook/book/features/subagents.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/features/web-search.html b/docs/handbook/book/features/web-search.html index 8712af3090..459d3cf04d 100644 --- a/docs/handbook/book/features/web-search.html +++ b/docs/handbook/book/features/web-search.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/foundations/architecture.html b/docs/handbook/book/foundations/architecture.html index 0f903b6aa4..ae5e85089f 100644 --- a/docs/handbook/book/foundations/architecture.html +++ b/docs/handbook/book/foundations/architecture.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/foundations/thesis.html b/docs/handbook/book/foundations/thesis.html index 0830c127f4..29337b2d34 100644 --- a/docs/handbook/book/foundations/thesis.html +++ b/docs/handbook/book/foundations/thesis.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; diff --git a/docs/handbook/book/foundations/verification.html b/docs/handbook/book/foundations/verification.html index 0bb16bb0bf..ff985d5a19 100644 --- a/docs/handbook/book/foundations/verification.html +++ b/docs/handbook/book/foundations/verification.html @@ -50,7 +50,7 @@ const path_to_root = "../"; const default_light_theme = "navy"; const default_dark_theme = "navy"; - window.path_to_searchindex_js = "../searchindex-74060183.js"; + window.path_to_searchindex_js = "../searchindex-2c0008ea.js"; @@ -221,29 +221,39 @@
The recorder refuses to publish a clip whose cadence is not the one it captured. Three
+criteria come from --expect-ms:
typical frame capture interval +/-1 ms (33/34 ms at 30 fps)
+moving average at least 80% of the capture rate, held stills set aside
+cadence share at least 85% of moving frames at the capture interval
-The first catches a resample, where every frame was rewritten. The second catches a -clip whose most common frame is correct and whose wall clock is mostly slower than it — -frames held for two or three intervals in the middle of a scroll. A hold at or past ten -intervals is a still screen, is reported, and does not count against the average. +
The typical frame catches a resample, where every frame was rewritten. The moving +average catches a clip whose wall clock is mostly slower than its most common frame. +The cadence share catches frequent short holds that an acceptable average can hide. +Byte-identical frames from normal terminal input and model output are coalesced by the +WebP encoder, so the average allows 20% while the share limits how often that occurs. +A hold at or past ten intervals is a still screen, is reported, and does not count. Measure a published file with:
python3 proof/webp-cadence.py assets/demo-hd.webp --expect-ms 33
The HD recorder starts Xvfb, picom, and kitty inside the recorder container. It drives the shipped CLI with real keyboard and pointer events and records the private display at 30 frames per second.
+The HD recorder starts Xvfb and kitty inside the recorder container. It drives the shipped CLI with real keyboard and pointer events and records the private display at 30 frames per second.
+30 is the rate the pipeline delivers whole. Measured at 2560x1440 with the hero’s chrome and a payload repainting every cell as fast as the terminal accepts it, a 30 fps capture returns 240 unique frames of 240 grabbed. Capturing at 60 adds no motion the session had: it doubles the encoder’s cores and the file, and writes a 60 fps header over slower content, which is how a stuttering take once read as smooth to ffprobe.
A take is judged on whether the picture moved, never on the rate the container declares. proof/motion-gate.sh counts unique frames with mpdecimate and fails a take below SCENE_MOTION_FLOOR; both session scripts run it before the take is published.
The landing-page terminal uses:
terminal kitty
-font JetBrains Mono 21
+font JetBrains Mono 15
canvas 2560x1440 at 30 fps
window inset 128 px
background #171b22
foreground #d3dae6
publish Lanczos downsample to 1920x1080
+proof/docker/scene-config.sh is the single definition of every SCENE_* knob. The two session scripts and the two host recorders source it; none of them restates a default. Override a knob by exporting it, never by editing one of those four files, because a default written down twice is two defaults and the one a run gets depends on which file it entered through.
The chrome — rounded corners, the shadow, the translucent window over the backdrop — is drawn after the take by proof/compose-chrome.sh, not by a compositor during it. The backdrop does not move, so blending it under the window every frame recomputes one static picture thousands of times, and it cost the capture: with picom’s blur on, ffmpeg could grab only 69 of 360 frames, and opacity alone still cost a third. xwallpaper puts the backdrop in the capture for free as a root pixmap; the pass replaces the square-cornered inset with the same pixels rounded, blended and shadowed.
SCENE_CHROME=live runs a compositor during the capture instead, for comparison. It is not the default and a take recorded that way is slower.
The pass is cosmetic. It cannot recover a frame the capture never drew, so a take that stuttered while it was recorded still stutters after it, and the motion gate runs on the composited file that ships.
Preview a scene without replacing tracked proof assets:
PUBLISH=0 DEMO_SERVER=x11 \
PROOF_LLM_BASE_URL=http://<host>:11434/v1 \
@@ -328,10 +338,17 @@ Z
The region is measured, not typed in: the stage diffs the frames around the moment and
holds the bounding box of what changed there, padded and clamped inside the frame at
the source aspect ratio. A moment with nothing moving in it produces no file.
-The zoom ceiling defaults to the capture width over the published width, so a held
-frame is a crop rather than an upscale. The stage runs on the take, before the cut,
-and keeps every frame and the recorded rate, so the cadence gate still measures the
-capture’s own cadence. A scene asks for one by setting ZOOM_ARGS in
+
The hero take drives the same stage from a cue file the scene writes next to its
+marks (zoom-in FRAME [x,y,w,h], pan FRAME x,y,w,h, zoom-out FRAME). The hold between those cues
+is at least two seconds of real time, with no time compression on that span, so
+the stored secret is readable. Magnification is relative to the published wide
+shot and is at least 2x: proof/glyph-height.py measures capture_width / crop_width on the hold. A missing rect on zoom-in means “measure it”.
+The zoom ceiling still defaults to the capture width over the published width so a
+1.33x hold is a crop. The hero’s 2x secret hold is a tighter crop scaled back to
+1920x1080 — a camera move of the one capture path, not a second recorder. The
+stage runs on the take, before the cut, and keeps every frame and the recorded
+rate, so the cadence gate still measures the capture’s own cadence. A scene asks
+for one by writing the cue file, or by setting ZOOM_ARGS in
scripts/demos/record-hd-demo.sh.
--self-check records a synthetic clip whose moving region is known and asserts the
measured rect, the frame count, the rate and the held magnification. Run it on a
diff --git a/docs/handbook/book/index.html b/docs/handbook/book/index.html
index f25490fd73..345d519593 100644
--- a/docs/handbook/book/index.html
+++ b/docs/handbook/book/index.html
@@ -50,7 +50,7 @@
const path_to_root = "";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "searchindex-74060183.js";
+ window.path_to_searchindex_js = "searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/introduction.html b/docs/handbook/book/introduction.html
index f25490fd73..345d519593 100644
--- a/docs/handbook/book/introduction.html
+++ b/docs/handbook/book/introduction.html
@@ -50,7 +50,7 @@
const path_to_root = "";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "searchindex-74060183.js";
+ window.path_to_searchindex_js = "searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/models/prompts.html b/docs/handbook/book/models/prompts.html
index d3dfc2bbde..3e49c7d0ae 100644
--- a/docs/handbook/book/models/prompts.html
+++ b/docs/handbook/book/models/prompts.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/models/providers.html b/docs/handbook/book/models/providers.html
index 80ff6fc65f..9193c3109d 100644
--- a/docs/handbook/book/models/providers.html
+++ b/docs/handbook/book/models/providers.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/models/system-prompt.html b/docs/handbook/book/models/system-prompt.html
index 5b4a8bc6f8..d11818d3ff 100644
--- a/docs/handbook/book/models/system-prompt.html
+++ b/docs/handbook/book/models/system-prompt.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/observability/overview.html b/docs/handbook/book/observability/overview.html
index c67842882f..52884f9a03 100644
--- a/docs/handbook/book/observability/overview.html
+++ b/docs/handbook/book/observability/overview.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/print.html b/docs/handbook/book/print.html
index a253b448fa..6edc1369f4 100644
--- a/docs/handbook/book/print.html
+++ b/docs/handbook/book/print.html
@@ -51,7 +51,7 @@
const path_to_root = "";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "searchindex-74060183.js";
+ window.path_to_searchindex_js = "searchindex-2c0008ea.js";
@@ -17212,29 +17212,39 @@
typical frame 33 ms, +/-1 (34 ms alternates at 30 fps)
-moving average within 10% of 30 fps, held stills set aside
-
-The first catches a resample, where every frame was rewritten. The second catches a -clip whose most common frame is correct and whose wall clock is mostly slower than it — -frames held for two or three intervals in the middle of a scroll. A hold at or past ten -intervals is a still screen, is reported, and does not count against the average. +
The recorder refuses to publish a clip whose cadence is not the one it captured. Three
+criteria come from --expect-ms:
typical frame capture interval +/-1 ms (33/34 ms at 30 fps)
+moving average at least 80% of the capture rate, held stills set aside
+cadence share at least 85% of moving frames at the capture interval
+
+The typical frame catches a resample, where every frame was rewritten. The moving +average catches a clip whose wall clock is mostly slower than its most common frame. +The cadence share catches frequent short holds that an acceptable average can hide. +Byte-identical frames from normal terminal input and model output are coalesced by the +WebP encoder, so the average allows 20% while the share limits how often that occurs. +A hold at or past ten intervals is a still screen, is reported, and does not count. Measure a published file with:
python3 proof/webp-cadence.py assets/demo-hd.webp --expect-ms 33
The HD recorder starts Xvfb, picom, and kitty inside the recorder container. It drives the shipped CLI with real keyboard and pointer events and records the private display at 30 frames per second.
+The HD recorder starts Xvfb and kitty inside the recorder container. It drives the shipped CLI with real keyboard and pointer events and records the private display at 30 frames per second.
+30 is the rate the pipeline delivers whole. Measured at 2560x1440 with the hero’s chrome and a payload repainting every cell as fast as the terminal accepts it, a 30 fps capture returns 240 unique frames of 240 grabbed. Capturing at 60 adds no motion the session had: it doubles the encoder’s cores and the file, and writes a 60 fps header over slower content, which is how a stuttering take once read as smooth to ffprobe.
A take is judged on whether the picture moved, never on the rate the container declares. proof/motion-gate.sh counts unique frames with mpdecimate and fails a take below SCENE_MOTION_FLOOR; both session scripts run it before the take is published.
The landing-page terminal uses:
terminal kitty
-font JetBrains Mono 21
+font JetBrains Mono 15
canvas 2560x1440 at 30 fps
window inset 128 px
background #171b22
foreground #d3dae6
publish Lanczos downsample to 1920x1080
+proof/docker/scene-config.sh is the single definition of every SCENE_* knob. The two session scripts and the two host recorders source it; none of them restates a default. Override a knob by exporting it, never by editing one of those four files, because a default written down twice is two defaults and the one a run gets depends on which file it entered through.
The chrome — rounded corners, the shadow, the translucent window over the backdrop — is drawn after the take by proof/compose-chrome.sh, not by a compositor during it. The backdrop does not move, so blending it under the window every frame recomputes one static picture thousands of times, and it cost the capture: with picom’s blur on, ffmpeg could grab only 69 of 360 frames, and opacity alone still cost a third. xwallpaper puts the backdrop in the capture for free as a root pixmap; the pass replaces the square-cornered inset with the same pixels rounded, blended and shadowed.
SCENE_CHROME=live runs a compositor during the capture instead, for comparison. It is not the default and a take recorded that way is slower.
The pass is cosmetic. It cannot recover a frame the capture never drew, so a take that stuttered while it was recorded still stutters after it, and the motion gate runs on the composited file that ships.
Preview a scene without replacing tracked proof assets:
PUBLISH=0 DEMO_SERVER=x11 \
PROOF_LLM_BASE_URL=http://<host>:11434/v1 \
@@ -17319,10 +17329,17 @@ Z
The region is measured, not typed in: the stage diffs the frames around the moment and
holds the bounding box of what changed there, padded and clamped inside the frame at
the source aspect ratio. A moment with nothing moving in it produces no file.
-The zoom ceiling defaults to the capture width over the published width, so a held
-frame is a crop rather than an upscale. The stage runs on the take, before the cut,
-and keeps every frame and the recorded rate, so the cadence gate still measures the
-capture’s own cadence. A scene asks for one by setting ZOOM_ARGS in
+
The hero take drives the same stage from a cue file the scene writes next to its
+marks (zoom-in FRAME [x,y,w,h], pan FRAME x,y,w,h, zoom-out FRAME). The hold between those cues
+is at least two seconds of real time, with no time compression on that span, so
+the stored secret is readable. Magnification is relative to the published wide
+shot and is at least 2x: proof/glyph-height.py measures capture_width / crop_width on the hold. A missing rect on zoom-in means “measure it”.
+The zoom ceiling still defaults to the capture width over the published width so a
+1.33x hold is a crop. The hero’s 2x secret hold is a tighter crop scaled back to
+1920x1080 — a camera move of the one capture path, not a second recorder. The
+stage runs on the take, before the cut, and keeps every frame and the recorded
+rate, so the cadence gate still measures the capture’s own cadence. A scene asks
+for one by writing the cue file, or by setting ZOOM_ARGS in
scripts/demos/record-hd-demo.sh.
--self-check records a synthetic clip whose moving region is known and asserts the
measured rect, the frame count, the rate and the held magnification. Run it on a
diff --git a/docs/handbook/book/reference/approval-mode.html b/docs/handbook/book/reference/approval-mode.html
index abdc9e435e..2331e3ac2a 100644
--- a/docs/handbook/book/reference/approval-mode.html
+++ b/docs/handbook/book/reference/approval-mode.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/cli.html b/docs/handbook/book/reference/cli.html
index 03123d45cf..43a0b1eb94 100644
--- a/docs/handbook/book/reference/cli.html
+++ b/docs/handbook/book/reference/cli.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/environment-complete.html b/docs/handbook/book/reference/environment-complete.html
index ed71f37e33..f1cf17eca4 100644
--- a/docs/handbook/book/reference/environment-complete.html
+++ b/docs/handbook/book/reference/environment-complete.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/environment.html b/docs/handbook/book/reference/environment.html
index 30d7538035..e7f87b2e80 100644
--- a/docs/handbook/book/reference/environment.html
+++ b/docs/handbook/book/reference/environment.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/exit-codes.html b/docs/handbook/book/reference/exit-codes.html
index 40e47c5338..531dab915b 100644
--- a/docs/handbook/book/reference/exit-codes.html
+++ b/docs/handbook/book/reference/exit-codes.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/file-locations.html b/docs/handbook/book/reference/file-locations.html
index 233df41d20..4baa9d5a68 100644
--- a/docs/handbook/book/reference/file-locations.html
+++ b/docs/handbook/book/reference/file-locations.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/hooks.html b/docs/handbook/book/reference/hooks.html
index aab67aaa59..e8ad9a16e3 100644
--- a/docs/handbook/book/reference/hooks.html
+++ b/docs/handbook/book/reference/hooks.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/index.html b/docs/handbook/book/reference/index.html
index ed436915d2..986c69112c 100644
--- a/docs/handbook/book/reference/index.html
+++ b/docs/handbook/book/reference/index.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/keybindings-config.html b/docs/handbook/book/reference/keybindings-config.html
index 2b8b9f79ff..c3091dbfa2 100644
--- a/docs/handbook/book/reference/keybindings-config.html
+++ b/docs/handbook/book/reference/keybindings-config.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/keybindings-ref.html b/docs/handbook/book/reference/keybindings-ref.html
index aa6afd6f89..30e8cfd813 100644
--- a/docs/handbook/book/reference/keybindings-ref.html
+++ b/docs/handbook/book/reference/keybindings-ref.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/mcp-config.html b/docs/handbook/book/reference/mcp-config.html
index 1cefcde9b2..00c68a7a65 100644
--- a/docs/handbook/book/reference/mcp-config.html
+++ b/docs/handbook/book/reference/mcp-config.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/models-yml.html b/docs/handbook/book/reference/models-yml.html
index a8b57eb9a1..719b215992 100644
--- a/docs/handbook/book/reference/models-yml.html
+++ b/docs/handbook/book/reference/models-yml.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/project-trust.html b/docs/handbook/book/reference/project-trust.html
index c5b90c2b7d..e94463d810 100644
--- a/docs/handbook/book/reference/project-trust.html
+++ b/docs/handbook/book/reference/project-trust.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/providers.html b/docs/handbook/book/reference/providers.html
index 6a0f5f1dfc..74e37d1ea1 100644
--- a/docs/handbook/book/reference/providers.html
+++ b/docs/handbook/book/reference/providers.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/rpc.html b/docs/handbook/book/reference/rpc.html
index 05f43137de..43d82f26a6 100644
--- a/docs/handbook/book/reference/rpc.html
+++ b/docs/handbook/book/reference/rpc.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/sdk.html b/docs/handbook/book/reference/sdk.html
index 830755fdaa..cd48f1723d 100644
--- a/docs/handbook/book/reference/sdk.html
+++ b/docs/handbook/book/reference/sdk.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/settings-reference.html b/docs/handbook/book/reference/settings-reference.html
index 330ccf4263..107eafd0f6 100644
--- a/docs/handbook/book/reference/settings-reference.html
+++ b/docs/handbook/book/reference/settings-reference.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/settings.html b/docs/handbook/book/reference/settings.html
index 3f62278407..5b8d0b8495 100644
--- a/docs/handbook/book/reference/settings.html
+++ b/docs/handbook/book/reference/settings.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/skills.html b/docs/handbook/book/reference/skills.html
index d4e8992faf..b0ffc35394 100644
--- a/docs/handbook/book/reference/skills.html
+++ b/docs/handbook/book/reference/skills.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/slash-commands.html b/docs/handbook/book/reference/slash-commands.html
index c0e3860f09..8f7456da8c 100644
--- a/docs/handbook/book/reference/slash-commands.html
+++ b/docs/handbook/book/reference/slash-commands.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/theme.html b/docs/handbook/book/reference/theme.html
index f9c2fbcd89..f45ce0ebb5 100644
--- a/docs/handbook/book/reference/theme.html
+++ b/docs/handbook/book/reference/theme.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/tools.html b/docs/handbook/book/reference/tools.html
index 9191e78e4e..8faaf6be56 100644
--- a/docs/handbook/book/reference/tools.html
+++ b/docs/handbook/book/reference/tools.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/reference/tree-command.html b/docs/handbook/book/reference/tree-command.html
index acbaf87ca1..b0e0954f85 100644
--- a/docs/handbook/book/reference/tree-command.html
+++ b/docs/handbook/book/reference/tree-command.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/repair/cascade.html b/docs/handbook/book/repair/cascade.html
index 438a5baa3e..d1605858c7 100644
--- a/docs/handbook/book/repair/cascade.html
+++ b/docs/handbook/book/repair/cascade.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/repair/overview.html b/docs/handbook/book/repair/overview.html
index 578520afc3..05b57be712 100644
--- a/docs/handbook/book/repair/overview.html
+++ b/docs/handbook/book/repair/overview.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/repair/per-model.html b/docs/handbook/book/repair/per-model.html
index c23cb0cf16..9096e36dd6 100644
--- a/docs/handbook/book/repair/per-model.html
+++ b/docs/handbook/book/repair/per-model.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/repair/soundness.html b/docs/handbook/book/repair/soundness.html
index db32c9f6a5..7714175e78 100644
--- a/docs/handbook/book/repair/soundness.html
+++ b/docs/handbook/book/repair/soundness.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/router/role-routing.html b/docs/handbook/book/router/role-routing.html
index 207ffd26d6..1efb2a11ea 100644
--- a/docs/handbook/book/router/role-routing.html
+++ b/docs/handbook/book/router/role-routing.html
@@ -50,7 +50,7 @@
const path_to_root = "../";
const default_light_theme = "navy";
const default_dark_theme = "navy";
- window.path_to_searchindex_js = "../searchindex-74060183.js";
+ window.path_to_searchindex_js = "../searchindex-2c0008ea.js";
diff --git a/docs/handbook/book/searcher-c2a407aa.js b/docs/handbook/book/searcher-c2a407aa.js
index f090ea4bdb..ccd65c1592 100644
--- a/docs/handbook/book/searcher-c2a407aa.js
+++ b/docs/handbook/book/searcher-c2a407aa.js
@@ -437,7 +437,7 @@ window.search = window.search || {};
if (yes) {
loadSearchScript(
window.path_to_searchindex_js ||
- path_to_root + 'searchindex-74060183.js',
+ path_to_root + 'searchindex-2c0008ea.js',
'mdbook-search-index');
search_wrap.classList.remove('hidden');
searchicon.setAttribute('aria-expanded', 'true');
diff --git a/docs/handbook/book/searchindex-2c0008ea.js b/docs/handbook/book/searchindex-2c0008ea.js
new file mode 100644
index 0000000000..01239f24cc
--- /dev/null
+++ b/docs/handbook/book/searchindex-2c0008ea.js
@@ -0,0 +1 @@
+window.search = Object.assign(window.search, JSON.parse('{"doc_urls":["introduction.html#the-veyyon-handbook","introduction.html#install","introduction.html#where-things-are","introduction.html#implementation","why/index.html#design-and-mechanisms","why/value.html#overview","why/value.html#subsystem-summary","why/value.html#lineage","why/value.html#related","why/innovations.html#mechanisms","why/innovations.html#hashline-edits","why/innovations.html#tool-approval-tiers","why/innovations.html#model-slots-and-roles","why/innovations.html#provider-neutral-loop","why/innovations.html#engine-modes","why/innovations.html#profiles","why/innovations.html#related","why/argot.html#argot","why/argot.html#shorthand-format","why/argot.html#dictionary-structure","why/argot.html#codec-boundaries","why/argot.html#cache-storage","why/argot.html#subagent-boundary","why/argot.html#related","foundations/thesis.html#harness-design-goals","foundations/thesis.html#primary-mechanisms","foundations/thesis.html#design-consequences","foundations/thesis.html#related","foundations/architecture.html#architecture-at-a-glance","foundations/architecture.html#the-request-path","foundations/architecture.html#subsystems","foundations/architecture.html#design-rules","why/performance.html#performance","why/performance.html#retry-bounds","why/performance.html#edit-path","why/performance.html#edit-preview-while-arguments-stream","why/performance.html#session-persistence","why/performance.html#runtime-architecture","why/performance.html#related","using/install.html#install","using/install.html#install-on-linux-or-macos","using/install.html#install-on-windows","using/install.html#prebuilt-release-platforms","using/install.html#after-install","using/install.html#install-a-specific-release","using/install.html#linux-or-macos","using/install.html#windows","using/install.html#run-an-unreleased-ref-or-an-unsupported-platform","using/install.html#verify-the-install","using/install.html#when-the-staged-binary-would-not-run","using/install.html#relocate-the-config-directory","using/install.html#first-credentials","using/install.html#updating","using/install.html#going-back-to-an-older-version","using/install.html#tab-completion","using/install.html#uninstall","using/authentication.html#signing-in","using/authentication.html#sign-in-from-the-tui","using/authentication.html#using-several-accounts-for-one-provider","using/authentication.html#naming-an-account","using/authentication.html#which-account-am-i-using","using/authentication.html#when-a-login-is-signed-out-for-you","using/authentication.html#headless-and-remote-hosts","using/authentication.html#using-an-environment-variable-instead","using/authentication.html#how-keys-are-resolved","using/authentication.html#credentials-are-shared-across-profiles","using/authentication.html#provider-data-is-data-driven","using/quickstart.html#quickstart","using/quickstart.html#before-you-start","using/quickstart.html#check-the-environment","using/quickstart.html#start-your-first-session","using/quickstart.html#ask-for-a-small-edit","using/quickstart.html#composer-conveniences","using/quickstart.html#next-steps","using/getting-started.html#getting-started","using/getting-started.html#1-install","using/getting-started.html#2-first-launch","using/getting-started.html#3-sign-in-to-a-provider","using/getting-started.html#4-run-your-first-task","using/getting-started.html#approval-mode","using/getting-started.html#where-to-go-next","using/configuring-providers.html#configuring-providers","using/configuring-providers.html#anatomy-of-a-provider-entry","using/configuring-providers.html#openai-api-key","using/configuring-providers.html#deepseek","using/configuring-providers.html#openrouter-openai-compatible-gateway","using/configuring-providers.html#anthropic","using/configuring-providers.html#other-openai-compatible-hosts","using/configuring-providers.html#amazon-bedrock","using/configuring-providers.html#ollama-local","using/configuring-providers.html#lm-studio-local","using/configuring-providers.html#pinning-models-for-roles-and-ci","using/configuring-providers.html#verify","using/configuring-providers.html#see-also","concepts/index.html#core-concepts","concepts/index.html#pages","concepts/index.html#foundations-that-pair-with-these-pages","concepts/index.html#how-the-pieces-fit","concepts/index.html#related-reading","concepts/sessions-turns-threads.html#sessions-turns-and-threads","concepts/sessions-turns-threads.html#lifecycle-of-a-run","concepts/sessions-turns-threads.html#what-a-session-is","concepts/sessions-turns-threads.html#what-a-turn-is","concepts/sessions-turns-threads.html#threads-and-the-active-leaf","concepts/sessions-turns-threads.html#context-pressure-and-compaction","concepts/sessions-turns-threads.html#the-rollout","concepts/sessions-turns-threads.html#how-the-pieces-relate","concepts/sessions-turns-threads.html#where-the-details-live","concepts/permission-model.html#permission-model","concepts/permission-model.html#tool-tiers","concepts/permission-model.html#modes","concepts/permission-model.html#the-working-directory-boundary","concepts/permission-model.html#secrets-in-arguments","concepts/permission-model.html#per-tool-overrides","concepts/permission-model.html#critical-bash-commands","concepts/permission-model.html#on-deny","concepts/permission-model.html#related","concepts/model-contract.html#model-contract","concepts/model-contract.html#the-three-things-you-bring","concepts/model-contract.html#example-shape","concepts/model-contract.html#what-the-harness-owns","concepts/model-contract.html#what-the-provider-owns","concepts/model-contract.html#system-prompts-and-tool-schemas","concepts/model-contract.html#freeform-vs-function-tools","concepts/model-contract.html#harness-vs-provider-a-clear-split","concepts/model-contract.html#provider-data-at-load-time","concepts/model-contract.html#per-role-models","concepts/model-contract.html#automation-note","concepts/model-contract.html#what-stays-constant","concepts/model-contract.html#next","features/index.html#features","features/index.html#interactive-surfaces","features/index.html#extend-and-customize","features/index.html#recipes","using/editing.html#editing-and-repair","using/editing.html#failure-modes","using/editing.html#write-path-versus-edit-path","using/editing.html#tools","using/editing.html#hashline-workflow","using/editing.html#verification-after-a-mutation","using/editing.html#safety","features/sandbox.html#approvals","features/sandbox.html#tool-tiers","features/sandbox.html#modes","features/sandbox.html#the-approval-prompt","features/sandbox.html#headless","features/sandbox.html#critical-bash-commands","features/sandbox.html#related","using/safety.html#safety","using/safety.html#operator-visible-cases","using/safety.html#headless","using/safety.html#related","features/cpu-limit.html#cpu-limits","features/cpu-limit.html#what-is-capped","features/cpu-limit.html#how-it-is-enforced","features/cpu-limit.html#example","features/cpu-limit.html#related","features/secrets.html#secrets","features/secrets.html#turning-it-on","features/secrets.html#your-first-secret","features/secrets.html#adding-your-own-keywords","features/secrets.html#what-the-model-sees","features/secrets.html#using-a-secret-in-a-command","features/secrets.html#what-the-agent-knows-and-when","features/secrets.html#why-a-removal-is-stated-rather-than-left-to-the-list","features/secrets.html#the-vault-storing-a-credential-with-secret","features/secrets.html#storing-a-credential","features/secrets.html#what-you-are-told-when-it-is-stored","features/secrets.html#managing-what-you-stored","features/secrets.html#finding-what-is-masked-and-not-stored","features/secrets.html#on-a-client-with-no-terminal","features/secrets.html#when-a-vault-file-cannot-be-read","features/secrets.html#lifetimes","features/secrets.html#scope","features/secrets.html#encryption-and-what-it-does-not-do","features/secrets.html#seeing-which-credential-was-used-where","features/secrets.html#declaring-secrets-yourself","features/secrets.html#the-two-modes","features/secrets.html#the-8-character-minimum","features/secrets.html#when-something-cannot-be-protected","features/secrets.html#where-the-value-goes","features/secrets.html#what-this-does-not-protect","features/secrets.html#reference","using/models.html#models-and-providers","using/models.html#api-keys-byok","using/models.html#minimal-byok-shape","using/models.html#built-in-providers","using/models.html#local-models-ollama-and-lm-studio","using/models.html#mid-session-model-switch","using/models.html#model-selection","using/models.html#per-model-harness-settings","using/models.html#harness-profiles","using/models.html#switching-providers","using/models.html#model-selection-notes","using/models.html#where-to-go-next","using/sessions.html#sessions","using/sessions.html#common-session-actions","using/sessions.html#long-work","using/sessions.html#session-files-are-trees","using/sessions.html#navigating-the-tree","using/sessions.html#forking-and-branching-to-a-new-file","using/sessions.html#exporting-a-session","using/sessions.html#cleaning-up-old-sessions","using/sessions.html#typing-while-the-agent-works","using/sessions.html#next","features/subagents.html#subagents","features/subagents.html#what-you-get-out-of-the-box","features/subagents.html#how-hard-to-push","features/subagents.html#what-counts-as-delegable-work","features/subagents.html#choosing-agents","features/subagents.html#choosing-models","features/subagents.html#fallback-models","features/subagents.html#watching-a-run","features/subagents.html#limits-and-isolation","features/subagents.html#when-each-budget-starts-counting","features/subagents.html#only-idle-and-parked-agents-have-a-deadline","features/subagents.html#turning-it-off","features/subagents.html#if-the-session-cannot-be-saved","features/subagents.html#nesting-depth","features/cockpit.html#multi-agent-monitoring","features/cockpit.html#status-line","features/cockpit.html#session-tree-and-agents","features/cockpit.html#the-live-roster","features/cockpit.html#inter-agent-messaging","features/cockpit.html#swarm-extension","features/keybindings.html#keybindings","features/keybindings.html#customize-keybindings","features/keybindings.html#slash-commands","features/web-search.html#web-search","features/web-search.html#configuration","features/web-search.html#provider-support","features/web-search.html#exa-research-and-webset-tools","features/web-search.html#approvals","features/review.html#code-review","features/review.html#review","features/review.html#advisor","features/review.html#plan-review","features/review.html#non-interactive","features/review.html#approvals","features/review.html#related","features/exec.html#non-interactive-mode-veyyon---print","features/exec.html#session-and-config","features/exec.html#output-and-models","features/exec.html#related","using/themes.html#themes-and-identity","using/themes.html#bundled-themes","using/themes.html#changing-theme","using/themes.html#backgrounds","using/themes.html#painted-ground","using/themes.html#what-the-theme-covers","using/themes.html#identity-elsewhere","features/collab.html#collab-live-session-sharing","features/collab.html#quick-start","features/collab.html#commands","features/collab.html#link-format","features/collab.html#end-to-end-encryption","features/collab.html#guest-permission-model","features/collab.html#web-client","features/collab.html#settings","features/collab.html#self-hosting-the-relay","features/collab.html#close-codes","features/collab.html#architecture-notes","using/task-guides.html#task-guides","using/task-guides.html#automate-a-check-on-every-edit-hooks","using/task-guides.html#run-a-bounded-task-from-a-script-or-ci","using/task-guides.html#give-the-agent-a-new-tool-mcp-or-skills","using/task-guides.html#choose-the-surface","using/task-guides.html#path-1-add-an-mcp-server","using/task-guides.html#path-2-author-a-skill","using/task-guides.html#share-context-across-sessions-memory-and-branching","using/task-guides.html#memory-carry-guidance-into-new-threads","using/task-guides.html#branching-explore-without-losing-the-main-line","using/task-guides.html#memory-vs-branching","using/task-guides.html#track-long-work-without-repeated-reminder-walls","using/task-guides.html#see-also","using/examples.html#examples","using/examples.html#understand-a-code-path","using/examples.html#make-a-small-fix","using/examples.html#improve-docs-with-code-truth","using/examples.html#review-a-change","using/examples.html#recover-a-malformed-tool-call","using/examples.html#continue-through-long-context","using/examples.html#verify-before-claiming-done","using/examples.html#use-the-modelprovider-contract","using/examples.html#recorded-end-to-end-workflow","features/plan-mode.html#plan-mode-and-goals","features/plan-mode.html#plan-mode","features/plan-mode.html#enabling","features/plan-mode.html#behavior","features/plan-mode.html#goal-mode","features/plan-mode.html#enabling-1","features/plan-mode.html#goal-state","features/plan-mode.html#goal-tool","features/plan-mode.html#example","features/plan-mode.html#vibe-mode","features/skills.html#skills","features/skills.html#skill-locations","features/skills.html#importing-another-tools-skills","features/skills.html#instruction-layers","features/skills.html#profiles-isolate-skills","features/skills.html#skill-structure","features/skills.html#the-skill-file-skillmd","features/skills.html#configuration","features/skills.html#master-switch","features/skills.html#skill-commands","features/skills.html#manage-individual-skills","features/skills.html#interactive-tui-controls","features/skills.html#slash-commands","features/skills.html#related-recipes","features/skills-authoring.html#skills-authoring","features/skills-authoring.html#directory-structure","features/skills-authoring.html#skillmd-frontmatter","features/skills-authoring.html#writing-the-body","features/skills-authoring.html#configuring-in-configyml","features/skills-authoring.html#worked-example-a-profile-skill","features/skills-authoring.html#invoking-the-skill","features/plugins.html#plugins","features/plugins.html#plugin-structure","features/plugins.html#plugin-manifest-pluginjson","features/plugins.html#marketplaces","features/plugins.html#file-locations","features/plugins.html#command-line-interface","features/plugins.html#managing-plugins","features/plugins.html#managing-marketplaces","features/plugins.html#tui-integration","features/plugins.html#slash-commands","features/plugins.html#registry-files","features/plugins.html#related-recipes","features/extensions.html#extensions","features/extensions.html#what-an-extension-is","features/extensions.html#runtime-model","features/extensions.html#quick-start","features/extensions.html#extension-api-surfaces","features/extensions.html#1-registration-and-actions-extensionapi","features/extensions.html#message-delivery-semantics","features/extensions.html#2-handler-context-extensioncontext","features/extensions.html#model-selection-ctxmodels","features/extensions.html#3-command-context-extensioncommandcontext","features/extensions.html#event-surface-current-names-and-behavior","features/extensions.html#session-lifecycle","features/extensions.html#prompt-and-turn-lifecycle","features/extensions.html#tool-lifecycle","features/extensions.html#reliabilityruntime-signals","features/extensions.html#user-command-interception","features/extensions.html#resources_discover","features/extensions.html#tool-authoring-details","features/extensions.html#ui-integration-points","features/extensions.html#interactive-mode-extension-ui-controllerts","features/extensions.html#rpc-mode-rpc-modets","features/extensions.html#printheadlesssubagent-paths","features/extensions.html#acp-mode","features/extensions.html#session-and-state-patterns","features/extensions.html#rendering-extension-points","features/extensions.html#custom-message-renderer","features/extensions.html#assistant-thinking-renderer","features/extensions.html#tool-callresult-renderer","features/extensions.html#constraints-and-pitfalls","features/extensions.html#extensions-vs-hooks-vs-custom-tools","features/extensions-authoring.html#name-authoring-extensions-description-use-when-creating-a-new-veyyon-extension-covers-extensionapi-factory-signature-toolcommandevent-registration-and-local-dev-testing","features/extensions-authoring.html#authoring-extensions","features/extensions-authoring.html#minimum-viable-extension","features/extensions-authoring.html#full-example","features/extensions-authoring.html#discovery-paths","features/extensions-authoring.html#packagejson-manifest","features/extensions-authoring.html#registering-commands","features/extensions-authoring.html#registering-tools","features/extensions-authoring.html#subscribing-to-events","features/extensions-authoring.html#extension-vs-hook-when-to-use-which","features/extensions-authoring.html#debugging","features/extensions-authoring.html#important-constraints","features/extensions-authoring.html#further-reading","using/custom-tools.html#custom-tools","using/custom-tools.html#what-this-is-and-is-not","using/custom-tools.html#integration-paths-in-current-code","using/custom-tools.html#discovery-locations-loader-api","using/custom-tools.html#important-behavior","using/custom-tools.html#module-contract","using/custom-tools.html#api-surface-passed-to-factories-customtoolapi","using/custom-tools.html#execution-contract-and-typing","using/custom-tools.html#how-tools-are-exposed-to-the-model","using/custom-tools.html#rendering-hooks","using/custom-tools.html#sessionstate-handling","using/custom-tools.html#failures-and-cancellation-semantics","using/custom-tools.html#synchronousasync-failures","using/custom-tools.html#cancellation","using/custom-tools.html#onsession-errors","using/custom-tools.html#real-constraints-to-design-for","features/marketplace.html#marketplace-plugin-system","features/marketplace.html#quick-start","features/marketplace.html#concepts","features/marketplace.html#commands","features/marketplace.html#marketplace-sources","features/marketplace.html#catalog-format-marketplacejson","features/marketplace.html#required-fields","features/marketplace.html#plugin-entry-fields","features/marketplace.html#plugin-source-formats","features/marketplace.html#on-disk-layout","features/marketplace.html#naming-rules","features/marketplace-authoring.html#name-authoring-marketplaces-description-use-when-creating-a-new-veyyon-marketplace-covers-marketplacejson-schema-source-types-install-commands-and-publishing","features/marketplace-authoring.html#authoring-marketplaces","features/marketplace-authoring.html#minimum-viable-marketplace","features/marketplace-authoring.html#marketplacejson-schema","features/marketplace-authoring.html#top-level-fields","features/marketplace-authoring.html#plugin-entry-fields","features/marketplace-authoring.html#full-catalog-example","features/marketplace-authoring.html#plugin-source-types","features/marketplace-authoring.html#1-relative-path-string","features/marketplace-authoring.html#2-git-url","features/marketplace-authoring.html#3-github-shorthand","features/marketplace-authoring.html#4-git-subdirectory-monorepo","features/marketplace-authoring.html#5-npm-package","features/marketplace-authoring.html#plugin-structure","features/marketplace-authoring.html#install-command","features/marketplace-authoring.html#naming-rules","features/marketplace-authoring.html#publishing-workflow","features/marketplace-authoring.html#further-reading","features/hooks.html#hooks","features/hooks.html#module-shape","features/hooks.html#discovery","features/hooks.html#lifecycle-extension-bus","features/hooks.html#typical-uses","features/hooks.html#related","features/hooks-authoring.html#name-authoring-hooks-description-use-when-creating-a-new-veyyon-hook-covers-hookapi-event-catalog-blockingoverriding-tool-calls-and-context-modification","features/hooks-authoring.html#authoring-hooks","features/hooks-authoring.html#factory-signature","features/hooks-authoring.html#event-catalog","features/hooks-authoring.html#tool-lifecycle","features/hooks-authoring.html#session-lifecycle","features/hooks-authoring.html#agentturn-lifecycle","features/hooks-authoring.html#pre-tool-blocking-contract","features/hooks-authoring.html#post-tool-override-contract","features/hooks-authoring.html#context-modification-contract","features/hooks-authoring.html#three-complete-examples","features/hooks-authoring.html#1-rm-rf-blocker","features/hooks-authoring.html#2-api-key-redactor","features/hooks-authoring.html#3-context-filter","features/hooks-authoring.html#ui-methods-in-hook-context","features/hooks-authoring.html#further-reading","features/lsp.html#lsp-configuration-in-veyyon","features/lsp.html#auto-detection","features/lsp.html#config-file-locations","features/lsp.html#file-shape","features/lsp.html#serverconfig-fields","features/lsp.html#capabilities","features/lsp.html#common-recipes","features/lsp.html#override-a-built-in-servers-settings","features/lsp.html#disable-a-built-in-server","features/lsp.html#register-a-custom-server","features/lsp.html#set-a-global-idle-timeout","features/lsp.html#disable-a-server-for-one-project-keep-it-globally","features/lsp.html#built-in-server-list","features/advisor.html#advisor-watchdogmd-and-watchdogyml","features/advisor.html#implementation-files","features/advisor.html#enabling-the-advisor","features/advisor.html#what-the-advisor-sees","features/advisor.html#tools-and-isolation","features/advisor.html#emission-guard","features/advisor.html#bounded-catch-up-with-advisorsyncbacklog","features/advisor.html#watchdogmd","features/advisor.html#discovery-locations","features/advisor.html#-imports","features/advisor.html#prompt-order","features/advisor.html#watchdogyml","features/advisor.html#discovery-locations-1","features/advisor.html#subagents","features/advisor.html#cost-and-context-behavior","features/advisor.html#transcript-persistence-and-observability","features/python-repl.html#eval-tool-python-backend","features/python-repl.html#scope-and-key-files","features/python-repl.html#what-evals-python-backend-is","features/python-repl.html#kernel-lifecycle","features/python-repl.html#wire-protocol-ndjson-host--runner","features/python-repl.html#magics","features/python-repl.html#session-persistence-semantics","features/python-repl.html#multi-cell-behavior-in-a-single-tool-call","features/python-repl.html#environment-filtering-and-runtime-resolution","features/python-repl.html#tool-availability-and-mode-selection","features/python-repl.html#persisted-helper-state","features/python-repl.html#execution-flow-and-cancellationtimeout","features/python-repl.html#cell-timeout","features/python-repl.html#kernel-execution-cancellation","features/python-repl.html#stdin-behavior","features/python-repl.html#output-capture-and-rendering","features/python-repl.html#captured-output-classes","features/python-repl.html#matplotlib","features/python-repl.html#storage-and-truncation","features/python-repl.html#renderer-behavior","features/python-repl.html#operational-troubleshooting","features/python-repl.html#relevant-environment-variables","features/mcp.html#mcp","features/mcp.html#what-it-looks-like","features/mcp.html#not-to-be-confused-with","features/mcp.html#related","using/mcp-setup.html#mcp-server-setup","using/mcp-setup.html#where-servers-are-configured","using/mcp-setup.html#file-shape","using/mcp-setup.html#choose-a-transport","using/mcp-setup.html#pass-environment-variables","using/mcp-setup.html#authenticate","using/mcp-setup.html#bearer-token-via-header","using/mcp-setup.html#oauth","using/mcp-setup.html#approve-tools","using/mcp-setup.html#in-the-tui","using/mcp-setup.html#resolve-common-errors","using/mcp-setup.html#server-not-found","using/mcp-setup.html#timeout","using/mcp-setup.html#authentication-failure","using/mcp-setup.html#model-cannot-see-the-tools","using/mcp-setup.html#where-to-go-next","features/connectors.html#connectors-and-apps","features/connectors.html#what-ships-instead","features/connectors.html#see-also","features/branching.html#session-branching","features/branching.html#navigate-in-place-tree","features/branching.html#filters","features/branching.html#selection-behavior","features/branching.html#new-session-file-branch-and-fork","features/branching.html#ephemeral-side-questions-btw","features/branching.html#configuration","features/branching.html#command-availability","features/branching.html#see-also","features/memory.html#memory","features/memory.html#backends","features/memory.html#mnemopi-recommended-for-long-running-work","features/memory.html#local-summary-pipeline","features/memory.html#compaction-primary-knobs","features/memory.html#what-the-model-sees","features/memory.html#configuration","features/profiles.html#profiles","features/profiles.html#layout","features/profiles.html#which-profile-launches","features/profiles.html#what-a-profile-owns-shipped","features/profiles.html#activating-a-profile","features/profiles.html#tui-profile-commands","features/profiles.html#profile-names-and-renaming","features/profiles.html#creating-and-managing-profiles","features/profiles.html#reading-the-size-in-profile-list---json","features/profiles.html#onboarding-import","features/profiles.html#model-policies-and-roles-per-profile","features/profiles.html#see-also","using/roles-and-profiles.html#models-roles-and-profiles","using/roles-and-profiles.html#concepts","using/roles-and-profiles.html#interactive-model","using/roles-and-profiles.html#built-in-roles","using/roles-and-profiles.html#subagent-policy-and-compaction-overrides","using/roles-and-profiles.html#cycling-roles-ctrlp","using/roles-and-profiles.html#profiles","using/roles-and-profiles.html#instruction-files-global-vs-per-profile-agentsmd","using/roles-and-profiles.html#approvals","using/roles-and-profiles.html#related","features/personalities.html#personalities","features/personalities.html#built-in-personalities","features/personalities.html#configuring-personality","features/personalities.html#extending-the-catalog","features/personalities.html#boundaries","features/personalities.html#see-also","features/speech.html#speech","features/speech.html#setup","features/speech.html#veyyon-say","features/speech.html#spoken-replies","features/speech.html#voice-input","features/speech.html#synthesis-backend","features/speech.html#cache-and-worker-recovery","features/export-import.html#export-and-import","features/export-import.html#session-export","features/export-import.html#migration-from-claude-code","using/configuration.html#configuration","using/configuration.html#where-settings-live","using/configuration.html#session-working-directory-sessionworkdir-vs-set_cwd","using/configuration.html#your-comments-and-formatting-survive-a-save","using/configuration.html#when-a-settings-file-has-a-syntax-error","using/configuration.html#when-a-setting-cannot-be-saved","using/configuration.html#pick-models-and-providers","using/configuration.html#stay-safe-approvals","using/configuration.html#run-unattended-or-in-ci","using/configuration.html#control-context-memory-and-compaction","using/configuration.html#save-tokens-with-project-shorthand-argot-experimental","using/configuration.html#you-never-see-a-handle","using/configuration.html#choose-which-models-write-shorthand","using/configuration.html#choose-when-a-project-is-loaded","using/configuration.html#size-the-dictionary","using/configuration.html#stop-shorthand-in-a-large-context","using/configuration.html#choose-how-subagents-start","using/configuration.html#restrict-tools-for-a-repo-or-role","using/configuration.html#set-the-default-working-directory","using/configuration.html#profiles","using/configuration.html#wire-mcp-servers-and-hooks","using/configuration.html#related","features/feature-flags.html#feature-flags","features/feature-flags.html#settings-schema-flags-features","features/feature-flags.html#plugin-feature-gates","features/feature-flags.html#related","using/extending.html#tools-skills-and-extension-data","using/extending.html#tools","using/extending.html#skills","using/extending.html#plugins","using/extending.html#mcp","using/extending.html#hooks","using/extending.html#related","using/migration-guide.html#migration-guide","using/migration-guide.html#before-you-upgrade","using/migration-guide.html#config-schema-updates","using/migration-guide.html#common-schema-changes","using/migration-guide.html#updating-your-config","using/migration-guide.html#session-and-state-data","using/migration-guide.html#if-you-need-to-force-a-state-rebuild","using/migration-guide.html#rolling-back-a-binary","using/migration-guide.html#checking-health-after-an-upgrade","using/migration-guide.html#where-to-go-next","reference/index.html#reference","reference/cli.html#cli-reference","reference/cli.html#starting-a-session","reference/cli.html#registered-subcommands","reference/cli.html#studying-a-session","reference/cli.html#exit-codes","reference/slash-commands.html#slash-commands","reference/slash-commands.html#every-argument-is-a-plain-word","reference/slash-commands.html#a-spelling-that-was-an-option","reference/slash-commands.html#a-bare-command-that-has-subcommands","reference/slash-commands.html#session-and-navigation","reference/slash-commands.html#model-modes-and-behavior","reference/slash-commands.html#tools-context-and-jobs","reference/slash-commands.html#auth-and-usage","reference/slash-commands.html#extensions","reference/slash-commands.html#side-agents-and-misc","reference/slash-commands.html#every-subcommand","reference/tools.html#tools-reference","reference/tools.html#core-loop","reference/tools.html#edit-and-write","reference/tools.html#read-and-search","reference/tools.html#shell-and-execution","reference/tools.html#long-running-and-stuck-commands","reference/tools.html#agent-coordination","reference/tools.html#memory-when-backend-enabled","reference/tools.html#other-builtins","reference/keybindings-ref.html#keybindings-reference","reference/keybindings-ref.html#app","reference/keybindings-ref.html#composer","reference/keybindings-ref.html#editor","reference/keybindings-ref.html#lists-and-selectors","reference/keybindings-ref.html#vim-mode","reference/keybindings-ref.html#customizing-real-path-keybindingsyml","reference/keybindings-config.html#keybindings","reference/keybindings-config.html#customize-keybindings","reference/keybindings-config.html#common-action-ids","reference/keybindings-config.html#status-line-affordances","reference/environment.html#environment-variables","reference/environment.html#location-and-identity","reference/environment.html#authentication","reference/environment.html#provider-keys","reference/environment.html#local-and-self-hosted-providers","reference/environment.html#tls-and-certificates","reference/environment.html#install-and-updates","reference/environment.html#mcp","reference/environment.html#remote-auth-broker-optional","reference/environment.html#repair","reference/environment.html#terminal-behavior","reference/environment.html#removed--does-not-exist","reference/environment-complete.html#environment-variables-complete","reference/environment-complete.html#resolution-model-and-precedence","reference/environment-complete.html#1-modelprovider-authentication","reference/environment-complete.html#core-provider-credentials","reference/environment-complete.html#githubcopilot-tokens","reference/environment-complete.html#auth-broker--auth-gateway-remote-credential-vault","reference/environment-complete.html#2-provider-specific-runtime-configuration","reference/environment-complete.html#anthropic-foundry-gateway-azure--enterprise-proxy","reference/environment-complete.html#amazon-bedrock","reference/environment-complete.html#azure-openai-responses","reference/environment-complete.html#google-vertex-ai","reference/environment-complete.html#kimi","reference/environment-complete.html#gemini-cli-compatibility","reference/environment-complete.html#openai-codex-responses-featuredebug-controls","reference/environment-complete.html#cursor-provider-debug","reference/environment-complete.html#transport-selection-openrouter-perplexity","reference/environment-complete.html#prompt-cache-controls","reference/environment-complete.html#3-web-search-subsystem","reference/environment-complete.html#search-provider-credentials","reference/environment-complete.html#anthropic-web-search-auth-chain","reference/environment-complete.html#perplexity-oauth-flow-behavior-flag","reference/environment-complete.html#4-python-tooling-and-kernel-runtime","reference/environment-complete.html#5-agentruntime-behavior-toggles","reference/environment-complete.html#6-storage-and-config-root-paths","reference/environment-complete.html#7-shelltool-execution-environment","reference/environment-complete.html#8-uithemesession-detection-auto-detected-env","reference/environment-complete.html#9-tui-runtime-flags-shared-package-affects-coding-agent-ux","reference/environment-complete.html#10-commit-generation-controls","reference/environment-complete.html#security-sensitive-variables","reference/settings.html#settings","reference/settings.html#where-settings-live","reference/settings.html#config-file-formats","reference/settings.html#nested-and-flat-keys","reference/settings.html#reading-and-writing-settings","reference/settings.html#subcommands","reference/settings.html#value-parsing","reference/settings.html#where-writes-go","reference/settings.html#precedence","reference/settings.html#environment-overrides","reference/settings.html#merge-rules","reference/settings.html#worked-example-profile-vs-overlay","reference/settings.html#per-repository-settings","reference/settings.html#path-scoped-arrays","reference/settings.html#provider-and-source-disabling","reference/settings.html#settings-catalog","reference/settings.html#models","reference/settings.html#advisor","reference/settings.html#thinking","reference/settings.html#sampling","reference/settings.html#retry-and-fallback","reference/settings.html#tools-and-approvals","reference/settings.html#subagents","reference/settings.html#shell-eval-and-lsp","reference/settings.html#files-editing-and-reading","reference/settings.html#automatic-tool-issue-reports","reference/settings.html#context-compaction-and-memory","reference/settings.html#appearance-and-terminal","reference/settings.html#interaction","reference/settings.html#providers-and-services","reference/settings.html#global-all-profiles","reference/settings.html#every-other-setting","reference/settings.html#legacy-migration","reference/settings.html#startup-migration-to-configyml","reference/settings.html#field-level-migrations","reference/settings.html#troubleshooting","reference/settings.html#a-veyyonconfigyml-in-a-repository-is-ignored","reference/settings.html#an-array-from-my-profile-disappeared-under-an-overlay","reference/settings.html#a-provider-is-still-available-after-editing-config","reference/settings.html#veyyon-config-set-changed-the-wrong-file","reference/settings.html#veyyon-config-reset-removed-my-global-override","reference/settings.html#a---config-overlay-fails-at-startup","reference/settings.html#an-environment-variable-beats-my-config","reference/settings.html#veyyon-config-set-key-says-unknown-setting","reference/settings-reference.html#settings-reference","reference/settings-reference.html#appearance","reference/settings-reference.html#theme","reference/settings-reference.html#status-line","reference/settings-reference.html#display","reference/settings-reference.html#model","reference/settings-reference.html#compaction","reference/settings-reference.html#roles","reference/settings-reference.html#thinking","reference/settings-reference.html#sampling","reference/settings-reference.html#prompt","reference/settings-reference.html#retry--fallback","reference/settings-reference.html#advisor","reference/settings-reference.html#prewalk","reference/settings-reference.html#vision","reference/settings-reference.html#interaction","reference/settings-reference.html#input","reference/settings-reference.html#approvals","reference/settings-reference.html#notifications","reference/settings-reference.html#speech","reference/settings-reference.html#collab","reference/settings-reference.html#magic-keywords","reference/settings-reference.html#startup--updates","reference/settings-reference.html#profile","reference/settings-reference.html#power-macos","reference/settings-reference.html#agent","reference/settings-reference.html#git","reference/settings-reference.html#resources","reference/settings-reference.html#cpu","reference/settings-reference.html#memory","reference/settings-reference.html#disk","reference/settings-reference.html#processes","reference/settings-reference.html#context","reference/settings-reference.html#general","reference/settings-reference.html#prompt-cache","reference/settings-reference.html#session-instrumentation","reference/settings-reference.html#rules","reference/settings-reference.html#rules-1","reference/settings-reference.html#stream-interrupts-ttsr","reference/settings-reference.html#memory-1","reference/settings-reference.html#general-1","reference/settings-reference.html#mnemopi","reference/settings-reference.html#hindsight","reference/settings-reference.html#files","reference/settings-reference.html#editing","reference/settings-reference.html#reading","reference/settings-reference.html#read-summaries","reference/settings-reference.html#lsp","reference/settings-reference.html#shell","reference/settings-reference.html#bash","reference/settings-reference.html#eval--runtimes","reference/settings-reference.html#tools","reference/settings-reference.html#available-tools","reference/settings-reference.html#todos","reference/settings-reference.html#grep--browser","reference/settings-reference.html#github","reference/settings-reference.html#output-limits","reference/settings-reference.html#execution","reference/settings-reference.html#discovery--mcp","reference/settings-reference.html#developer","reference/settings-reference.html#tasks","reference/settings-reference.html#modes","reference/settings-reference.html#commands--skills","reference/settings-reference.html#subagents","reference/settings-reference.html#delegation","reference/settings-reference.html#subagents-1","reference/settings-reference.html#limits","reference/settings-reference.html#auto-close","reference/settings-reference.html#isolation","reference/settings-reference.html#coordination","reference/settings-reference.html#providers","reference/settings-reference.html#accounts","reference/settings-reference.html#services","reference/settings-reference.html#discovery","reference/settings-reference.html#fireworks","reference/settings-reference.html#tiny-model","reference/settings-reference.html#protocol","reference/settings-reference.html#timeouts","reference/settings-reference.html#privacy","reference/settings-reference.html#experimental","reference/settings-reference.html#argot","reference/settings-reference.html#tool-calling","reference/settings-reference.html#auto-learn","reference/settings-reference.html#global","reference/settings-reference.html#profiles","reference/settings-reference.html#credentials","reference/settings-reference.html#auth-broker","reference/settings-reference.html#configuration-file-only","reference/models-yml.html#model-and-provider-configuration-modelsyml--modelsyaml","reference/models-yml.html#what-controls-model-behavior","reference/models-yml.html#config-file-location-and-legacy-behavior","reference/models-yml.html#modelsyml--modelsyaml-shape","reference/models-yml.html#provider-level-fields","reference/models-yml.html#allowed-providermodel-api-values","reference/models-yml.html#allowed-authdiscovery-values","reference/models-yml.html#validation-rules-current","reference/models-yml.html#full-custom-provider-models-is-non-empty","reference/models-yml.html#override-only-provider-models-missing-or-empty","reference/models-yml.html#discovery","reference/models-yml.html#model-value-checks","reference/models-yml.html#secret-values","reference/models-yml.html#merge-and-override-order","reference/models-yml.html#provider-model-cache-and-static-fingerprint","reference/models-yml.html#canonical-model-equivalence-and-coalescing","reference/models-yml.html#modelsyml-equivalence-config","reference/models-yml.html#canonical-resolution-behavior","reference/models-yml.html#runtime-discovery-integration","reference/models-yml.html#implicit-ollama-discovery","reference/models-yml.html#implicit-llamacpp-discovery","reference/models-yml.html#implicit-lm-studio-discovery","reference/models-yml.html#litellm-provider-discovery","reference/models-yml.html#explicit-provider-discovery","reference/models-yml.html#proxy-discovery-discoverytype-proxy","reference/models-yml.html#extension-provider-registration","reference/models-yml.html#auth-and-api-key-resolution-order","reference/models-yml.html#broker-mode","reference/models-yml.html#model-availability-vs-all-models","reference/models-yml.html#runtime-model-resolution","reference/models-yml.html#cli-and-pattern-parsing","reference/models-yml.html#initial-model-selection-priority","reference/models-yml.html#role-aliases-and-settings","reference/models-yml.html#model-and-veyyon-models","reference/models-yml.html#context-promotion-model-level-fallback-chains","reference/models-yml.html#trigger-and-order","reference/models-yml.html#target-selection","reference/models-yml.html#openai-codex-websocket-handoff","reference/models-yml.html#persistence-behavior","reference/models-yml.html#configuring-explicit-fallback-chains","reference/models-yml.html#compatibility-and-routing-fields","reference/models-yml.html#anthropic-compatibility-anthropic-messages","reference/models-yml.html#strict-tool-schemas-disablestricttools","reference/models-yml.html#practical-examples","reference/models-yml.html#local-openai-compatible-endpoint-no-auth","reference/models-yml.html#hosted-proxy-with-env-based-key","reference/models-yml.html#override-built-in-provider-route--model-metadata","reference/models-yml.html#legacy-consumer-caveat","reference/models-yml.html#failure-mode","reference/providers.html#providers","reference/providers.html#when-a-provider-is-available","reference/providers.html#credentials-and-precedence","reference/providers.html#oauth-vs-api-key-and-provider-scoped-logins","reference/providers.html#pinning-a-key-in-modelsyml","reference/providers.html#environment-variables-and-env-files","reference/providers.html#core-providers","reference/providers.html#additional-hosted-providers","reference/providers.html#env-discovery-and-precedence","reference/providers.html#built-in-local-engines","reference/providers.html#disabling-model-providers","reference/providers.html#per-project-provider-control","reference/providers.html#path-scoped-disabledproviders","reference/providers.html#provider-ids-vs-discovery-provider-ids","reference/providers.html#custom-providers-in-modelsyml","reference/providers.html#troubleshooting","reference/mcp-config.html#mcp-configuration-in-veyyon","reference/mcp-config.html#where-mcp-config-lives","reference/mcp-config.html#profiles","reference/mcp-config.html#add-a-schema-reference","reference/mcp-config.html#file-shape","reference/mcp-config.html#supported-server-fields","reference/mcp-config.html#stdio-transport","reference/mcp-config.html#http-transport","reference/mcp-config.html#sse-transport","reference/mcp-config.html#stopping-a-stdio-server","reference/mcp-config.html#auth-fields","reference/mcp-config.html#auth","reference/mcp-config.html#oauth","reference/mcp-config.html#common-copy-paste-examples","reference/mcp-config.html#filesystem-server-via-stdio","reference/mcp-config.html#github-hosted-server-via-http","reference/mcp-config.html#github-local-server-via-docker","reference/mcp-config.html#slack-hosted-server-via-oauth","reference/mcp-config.html#secrets-and-variable-resolution","reference/mcp-config.html#discovery-time--expansion","reference/mcp-config.html#pre-connect-envheader-resolution","reference/mcp-config.html#when-a-command-runs-again","reference/mcp-config.html#when-a-stored-credential-cannot-be-presented","reference/mcp-config.html#disabledservers","reference/mcp-config.html#mcp-add-vs-editing-json-directly","reference/mcp-config.html#validation-rules-veyyon-enforces","reference/mcp-config.html#discovery-and-precedence","reference/mcp-config.html#troubleshooting","reference/mcp-config.html#server-name-stdio-server-requires-command-field","reference/mcp-config.html#server-name-both-command-and-url-are-set","reference/mcp-config.html#mcp-add-worked-but-the-server-still-does-not-connect","reference/mcp-config.html#the-server-exists-in-another-tools-config-but-not-in-veyyon","reference/mcp-config.html#a-call-fails-with-a-protocol-error-rather-than-a-timeout","reference/mcp-config.html#references","reference/approval-mode.html#tool-approval-mode","reference/approval-mode.html#modes","reference/approval-mode.html#the-working-directory-boundary","reference/approval-mode.html#the-secret-use-boundary","reference/approval-mode.html#the-yolo-command-full-session-bypass","reference/approval-mode.html#user-overrides","reference/approval-mode.html#safety-overrides","reference/approval-mode.html#per-tool-prompt-details","reference/approval-mode.html#defining-approval-on-tools","reference/approval-mode.html#acp-sessions","reference/approval-mode.html#subagents","reference/project-trust.html#project-trust","reference/project-trust.html#what-is-withheld","reference/project-trust.html#deciding","reference/project-trust.html#what-a-decision-records","reference/project-trust.html#reading-a-refusal","reference/theme.html#theming-reference","reference/theme.html#what-the-theme-system-controls","reference/theme.html#theme-json-shape","reference/theme.html#required-color-tokens-current","reference/theme.html#core-text-and-borders-11","reference/theme.html#background-blocks-7","reference/theme.html#messagetool-text-5","reference/theme.html#markdown-10","reference/theme.html#tool-diff--syntax-highlighting-12","reference/theme.html#modethinking-borders-8","reference/theme.html#status-line-segment-colors-13","reference/theme.html#optional-tokens","reference/theme.html#purpose-accents-5-optional","reference/theme.html#composerbg-optional","reference/theme.html#export-section-optional","reference/theme.html#symbols-section-optional","reference/theme.html#built-in-vs-custom-theme-sources","reference/theme.html#loading-validation-and-resolution","reference/theme.html#terminal-color-mode-behavior","reference/theme.html#runtime-switching-behavior","reference/theme.html#initial-theme-inittheme","reference/theme.html#explicit-switching-settheme","reference/theme.html#preview-switching-previewtheme","reference/theme.html#watchers-and-live-reload","reference/theme.html#color-blind-mode-behavior","reference/theme.html#where-theme-settings-are-persisted","reference/theme.html#creating-a-custom-theme-practical","reference/theme.html#testing-custom-themes","reference/theme.html#real-constraints-and-caveats","reference/hooks.html#hooks","reference/hooks.html#runtime-loading","reference/hooks.html#key-files","reference/hooks.html#what-a-hook-module-is","reference/hooks.html#discovery-and-loading","reference/hooks.html#path-resolution","reference/hooks.html#event-surfaces","reference/hooks.html#session-events","reference/hooks.html#agentcontext-events","reference/hooks.html#tool-events-prepost-model","reference/hooks.html#execution-model-and-mutation-semantics","reference/hooks.html#1-pre-execution-tool_call","reference/hooks.html#2-tool-execution","reference/hooks.html#3-post-execution-tool_result","reference/hooks.html#what-hooks-can-mutate","reference/hooks.html#what-hooks-cannot-mutate-in-this-implementation","reference/hooks.html#ordering-and-conflict-behavior","reference/hooks.html#discovery-level-ordering","reference/hooks.html#load-order","reference/hooks.html#runtime-handler-order","reference/hooks.html#ui-interactions-hookcontextui","reference/hooks.html#status-line-behavior","reference/hooks.html#error-propagation-and-fallback","reference/hooks.html#load-time","reference/hooks.html#event-time","reference/hooks.html#realistic-api-examples","reference/hooks.html#block-unsafe-bash-commands","reference/hooks.html#redact-tool-output-on-post-execution","reference/hooks.html#modify-model-context-per-llm-call","reference/hooks.html#register-slash-command-with-command-safe-context-methods","reference/hooks.html#export-surface","reference/skills.html#skills","reference/skills.html#what-a-skill-is-in-this-codebase","reference/skills.html#required-layout-and-skillmd-expectations","reference/skills.html#directory-layout","reference/skills.html#skillmd-frontmatter","reference/skills.html#discovery-pipeline","reference/skills.html#foreign-providers-are-import-only","reference/skills.html#filtering","reference/skills.html#collision-and-duplicate-handling","reference/skills.html#runtime-usage-behavior","reference/skills.html#system-prompt-exposure","reference/skills.html#interactive-skillname-commands","reference/skills.html#skill-url-behavior","reference/skills.html#skills-vs-agentsmd-commands-tools-hooks","reference/skills.html#skills-vs-agentsmd","reference/skills.html#skills-vs-slash-commands","reference/skills.html#skills-vs-custom-tools","reference/skills.html#skills-vs-hooks","reference/skills.html#practical-authoring-guidance-tied-to-discovery-logic","reference/tree-command.html#tree-command-reference","reference/tree-command.html#what-tree-does","reference/tree-command.html#how-to-open-it","reference/tree-command.html#tree-ui-model","reference/tree-command.html#keybindings-inside-tree-selector","reference/tree-command.html#filters-and-search-semantics","reference/tree-command.html#default","reference/tree-command.html#no-tools","reference/tree-command.html#user-only","reference/tree-command.html#labeled-only","reference/tree-command.html#all","reference/tree-command.html#tool-only-assistant-node-behavior","reference/tree-command.html#search-behavior","reference/tree-command.html#selection-outcomes-important","reference/tree-command.html#selecting-user-message","reference/tree-command.html#selecting-custom_message","reference/tree-command.html#selecting-non-user-node-assistanttoolsummarycompactioncustom-bookkeepingetc","reference/tree-command.html#selecting-current-leaf","reference/tree-command.html#summary-on-switch-flow","reference/tree-command.html#labels","reference/tree-command.html#tree-vs-adjacent-operations","reference/tree-command.html#operator-workflows","reference/tree-command.html#re-run-from-an-earlier-user-prompt-without-losing-current-branch","reference/tree-command.html#leave-current-branch-with-context-breadcrumb","reference/tree-command.html#investigate-hidden-bookkeeping-entries","reference/tree-command.html#bookmark-pivot-points-for-later-jumps","reference/rpc.html#rpc-protocol-reference","reference/rpc.html#startup","reference/rpc.html#transport-and-framing","reference/rpc.html#outbound-frame-categories-stdout","reference/rpc.html#inbound-frame-categories-stdin","reference/rpc.html#requestresponse-correlation","reference/rpc.html#command-schema-canonical","reference/rpc.html#prompting","reference/rpc.html#state","reference/rpc.html#model","reference/rpc.html#thinking","reference/rpc.html#queue-modes","reference/rpc.html#compaction","reference/rpc.html#retry","reference/rpc.html#bash","reference/rpc.html#session","reference/rpc.html#messages","reference/rpc.html#login","reference/rpc.html#response-schema","reference/rpc.html#prompt-payload","reference/rpc.html#get_state-payload","reference/rpc.html#set_todos-payload","reference/rpc.html#set_host_tools-payload","reference/rpc.html#set_host_uri_schemes-payload","reference/rpc.html#event-stream-schema","reference/rpc.html#promptqueue-concurrency-and-ordering","reference/rpc.html#immediate-ack-vs-completion","reference/rpc.html#while-streaming","reference/rpc.html#queue-defaults","reference/rpc.html#mode-semantics","reference/rpc.html#extension-ui-sub-protocol","reference/rpc.html#outbound-request","reference/rpc.html#inbound-response","reference/rpc.html#host-tool-sub-protocol","reference/rpc.html#outbound-request-1","reference/rpc.html#inbound-updates-and-completion","reference/rpc.html#host-uri-sub-protocol","reference/rpc.html#outbound-request-2","reference/rpc.html#inbound-result","reference/rpc.html#constraints","reference/rpc.html#error-model-and-recoverability","reference/rpc.html#command-level-failures","reference/rpc.html#recoverability-expectations","reference/rpc.html#compact-command-flows","reference/rpc.html#1-prompt-and-stream","reference/rpc.html#2-prompt-during-streaming-with-explicit-queue-policy","reference/rpc.html#3-inspect-and-tune-queue-behavior","reference/rpc.html#4-extension-ui-round-trip","reference/rpc.html#notes-on-rpcclient-helper","reference/sdk.html#sdk","reference/sdk.html#installation","reference/sdk.html#entry-points","reference/sdk.html#quick-start-auto-discovery-defaults","reference/sdk.html#what-createagentsession-discovers-by-default","reference/sdk.html#required-vs-optional-inputs","reference/sdk.html#session-manager-behavior-persistent-vs-in-memory","reference/sdk.html#file-backed-default","reference/sdk.html#in-memory","reference/sdk.html#resumeopenlist-helpers","reference/sdk.html#model-and-auth-wiring","reference/sdk.html#explicit-wiring","reference/sdk.html#selection-order-when-model-is-omitted","reference/sdk.html#auth-priority","reference/sdk.html#event-subscription-model","reference/sdk.html#prompt-lifecycle","reference/sdk.html#tools-and-extension-integration","reference/sdk.html#built-ins-and-filtering","reference/sdk.html#tool-names-have-one-owner","reference/sdk.html#extensions","reference/sdk.html#runtime-tool-set-changes","reference/sdk.html#discovery-helpers","reference/sdk.html#subagent-oriented-options","reference/sdk.html#createagentsession-return-value","reference/sdk.html#startup-performance","reference/sdk.html#minimal-controlled-embed-example","reference/exit-codes.html#exit-codes","reference/file-locations.html#file-locations","reference/file-locations.html#the-config-home-veyyon","reference/file-locations.html#profiles-veyyonprofilesname","reference/file-locations.html#which-profile-launches","reference/file-locations.html#legacy-layout-migration","reference/file-locations.html#credential-storage","reference/file-locations.html#project-local-files","architecture/overview.html#architecture-overview","architecture/overview.html#the-request-path","architecture/overview.html#subsystem-map","architecture/sandbox.html#approvals","architecture/sandbox.html#responsibility","architecture/sandbox.html#public-boundary","architecture/sandbox.html#key-concepts","architecture/session-turn.html#session-and-turn","architecture/session-turn.html#responsibility","architecture/session-turn.html#public-boundary","architecture/config.html#config","architecture/config.html#responsibility","architecture/config.html#public-boundary","architecture/config.html#how-configuration-resolves","architecture/config.html#scope","architecture/config.html#resolution-flow-visual","architecture/config.html#1-config-roots-and-source-order","architecture/config.html#canonical-roots","architecture/config.html#profiles","architecture/config.html#important-constraint","architecture/config.html#2-core-discovery-helpers-srcconfigts","architecture/config.html#getconfigdirssubpath-options","architecture/config.html#findconfigfilesubpath-options--findconfigfilewithmeta","architecture/config.html#findallnearestprojectconfigdirssubpath-cwd","architecture/config.html#3-file-config-wrapper-configfilet-in-srcconfigconfig-filets-re-exported-from-srcconfigts","architecture/config.html#4-settings-resolution-model-srcconfigsettingsts","architecture/config.html#migration-behavior-still-active","architecture/config.html#5-capabilitydiscovery-integration","architecture/config.html#provider-ordering","architecture/config.html#dedup-semantics","architecture/config.html#6-native-veyyon-provider-behavior-packagescoding-agentsrcdiscoverybuiltints","architecture/config.html#directory-admission-rules","architecture/config.html#scope-specific-loading","architecture/config.html#project-context-file-walk","architecture/config.html#7-how-major-subsystems-consume-config","architecture/config.html#settings-subsystem","architecture/config.html#session-title-prompt-override","architecture/config.html#skills-subsystem","architecture/config.html#hooks-subsystem","architecture/config.html#tools-subsystem","architecture/config.html#extensions-subsystem","architecture/config.html#8-precedence-rules-to-rely-on","architecture/config.html#settings-specific-caveat","architecture/config.html#9-legacycompatibility-behaviors-still-present","architecture/mcp.html#mcp","architecture/mcp.html#responsibility","architecture/mcp.html#implementation-typescript","architecture/providers.html#providers","architecture/providers.html#responsibility","architecture/providers.html#implementation","architecture/providers.html#key-concepts","architecture/providers.html#prompt-caching","architecture/providers.html#the-first-event-budget","architecture/providers.html#where-each-providers-deadline-sits","architecture/secrets.html#secrets-internals","architecture/secrets.html#enabling","architecture/secrets.html#how-it-works","architecture/secrets.html#spending-a-secret-prompts-first","architecture/secrets.html#what-the-session-file-records-about-the-call","architecture/secrets.html#the-8-character-minimum","architecture/secrets.html#per-entry-validation-is-a-refusal-not-a-skip","architecture/secrets.html#the-vault-secret","architecture/secrets.html#two-grammars-selected-by-surface","architecture/secrets.html#discard-the-repair-for-a-vault-that-cannot-be-read","architecture/secrets.html#masked-entry","architecture/secrets.html#named-and-unnamed-placeholders","architecture/secrets.html#completing-secret","architecture/secrets.html#what-the-model-is-told-about-a-stored-secret","architecture/secrets.html#storage","architecture/secrets.html#lifetimes","architecture/secrets.html#the-expansion-log","architecture/secrets.html#operator-notices","architecture/secrets.html#env-keyword-list","architecture/secrets.html#secretsyml","architecture/secrets.html#schema","architecture/secrets.html#examples","architecture/secrets.html#interaction-with-env-var-detection","architecture/secrets.html#key-files","architecture/secrets.html#see-also","architecture/memory.html#autonomous-memory","architecture/memory.html#backends","architecture/memory.html#usage","architecture/memory.html#what-gets-injected","architecture/memory.html#reading-memory-artifacts","architecture/memory.html#memory-slash-command","architecture/memory.html#how-it-works","architecture/memory.html#extraction-behavior","architecture/memory.html#model-selection","architecture/memory.html#configuration","architecture/memory.html#key-files","architecture/compaction.html#compaction-and-branch-summaries","architecture/compaction.html#key-implementation-files","architecture/compaction.html#session-entry-model","architecture/compaction.html#compaction-pipeline","architecture/compaction.html#triggers","architecture/compaction.html#compaction-shape-visual","architecture/compaction.html#overflowincomplete-recovery-vs-thresholdidle-maintenance","architecture/compaction.html#compaction-and-manual-handoff","architecture/compaction.html#legacy-compaction-strategies","architecture/compaction.html#display-transcript","architecture/compaction.html#per-turn-and-pre-compaction-pruning","architecture/compaction.html#superseded-read-elision","architecture/compaction.html#useless-result-elision","architecture/compaction.html#what-the-summary-prompts-request","architecture/compaction.html#empty-responses","architecture/compaction.html#boundary-and-cut-point-logic","architecture/compaction.html#split-turn-handling","architecture/compaction.html#summary-generation","architecture/compaction.html#short-summary","architecture/compaction.html#handoff-generation","architecture/compaction.html#file-operation-context-in-summaries","architecture/compaction.html#persist-and-reload","architecture/compaction.html#branch-summarization-pipeline","architecture/compaction.html#trigger","architecture/compaction.html#branch-switch-shape-visual","architecture/compaction.html#preparation-and-token-budget","architecture/compaction.html#summary-generation-and-persistence","architecture/compaction.html#extension-and-hook-touchpoints","architecture/compaction.html#session_before_compact","architecture/compaction.html#session_compacting","architecture/compaction.html#session_compact","architecture/compaction.html#session_before_tree","architecture/compaction.html#session_tree","architecture/compaction.html#which-model-compacts","architecture/compaction.html#runtime-behavior-and-failure-semantics","architecture/compaction.html#settings-and-defaults","architecture/tui.html#tui-integration-for-extensions-and-custom-tools","architecture/tui.html#what-this-subsystem-is","architecture/tui.html#runtime-behavior-by-mode","architecture/tui.html#core-component-contract-veyyontui","architecture/tui.html#rendering-constraints-terminal-safety","architecture/tui.html#input-handling-and-keybindings","architecture/tui.html#raw-key-matching","architecture/tui.html#match-app-keybinding-actions","architecture/tui.html#key-releaserepeat-events","architecture/tui.html#focus-overlays-and-cursor","architecture/tui.html#mount-points-and-return-contracts","architecture/tui.html#1-extension-ui-extensionuicontext","architecture/tui.html#2-hookcustom-tool-ui-context-legacy-typing","architecture/tui.html#3-custom-tool-callresult-renderers","architecture/tui.html#lifecycle-and-cancellation","architecture/tui.html#realistic-custom-component-example-extension-command","architecture/tui.html#key-implementation-files","foundations/verification.html#testing-and-verification","foundations/verification.html#examples-of-what-tests-check","foundations/verification.html#recording-terminal-proofs","foundations/verification.html#which-artifact-proves-which-change","foundations/verification.html#real-interactive-sessions","foundations/verification.html#where-the-settings-live-and-where-the-chrome-is-drawn","foundations/verification.html#settings-differentials","foundations/verification.html#before-and-after-pairs-for-a-ui-change","foundations/verification.html#zooming-into-a-detail","foundations/verification.html#off-screen-component-renders-are-a-debugging-aid-not-a-proof","foundations/verification.html#related","repair/overview.html#tool-call-repair","repair/overview.html#behavior","repair/overview.html#related","repair/cascade.html#the-repair-cascade","repair/cascade.html#related","repair/per-model.html#per-model-repair-posture","repair/soundness.html#observability-for-repair-and-sessions","repair/soundness.html#usage-and-sessions","repair/soundness.html#opentelemetry","repair/soundness.html#related","edit/engine.html#the-hashline-edit-engine","edit/engine.html#how-a-hashline-edit-works","edit/engine.html#invariants","edit/engine.html#further-reading","edit/edit-repair.html#repair-on-edits","edit/roadmap.html#edit-path-properties","edit/roadmap.html#bom-and-line-endings","edit/roadmap.html#multi-edit-in-one-call","edit/roadmap.html#hashline","edit/roadmap.html#concurrency","edit/roadmap.html#trailing-newlines","models/providers.html#the-provider-stack-and-bring-your-own-key","models/providers.html#credentials","models/providers.html#custom-providers","models/providers.html#local-engines","models/prompts.html#execution-order-prompts","models/prompts.html#delivery","models/prompts.html#seeing-every-prompt","models/prompts.html#going-deeper","models/system-prompt.html#system-prompt-customization","models/system-prompt.html#where-prompts-live","models/system-prompt.html#1-inputs","models/system-prompt.html#2-replace-vs-append","models/system-prompt.html#3-templating-contract","models/system-prompt.html#4-recommended-patterns","models/system-prompt.html#tweak-the-default-keep-default-add-a-few-rules","models/system-prompt.html#replace-the-stable-default-instructions-bring-your-own-base-prompt","models/system-prompt.html#customize-while-keeping-the-tool-inventory-and-default-workflow-guidance","models/system-prompt.html#customize-automatic-session-titles","models/system-prompt.html#replace-everything-including-project-context-sdk-only","models/system-prompt.html#change-one-section-of-the-default-instructions-keep-the-rest","models/system-prompt.html#5-deduplication","models/system-prompt.html#6-discovery-paths","models/system-prompt.html#7-quick-reference","models/system-prompt.html#8-changing-one-section-prompt_sections","models/system-prompt.html#9-seeing-the-prompt-veyyon-prompt","models/system-prompt.html#the-other-prompts","models/system-prompt.html#10-adding-a-section-contributors","models/system-prompt.html#11-adding-a-prompt-contributors","models/system-prompt.html#12-settings-that-change-the-prompt","models/system-prompt.html#the-rule-policy-is-a-setting-not-a-sentence","models/system-prompt.html#the-gates","models/system-prompt.html#live-and-frozen-gates","models/system-prompt.html#what-a-gate-is-worth-when-nobody-says","models/system-prompt.html#adding-a-gate","models/system-prompt.html#13-statements-the-prompt-is-a-list-not-a-document","models/system-prompt.html#why","models/system-prompt.html#how-fine-is-a-statement","models/system-prompt.html#conditions","models/system-prompt.html#what-stays-in-handlebars","models/system-prompt.html#the-registry-owns-section-structure","models/system-prompt.html#how-statements-reach-the-model","models/system-prompt.html#structural-invariants","models/system-prompt.html#what-each-rule-costs-and-testing-one-of-them","context/reads-search.html#bounded-reads-and-search","context/reads-search.html#the-read-tool-toolsreadts","context/reads-search.html#the-glob-tool-toolsglobts","context/reads-search.html#the-grep-tool-toolsgrepts","context/reads-search.html#the-write-tool-toolswritets","context/reads-search.html#sanitizing-exec-output-for-the-model","context/reads-search.html#why-these-are-grouped-with-context","context/context-files.html#context-files","context/context-files.html#how-context-files-relate-to-other-concepts","context/context-files.html#native-veyyon-files","context/context-files.html#monorepo-example","context/context-files.html#other-supported-context-conventions","context/context-files.html#load-order-and-shadowing","context/context-files.html#scope-authority-your-own-configuration-is-last-and-wins","context/context-files.html#worked-shadowing-example","context/context-files.html#injection-behavior","context/context-files.html#-imports","context/context-files.html#sticky-rules-vs-normal-context","context/context-files.html#disabling-discovery-providers","context/context-files.html#troubleshooting","context/context-files.html#a-file-is-not-loaded","context/context-files.html#the-wrong-file-wins","context/context-files.html#user-context-disappeared","context/context-files.html#a-rulesmd-file-is-ignored","context/context-files.html#an--import-did-not-expand","context/goal-state.html#goal-state-and-long-sessions","context/goal-state.html#goal-card-session-backed","context/goal-state.html#status-indicator","context/goal-state.html#context-assembly","context/goal-state.html#what-goal-mode-provides","context/compaction-memory.html#compaction-and-project-memory","context/compaction-memory.html#context-compaction","context/compaction-memory.html#fallback-models","context/compaction-memory.html#shake-and-duplicate-elision","context/compaction-memory.html#memory-backends","context/compaction-memory.html#goals","router/role-routing.html#role-policy","router/role-routing.html#what-exists-today","router/role-routing.html#no-fixed-role-pipeline","observability/overview.html#observability","observability/overview.html#interactive-and-cli-usage","observability/overview.html#opentelemetry","observability/overview.html#session-debugging","observability/overview.html#recording-raw-provider-traffic","using/troubleshooting.html#troubleshooting","using/troubleshooting.html#install-or-startup","using/troubleshooting.html#provider-errors","using/troubleshooting.html#command-or-edit-blocked-or-prompting","using/troubleshooting.html#truncated-tool-output","using/troubleshooting.html#related","using/faq.html#frequently-asked-questions","using/faq.html#setup","using/faq.html#veyyon-plugin-doctor-fails-what-do-i-fix","using/faq.html#does-veyyon-sandbox-the-commands-it-runs","using/faq.html#database-and-session-locking","using/faq.html#model-authentication","using/faq.html#invalid-api-key-or-authentication-failed","using/faq.html#unsupported-region-or-endpoint-errors","using/faq.html#why-is-my-model-not-listed","using/faq.html#workflow","using/faq.html#why-did-my-edit-ask-for-approval","using/faq.html#how-do-i-resume-a-session","using/faq.html#what-happened-to-my-queued-follow-up","using/faq.html#why-does-my-output-look-truncated","using/faq.html#where-to-go-next","features/doctor.html#diagnostics-and-health","features/doctor.html#system-health","features/doctor.html#plugin-doctor","features/doctor.html#tui-debug","features/doctor.html#memory-diagnostics","features/doctor.html#which-one-to-reach-for","features/doctor.html#exit-status","features/doctor.html#see-also","acknowledgements.html#acknowledgements","appendix/glossary.html#glossary"],"index":{"documentStore":{"docInfo":{"0":{"body":26,"breadcrumbs":4,"title":2},"1":{"body":45,"breadcrumbs":3,"title":1},"10":{"body":36,"breadcrumbs":5,"title":2},"100":{"body":73,"breadcrumbs":7,"title":2},"1000":{"body":42,"breadcrumbs":5,"title":3},"1001":{"body":34,"breadcrumbs":3,"title":1},"1002":{"body":37,"breadcrumbs":5,"title":3},"1003":{"body":0,"breadcrumbs":5,"title":3},"1004":{"body":58,"breadcrumbs":5,"title":3},"1005":{"body":94,"breadcrumbs":5,"title":3},"1006":{"body":76,"breadcrumbs":5,"title":3},"1007":{"body":0,"breadcrumbs":8,"title":6},"1008":{"body":48,"breadcrumbs":5,"title":3},"1009":{"body":22,"breadcrumbs":6,"title":4},"101":{"body":49,"breadcrumbs":6,"title":1},"1010":{"body":19,"breadcrumbs":6,"title":4},"1011":{"body":12,"breadcrumbs":5,"title":3},"1012":{"body":45,"breadcrumbs":8,"title":6},"1013":{"body":22,"breadcrumbs":6,"title":3},"1014":{"body":76,"breadcrumbs":4,"title":1},"1015":{"body":26,"breadcrumbs":4,"title":1},"1016":{"body":79,"breadcrumbs":6,"title":3},"1017":{"body":51,"breadcrumbs":7,"title":4},"1018":{"body":7,"breadcrumbs":6,"title":3},"1019":{"body":26,"breadcrumbs":4,"title":1},"102":{"body":50,"breadcrumbs":6,"title":1},"1020":{"body":6,"breadcrumbs":4,"title":1},"1021":{"body":4,"breadcrumbs":4,"title":1},"1022":{"body":4,"breadcrumbs":4,"title":1},"1023":{"body":6,"breadcrumbs":3,"title":0},"1024":{"body":21,"breadcrumbs":7,"title":4},"1025":{"body":31,"breadcrumbs":5,"title":2},"1026":{"body":8,"breadcrumbs":6,"title":3},"1027":{"body":21,"breadcrumbs":6,"title":3},"1028":{"body":11,"breadcrumbs":5,"title":2},"1029":{"body":8,"breadcrumbs":9,"title":6},"103":{"body":50,"breadcrumbs":8,"title":3},"1030":{"body":32,"breadcrumbs":6,"title":3},"1031":{"body":103,"breadcrumbs":6,"title":3},"1032":{"body":31,"breadcrumbs":4,"title":1},"1033":{"body":70,"breadcrumbs":7,"title":4},"1034":{"body":0,"breadcrumbs":5,"title":2},"1035":{"body":24,"breadcrumbs":12,"title":9},"1036":{"body":18,"breadcrumbs":8,"title":5},"1037":{"body":15,"breadcrumbs":7,"title":4},"1038":{"body":18,"breadcrumbs":8,"title":5},"1039":{"body":45,"breadcrumbs":6,"title":3},"104":{"body":60,"breadcrumbs":8,"title":3},"1040":{"body":106,"breadcrumbs":4,"title":1},"1041":{"body":12,"breadcrumbs":5,"title":2},"1042":{"body":73,"breadcrumbs":7,"title":4},"1043":{"body":13,"breadcrumbs":7,"title":4},"1044":{"body":99,"breadcrumbs":5,"title":2},"1045":{"body":4,"breadcrumbs":6,"title":3},"1046":{"body":39,"breadcrumbs":4,"title":1},"1047":{"body":39,"breadcrumbs":4,"title":1},"1048":{"body":13,"breadcrumbs":4,"title":1},"1049":{"body":8,"breadcrumbs":4,"title":1},"105":{"body":66,"breadcrumbs":6,"title":1},"1050":{"body":18,"breadcrumbs":5,"title":2},"1051":{"body":10,"breadcrumbs":4,"title":1},"1052":{"body":8,"breadcrumbs":4,"title":1},"1053":{"body":49,"breadcrumbs":4,"title":1},"1054":{"body":34,"breadcrumbs":4,"title":1},"1055":{"body":3,"breadcrumbs":4,"title":1},"1056":{"body":8,"breadcrumbs":4,"title":1},"1057":{"body":30,"breadcrumbs":5,"title":2},"1058":{"body":90,"breadcrumbs":5,"title":2},"1059":{"body":58,"breadcrumbs":5,"title":2},"106":{"body":40,"breadcrumbs":7,"title":2},"1060":{"body":49,"breadcrumbs":5,"title":2},"1061":{"body":60,"breadcrumbs":5,"title":2},"1062":{"body":53,"breadcrumbs":5,"title":2},"1063":{"body":43,"breadcrumbs":6,"title":3},"1064":{"body":3,"breadcrumbs":6,"title":3},"1065":{"body":32,"breadcrumbs":7,"title":4},"1066":{"body":25,"breadcrumbs":4,"title":1},"1067":{"body":10,"breadcrumbs":5,"title":2},"1068":{"body":36,"breadcrumbs":5,"title":2},"1069":{"body":7,"breadcrumbs":7,"title":4},"107":{"body":41,"breadcrumbs":7,"title":2},"1070":{"body":57,"breadcrumbs":5,"title":2},"1071":{"body":32,"breadcrumbs":5,"title":2},"1072":{"body":14,"breadcrumbs":7,"title":4},"1073":{"body":33,"breadcrumbs":5,"title":2},"1074":{"body":43,"breadcrumbs":6,"title":3},"1075":{"body":20,"breadcrumbs":7,"title":4},"1076":{"body":45,"breadcrumbs":5,"title":2},"1077":{"body":47,"breadcrumbs":5,"title":2},"1078":{"body":35,"breadcrumbs":4,"title":1},"1079":{"body":0,"breadcrumbs":6,"title":3},"108":{"body":50,"breadcrumbs":6,"title":2},"1080":{"body":17,"breadcrumbs":6,"title":3},"1081":{"body":42,"breadcrumbs":5,"title":2},"1082":{"body":0,"breadcrumbs":6,"title":3},"1083":{"body":34,"breadcrumbs":6,"title":3},"1084":{"body":10,"breadcrumbs":10,"title":7},"1085":{"body":16,"breadcrumbs":8,"title":5},"1086":{"body":20,"breadcrumbs":8,"title":5},"1087":{"body":59,"breadcrumbs":6,"title":3},"1088":{"body":28,"breadcrumbs":3,"title":1},"1089":{"body":87,"breadcrumbs":3,"title":1},"109":{"body":45,"breadcrumbs":6,"title":2},"1090":{"body":35,"breadcrumbs":4,"title":2},"1091":{"body":27,"breadcrumbs":7,"title":5},"1092":{"body":88,"breadcrumbs":5,"title":3},"1093":{"body":101,"breadcrumbs":6,"title":4},"1094":{"body":8,"breadcrumbs":8,"title":6},"1095":{"body":27,"breadcrumbs":5,"title":3},"1096":{"body":34,"breadcrumbs":3,"title":1},"1097":{"body":18,"breadcrumbs":4,"title":2},"1098":{"body":9,"breadcrumbs":5,"title":3},"1099":{"body":40,"breadcrumbs":4,"title":2},"11":{"body":71,"breadcrumbs":6,"title":3},"110":{"body":99,"breadcrumbs":5,"title":1},"1100":{"body":26,"breadcrumbs":6,"title":4},"1101":{"body":47,"breadcrumbs":4,"title":2},"1102":{"body":41,"breadcrumbs":5,"title":3},"1103":{"body":63,"breadcrumbs":4,"title":2},"1104":{"body":0,"breadcrumbs":5,"title":3},"1105":{"body":35,"breadcrumbs":5,"title":3},"1106":{"body":121,"breadcrumbs":6,"title":4},"1107":{"body":19,"breadcrumbs":3,"title":1},"1108":{"body":16,"breadcrumbs":6,"title":4},"1109":{"body":23,"breadcrumbs":4,"title":2},"111":{"body":186,"breadcrumbs":7,"title":3},"1110":{"body":39,"breadcrumbs":5,"title":3},"1111":{"body":39,"breadcrumbs":5,"title":3},"1112":{"body":208,"breadcrumbs":4,"title":2},"1113":{"body":87,"breadcrumbs":6,"title":4},"1114":{"body":279,"breadcrumbs":5,"title":2},"1115":{"body":22,"breadcrumbs":5,"title":2},"1116":{"body":88,"breadcrumbs":6,"title":3},"1117":{"body":220,"breadcrumbs":5,"title":2},"1118":{"body":35,"breadcrumbs":5,"title":2},"1119":{"body":26,"breadcrumbs":6,"title":3},"112":{"body":119,"breadcrumbs":6,"title":2},"1120":{"body":20,"breadcrumbs":5,"title":2},"1121":{"body":55,"breadcrumbs":6,"title":3},"1122":{"body":28,"breadcrumbs":4,"title":2},"1123":{"body":32,"breadcrumbs":4,"title":2},"1124":{"body":57,"breadcrumbs":4,"title":2},"1125":{"body":27,"breadcrumbs":5,"title":1},"1126":{"body":111,"breadcrumbs":5,"title":1},"1127":{"body":33,"breadcrumbs":6,"title":2},"1128":{"body":74,"breadcrumbs":6,"title":2},"1129":{"body":17,"breadcrumbs":7,"title":2},"113":{"body":76,"breadcrumbs":7,"title":3},"1130":{"body":31,"breadcrumbs":6,"title":1},"1131":{"body":20,"breadcrumbs":7,"title":2},"1132":{"body":35,"breadcrumbs":5,"title":1},"1133":{"body":46,"breadcrumbs":5,"title":1},"1134":{"body":19,"breadcrumbs":6,"title":2},"1135":{"body":9,"breadcrumbs":6,"title":2},"1136":{"body":35,"breadcrumbs":5,"title":1},"1137":{"body":50,"breadcrumbs":7,"title":3},"1138":{"body":0,"breadcrumbs":9,"title":5},"1139":{"body":61,"breadcrumbs":6,"title":2},"114":{"body":318,"breadcrumbs":7,"title":3},"1140":{"body":128,"breadcrumbs":5,"title":1},"1141":{"body":8,"breadcrumbs":6,"title":2},"1142":{"body":0,"breadcrumbs":9,"title":5},"1143":{"body":39,"breadcrumbs":6,"title":2},"1144":{"body":11,"breadcrumbs":7,"title":3},"1145":{"body":38,"breadcrumbs":6,"title":2},"1146":{"body":52,"breadcrumbs":14,"title":10},"1147":{"body":69,"breadcrumbs":9,"title":5},"1148":{"body":59,"breadcrumbs":8,"title":4},"1149":{"body":10,"breadcrumbs":7,"title":3},"115":{"body":17,"breadcrumbs":5,"title":1},"1150":{"body":39,"breadcrumbs":6,"title":2},"1151":{"body":34,"breadcrumbs":6,"title":2},"1152":{"body":56,"breadcrumbs":11,"title":7},"1153":{"body":84,"breadcrumbs":7,"title":3},"1154":{"body":50,"breadcrumbs":7,"title":3},"1155":{"body":30,"breadcrumbs":8,"title":4},"1156":{"body":0,"breadcrumbs":9,"title":5},"1157":{"body":16,"breadcrumbs":6,"title":2},"1158":{"body":100,"breadcrumbs":8,"title":4},"1159":{"body":39,"breadcrumbs":6,"title":2},"116":{"body":3,"breadcrumbs":5,"title":1},"1160":{"body":14,"breadcrumbs":6,"title":2},"1161":{"body":22,"breadcrumbs":6,"title":2},"1162":{"body":20,"breadcrumbs":6,"title":2},"1163":{"body":38,"breadcrumbs":8,"title":4},"1164":{"body":25,"breadcrumbs":7,"title":3},"1165":{"body":77,"breadcrumbs":9,"title":5},"1166":{"body":22,"breadcrumbs":5,"title":1},"1167":{"body":32,"breadcrumbs":5,"title":1},"1168":{"body":120,"breadcrumbs":6,"title":2},"1169":{"body":11,"breadcrumbs":5,"title":1},"117":{"body":50,"breadcrumbs":6,"title":2},"1170":{"body":28,"breadcrumbs":5,"title":1},"1171":{"body":44,"breadcrumbs":5,"title":1},"1172":{"body":37,"breadcrumbs":6,"title":2},"1173":{"body":53,"breadcrumbs":6,"title":2},"1174":{"body":395,"breadcrumbs":7,"title":3},"1175":{"body":160,"breadcrumbs":8,"title":4},"1176":{"body":51,"breadcrumbs":6,"title":2},"1177":{"body":34,"breadcrumbs":5,"title":1},"1178":{"body":264,"breadcrumbs":5,"title":1},"1179":{"body":95,"breadcrumbs":8,"title":4},"118":{"body":57,"breadcrumbs":7,"title":3},"1180":{"body":139,"breadcrumbs":8,"title":4},"1181":{"body":106,"breadcrumbs":7,"title":3},"1182":{"body":80,"breadcrumbs":9,"title":5},"1183":{"body":17,"breadcrumbs":6,"title":2},"1184":{"body":987,"breadcrumbs":8,"title":4},"1185":{"body":220,"breadcrumbs":8,"title":4},"1186":{"body":88,"breadcrumbs":6,"title":2},"1187":{"body":80,"breadcrumbs":7,"title":3},"1188":{"body":87,"breadcrumbs":6,"title":2},"1189":{"body":287,"breadcrumbs":8,"title":4},"119":{"body":34,"breadcrumbs":6,"title":2},"1190":{"body":307,"breadcrumbs":5,"title":1},"1191":{"body":228,"breadcrumbs":5,"title":1},"1192":{"body":338,"breadcrumbs":6,"title":2},"1193":{"body":96,"breadcrumbs":6,"title":2},"1194":{"body":168,"breadcrumbs":7,"title":3},"1195":{"body":53,"breadcrumbs":5,"title":1},"1196":{"body":51,"breadcrumbs":5,"title":1},"1197":{"body":212,"breadcrumbs":5,"title":1},"1198":{"body":32,"breadcrumbs":8,"title":4},"1199":{"body":146,"breadcrumbs":6,"title":2},"12":{"body":61,"breadcrumbs":6,"title":3},"120":{"body":67,"breadcrumbs":6,"title":2},"1200":{"body":21,"breadcrumbs":5,"title":1},"1201":{"body":34,"breadcrumbs":6,"title":2},"1202":{"body":53,"breadcrumbs":5,"title":1},"1203":{"body":0,"breadcrumbs":5,"title":1},"1204":{"body":154,"breadcrumbs":6,"title":2},"1205":{"body":27,"breadcrumbs":7,"title":3},"1206":{"body":90,"breadcrumbs":7,"title":3},"1207":{"body":134,"breadcrumbs":5,"title":1},"1208":{"body":54,"breadcrumbs":6,"title":2},"1209":{"body":46,"breadcrumbs":6,"title":2},"121":{"body":64,"breadcrumbs":6,"title":2},"1210":{"body":55,"breadcrumbs":5,"title":1},"1211":{"body":37,"breadcrumbs":6,"title":2},"1212":{"body":42,"breadcrumbs":7,"title":3},"1213":{"body":37,"breadcrumbs":7,"title":3},"1214":{"body":181,"breadcrumbs":7,"title":3},"1215":{"body":0,"breadcrumbs":6,"title":2},"1216":{"body":70,"breadcrumbs":5,"title":1},"1217":{"body":76,"breadcrumbs":7,"title":3},"1218":{"body":201,"breadcrumbs":9,"title":5},"1219":{"body":149,"breadcrumbs":7,"title":3},"122":{"body":100,"breadcrumbs":8,"title":4},"1220":{"body":85,"breadcrumbs":7,"title":3},"1221":{"body":73,"breadcrumbs":6,"title":2},"1222":{"body":155,"breadcrumbs":9,"title":5},"1223":{"body":91,"breadcrumbs":7,"title":3},"1224":{"body":146,"breadcrumbs":7,"title":3},"1225":{"body":165,"breadcrumbs":7,"title":3},"1226":{"body":33,"breadcrumbs":6,"title":2},"1227":{"body":72,"breadcrumbs":8,"title":4},"1228":{"body":50,"breadcrumbs":7,"title":3},"1229":{"body":65,"breadcrumbs":6,"title":2},"123":{"body":94,"breadcrumbs":8,"title":4},"1230":{"body":360,"breadcrumbs":6,"title":2},"1231":{"body":65,"breadcrumbs":6,"title":2},"1232":{"body":124,"breadcrumbs":8,"title":4},"1233":{"body":39,"breadcrumbs":6,"title":2},"1234":{"body":7,"breadcrumbs":7,"title":3},"1235":{"body":34,"breadcrumbs":5,"title":1},"1236":{"body":31,"breadcrumbs":8,"title":4},"1237":{"body":54,"breadcrumbs":7,"title":3},"1238":{"body":34,"breadcrumbs":7,"title":3},"1239":{"body":0,"breadcrumbs":7,"title":3},"124":{"body":67,"breadcrumbs":9,"title":5},"1240":{"body":14,"breadcrumbs":5,"title":1},"1241":{"body":22,"breadcrumbs":5,"title":1},"1242":{"body":7,"breadcrumbs":5,"title":1},"1243":{"body":19,"breadcrumbs":5,"title":1},"1244":{"body":9,"breadcrumbs":5,"title":1},"1245":{"body":75,"breadcrumbs":6,"title":2},"1246":{"body":86,"breadcrumbs":8,"title":4},"1247":{"body":210,"breadcrumbs":6,"title":2},"1248":{"body":14,"breadcrumbs":9,"title":5},"1249":{"body":28,"breadcrumbs":5,"title":1},"125":{"body":60,"breadcrumbs":8,"title":4},"1250":{"body":46,"breadcrumbs":7,"title":3},"1251":{"body":72,"breadcrumbs":8,"title":4},"1252":{"body":63,"breadcrumbs":8,"title":4},"1253":{"body":0,"breadcrumbs":7,"title":3},"1254":{"body":5,"breadcrumbs":7,"title":3},"1255":{"body":24,"breadcrumbs":8,"title":4},"1256":{"body":13,"breadcrumbs":7,"title":3},"1257":{"body":45,"breadcrumbs":7,"title":3},"1258":{"body":0,"breadcrumbs":8,"title":4},"1259":{"body":70,"breadcrumbs":8,"title":4},"126":{"body":71,"breadcrumbs":7,"title":3},"1260":{"body":86,"breadcrumbs":11,"title":7},"1261":{"body":25,"breadcrumbs":9,"title":5},"1262":{"body":44,"breadcrumbs":6,"title":2},"1263":{"body":128,"breadcrumbs":10,"title":6},"1264":{"body":67,"breadcrumbs":7,"title":3},"1265":{"body":10,"breadcrumbs":4,"title":2},"1266":{"body":51,"breadcrumbs":5,"title":3},"1267":{"body":24,"breadcrumbs":5,"title":3},"1268":{"body":182,"breadcrumbs":5,"title":3},"1269":{"body":120,"breadcrumbs":5,"title":3},"127":{"body":64,"breadcrumbs":6,"title":2},"1270":{"body":459,"breadcrumbs":6,"title":4},"1271":{"body":68,"breadcrumbs":4,"title":2},"1272":{"body":117,"breadcrumbs":6,"title":4},"1273":{"body":190,"breadcrumbs":4,"title":2},"1274":{"body":112,"breadcrumbs":8,"title":6},"1275":{"body":5,"breadcrumbs":3,"title":1},"1276":{"body":49,"breadcrumbs":4,"title":3},"1277":{"body":74,"breadcrumbs":2,"title":1},"1278":{"body":19,"breadcrumbs":2,"title":1},"1279":{"body":133,"breadcrumbs":5,"title":2},"128":{"body":20,"breadcrumbs":6,"title":2},"1280":{"body":4,"breadcrumbs":4,"title":1},"1281":{"body":182,"breadcrumbs":8,"title":4},"1282":{"body":0,"breadcrumbs":6,"title":3},"1283":{"body":19,"breadcrumbs":5,"title":2},"1284":{"body":30,"breadcrumbs":4,"title":1},"1285":{"body":3,"breadcrumbs":4,"title":1},"1286":{"body":55,"breadcrumbs":5,"title":3},"1287":{"body":85,"breadcrumbs":5,"title":3},"1288":{"body":41,"breadcrumbs":3,"title":1},"1289":{"body":34,"breadcrumbs":4,"title":2},"129":{"body":33,"breadcrumbs":5,"title":1},"1290":{"body":69,"breadcrumbs":6,"title":2},"1291":{"body":18,"breadcrumbs":8,"title":3},"1292":{"body":36,"breadcrumbs":8,"title":3},"1293":{"body":29,"breadcrumbs":9,"title":4},"1294":{"body":34,"breadcrumbs":6,"title":1},"1295":{"body":26,"breadcrumbs":6,"title":1},"1296":{"body":20,"breadcrumbs":7,"title":2},"1297":{"body":50,"breadcrumbs":6,"title":4},"1298":{"body":42,"breadcrumbs":3,"title":1},"1299":{"body":34,"breadcrumbs":4,"title":2},"13":{"body":27,"breadcrumbs":6,"title":3},"130":{"body":12,"breadcrumbs":2,"title":1},"1300":{"body":36,"breadcrumbs":4,"title":2},"1301":{"body":31,"breadcrumbs":8,"title":3},"1302":{"body":44,"breadcrumbs":6,"title":1},"1303":{"body":349,"breadcrumbs":7,"title":2},"1304":{"body":34,"breadcrumbs":7,"title":2},"1305":{"body":241,"breadcrumbs":8,"title":3},"1306":{"body":255,"breadcrumbs":7,"title":2},"1307":{"body":171,"breadcrumbs":7,"title":2},"1308":{"body":204,"breadcrumbs":9,"title":4},"1309":{"body":136,"breadcrumbs":8,"title":3},"131":{"body":38,"breadcrumbs":3,"title":2},"1310":{"body":0,"breadcrumbs":8,"title":3},"1311":{"body":48,"breadcrumbs":12,"title":7},"1312":{"body":85,"breadcrumbs":12,"title":7},"1313":{"body":82,"breadcrumbs":12,"title":7},"1314":{"body":72,"breadcrumbs":9,"title":4},"1315":{"body":19,"breadcrumbs":11,"title":6},"1316":{"body":39,"breadcrumbs":12,"title":7},"1317":{"body":23,"breadcrumbs":7,"title":2},"1318":{"body":51,"breadcrumbs":8,"title":3},"1319":{"body":83,"breadcrumbs":8,"title":3},"132":{"body":104,"breadcrumbs":3,"title":2},"1320":{"body":286,"breadcrumbs":10,"title":5},"1321":{"body":296,"breadcrumbs":10,"title":5},"1322":{"body":114,"breadcrumbs":6,"title":1},"1323":{"body":427,"breadcrumbs":9,"title":4},"1324":{"body":259,"breadcrumbs":9,"title":4},"1325":{"body":0,"breadcrumbs":9,"title":4},"1326":{"body":151,"breadcrumbs":9,"title":4},"1327":{"body":50,"breadcrumbs":6,"title":1},"1328":{"body":193,"breadcrumbs":8,"title":3},"1329":{"body":129,"breadcrumbs":8,"title":3},"133":{"body":11,"breadcrumbs":2,"title":1},"1330":{"body":269,"breadcrumbs":7,"title":2},"1331":{"body":21,"breadcrumbs":10,"title":5},"1332":{"body":62,"breadcrumbs":5,"title":0},"1333":{"body":131,"breadcrumbs":7,"title":2},"1334":{"body":101,"breadcrumbs":6,"title":1},"1335":{"body":61,"breadcrumbs":7,"title":2},"1336":{"body":32,"breadcrumbs":9,"title":4},"1337":{"body":80,"breadcrumbs":8,"title":3},"1338":{"body":76,"breadcrumbs":7,"title":2},"1339":{"body":282,"breadcrumbs":10,"title":5},"134":{"body":45,"breadcrumbs":4,"title":2},"1340":{"body":57,"breadcrumbs":4,"title":3},"1341":{"body":229,"breadcrumbs":4,"title":3},"1342":{"body":124,"breadcrumbs":4,"title":3},"1343":{"body":137,"breadcrumbs":4,"title":3},"1344":{"body":86,"breadcrumbs":4,"title":3},"1345":{"body":124,"breadcrumbs":5,"title":4},"1346":{"body":28,"breadcrumbs":3,"title":2},"1347":{"body":41,"breadcrumbs":5,"title":2},"1348":{"body":143,"breadcrumbs":7,"title":4},"1349":{"body":224,"breadcrumbs":6,"title":3},"135":{"body":34,"breadcrumbs":4,"title":2},"1350":{"body":46,"breadcrumbs":5,"title":2},"1351":{"body":215,"breadcrumbs":6,"title":3},"1352":{"body":249,"breadcrumbs":6,"title":3},"1353":{"body":121,"breadcrumbs":8,"title":5},"1354":{"body":62,"breadcrumbs":6,"title":3},"1355":{"body":204,"breadcrumbs":5,"title":2},"1356":{"body":120,"breadcrumbs":4,"title":1},"1357":{"body":111,"breadcrumbs":8,"title":5},"1358":{"body":185,"breadcrumbs":6,"title":3},"1359":{"body":0,"breadcrumbs":4,"title":1},"136":{"body":62,"breadcrumbs":7,"title":5},"1360":{"body":78,"breadcrumbs":5,"title":2},"1361":{"body":31,"breadcrumbs":6,"title":3},"1362":{"body":45,"breadcrumbs":6,"title":3},"1363":{"body":14,"breadcrumbs":6,"title":3},"1364":{"body":37,"breadcrumbs":5,"title":2},"1365":{"body":40,"breadcrumbs":9,"title":4},"1366":{"body":77,"breadcrumbs":9,"title":4},"1367":{"body":137,"breadcrumbs":7,"title":2},"1368":{"body":26,"breadcrumbs":7,"title":2},"1369":{"body":20,"breadcrumbs":8,"title":3},"137":{"body":39,"breadcrumbs":3,"title":1},"1370":{"body":42,"breadcrumbs":7,"title":3},"1371":{"body":201,"breadcrumbs":6,"title":2},"1372":{"body":179,"breadcrumbs":6,"title":2},"1373":{"body":187,"breadcrumbs":7,"title":3},"1374":{"body":17,"breadcrumbs":6,"title":2},"1375":{"body":18,"breadcrumbs":5,"title":1},"1376":{"body":29,"breadcrumbs":4,"title":2},"1377":{"body":158,"breadcrumbs":4,"title":2},"1378":{"body":31,"breadcrumbs":5,"title":3},"1379":{"body":0,"breadcrumbs":2,"title":1},"138":{"body":35,"breadcrumbs":4,"title":2},"1380":{"body":21,"breadcrumbs":4,"title":3},"1381":{"body":16,"breadcrumbs":2,"title":1},"1382":{"body":12,"breadcrumbs":3,"title":2},"1383":{"body":277,"breadcrumbs":5,"title":4},"1384":{"body":3,"breadcrumbs":2,"title":1},"1385":{"body":22,"breadcrumbs":3,"title":2},"1386":{"body":154,"breadcrumbs":3,"title":2},"1387":{"body":43,"breadcrumbs":5,"title":4},"1388":{"body":19,"breadcrumbs":4,"title":3},"1389":{"body":1,"breadcrumbs":2,"title":1},"139":{"body":51,"breadcrumbs":4,"title":2},"1390":{"body":8,"breadcrumbs":4,"title":3},"1391":{"body":0,"breadcrumbs":2,"title":1},"1392":{"body":25,"breadcrumbs":6,"title":5},"1393":{"body":45,"breadcrumbs":5,"title":4},"1394":{"body":36,"breadcrumbs":4,"title":3},"1395":{"body":0,"breadcrumbs":3,"title":2},"1396":{"body":20,"breadcrumbs":6,"title":5},"1397":{"body":35,"breadcrumbs":5,"title":4},"1398":{"body":25,"breadcrumbs":3,"title":2},"1399":{"body":0,"breadcrumbs":2,"title":1},"14":{"body":37,"breadcrumbs":5,"title":2},"140":{"body":61,"breadcrumbs":3,"title":1},"1400":{"body":64,"breadcrumbs":4,"title":3},"1401":{"body":27,"breadcrumbs":3,"title":2},"1402":{"body":29,"breadcrumbs":5,"title":4},"1403":{"body":21,"breadcrumbs":4,"title":3},"1404":{"body":19,"breadcrumbs":3,"title":2},"1405":{"body":23,"breadcrumbs":4,"title":2},"1406":{"body":158,"breadcrumbs":4,"title":2},"1407":{"body":16,"breadcrumbs":4,"title":2},"1408":{"body":7,"breadcrumbs":4,"title":2},"1409":{"body":17,"breadcrumbs":4,"title":2},"141":{"body":47,"breadcrumbs":2,"title":1},"1410":{"body":23,"breadcrumbs":4,"title":2},"1411":{"body":17,"breadcrumbs":4,"title":2},"1412":{"body":3,"breadcrumbs":3,"title":1},"1413":{"body":155,"breadcrumbs":3,"title":1},"1414":{"body":556,"breadcrumbs":2,"title":1},"142":{"body":14,"breadcrumbs":3,"title":2},"143":{"body":83,"breadcrumbs":2,"title":1},"144":{"body":127,"breadcrumbs":3,"title":2},"145":{"body":41,"breadcrumbs":2,"title":1},"146":{"body":273,"breadcrumbs":4,"title":3},"147":{"body":6,"breadcrumbs":2,"title":1},"148":{"body":52,"breadcrumbs":3,"title":1},"149":{"body":49,"breadcrumbs":5,"title":3},"15":{"body":16,"breadcrumbs":4,"title":1},"150":{"body":20,"breadcrumbs":3,"title":1},"151":{"body":3,"breadcrumbs":3,"title":1},"152":{"body":55,"breadcrumbs":4,"title":2},"153":{"body":184,"breadcrumbs":3,"title":1},"154":{"body":156,"breadcrumbs":3,"title":1},"155":{"body":111,"breadcrumbs":3,"title":1},"156":{"body":6,"breadcrumbs":3,"title":1},"157":{"body":39,"breadcrumbs":2,"title":1},"158":{"body":58,"breadcrumbs":2,"title":1},"159":{"body":98,"breadcrumbs":3,"title":2},"16":{"body":5,"breadcrumbs":4,"title":1},"160":{"body":135,"breadcrumbs":3,"title":2},"161":{"body":191,"breadcrumbs":3,"title":2},"162":{"body":86,"breadcrumbs":4,"title":3},"163":{"body":237,"breadcrumbs":3,"title":2},"164":{"body":136,"breadcrumbs":5,"title":4},"165":{"body":15,"breadcrumbs":5,"title":4},"166":{"body":617,"breadcrumbs":3,"title":2},"167":{"body":85,"breadcrumbs":3,"title":2},"168":{"body":366,"breadcrumbs":3,"title":2},"169":{"body":101,"breadcrumbs":4,"title":3},"17":{"body":45,"breadcrumbs":4,"title":1},"170":{"body":221,"breadcrumbs":3,"title":2},"171":{"body":396,"breadcrumbs":4,"title":3},"172":{"body":288,"breadcrumbs":2,"title":1},"173":{"body":367,"breadcrumbs":2,"title":1},"174":{"body":301,"breadcrumbs":2,"title":1},"175":{"body":405,"breadcrumbs":4,"title":3},"176":{"body":108,"breadcrumbs":4,"title":3},"177":{"body":77,"breadcrumbs":3,"title":2},"178":{"body":203,"breadcrumbs":4,"title":3},"179":{"body":123,"breadcrumbs":3,"title":2},"18":{"body":34,"breadcrumbs":5,"title":2},"180":{"body":108,"breadcrumbs":3,"title":2},"181":{"body":145,"breadcrumbs":2,"title":1},"182":{"body":26,"breadcrumbs":2,"title":1},"183":{"body":38,"breadcrumbs":4,"title":2},"184":{"body":52,"breadcrumbs":5,"title":3},"185":{"body":27,"breadcrumbs":5,"title":3},"186":{"body":90,"breadcrumbs":4,"title":2},"187":{"body":45,"breadcrumbs":7,"title":5},"188":{"body":55,"breadcrumbs":6,"title":4},"189":{"body":95,"breadcrumbs":4,"title":2},"19":{"body":72,"breadcrumbs":5,"title":2},"190":{"body":21,"breadcrumbs":6,"title":4},"191":{"body":75,"breadcrumbs":4,"title":2},"192":{"body":21,"breadcrumbs":4,"title":2},"193":{"body":49,"breadcrumbs":5,"title":3},"194":{"body":27,"breadcrumbs":4,"title":2},"195":{"body":44,"breadcrumbs":2,"title":1},"196":{"body":49,"breadcrumbs":4,"title":3},"197":{"body":38,"breadcrumbs":3,"title":2},"198":{"body":201,"breadcrumbs":4,"title":3},"199":{"body":120,"breadcrumbs":3,"title":2},"2":{"body":28,"breadcrumbs":3,"title":1},"20":{"body":71,"breadcrumbs":5,"title":2},"200":{"body":101,"breadcrumbs":5,"title":4},"201":{"body":56,"breadcrumbs":3,"title":2},"202":{"body":69,"breadcrumbs":5,"title":4},"203":{"body":170,"breadcrumbs":4,"title":3},"204":{"body":5,"breadcrumbs":2,"title":1},"205":{"body":51,"breadcrumbs":2,"title":1},"206":{"body":65,"breadcrumbs":3,"title":2},"207":{"body":104,"breadcrumbs":3,"title":2},"208":{"body":108,"breadcrumbs":4,"title":3},"209":{"body":132,"breadcrumbs":3,"title":2},"21":{"body":46,"breadcrumbs":5,"title":2},"210":{"body":78,"breadcrumbs":3,"title":2},"211":{"body":185,"breadcrumbs":3,"title":2},"212":{"body":225,"breadcrumbs":3,"title":2},"213":{"body":187,"breadcrumbs":3,"title":2},"214":{"body":52,"breadcrumbs":5,"title":4},"215":{"body":50,"breadcrumbs":5,"title":4},"216":{"body":37,"breadcrumbs":2,"title":1},"217":{"body":27,"breadcrumbs":3,"title":2},"218":{"body":159,"breadcrumbs":3,"title":2},"219":{"body":22,"breadcrumbs":6,"title":3},"22":{"body":44,"breadcrumbs":5,"title":2},"220":{"body":597,"breadcrumbs":5,"title":2},"221":{"body":84,"breadcrumbs":6,"title":3},"222":{"body":316,"breadcrumbs":5,"title":2},"223":{"body":116,"breadcrumbs":6,"title":3},"224":{"body":32,"breadcrumbs":5,"title":2},"225":{"body":17,"breadcrumbs":4,"title":1},"226":{"body":90,"breadcrumbs":5,"title":2},"227":{"body":29,"breadcrumbs":5,"title":2},"228":{"body":20,"breadcrumbs":4,"title":2},"229":{"body":66,"breadcrumbs":3,"title":1},"23":{"body":5,"breadcrumbs":4,"title":1},"230":{"body":41,"breadcrumbs":4,"title":2},"231":{"body":88,"breadcrumbs":6,"title":4},"232":{"body":27,"breadcrumbs":3,"title":1},"233":{"body":35,"breadcrumbs":4,"title":2},"234":{"body":26,"breadcrumbs":3,"title":1},"235":{"body":28,"breadcrumbs":3,"title":1},"236":{"body":13,"breadcrumbs":4,"title":2},"237":{"body":35,"breadcrumbs":4,"title":2},"238":{"body":10,"breadcrumbs":3,"title":1},"239":{"body":6,"breadcrumbs":3,"title":1},"24":{"body":20,"breadcrumbs":6,"title":3},"240":{"body":68,"breadcrumbs":8,"title":5},"241":{"body":49,"breadcrumbs":5,"title":2},"242":{"body":39,"breadcrumbs":5,"title":2},"243":{"body":3,"breadcrumbs":4,"title":1},"244":{"body":19,"breadcrumbs":4,"title":2},"245":{"body":57,"breadcrumbs":4,"title":2},"246":{"body":49,"breadcrumbs":4,"title":2},"247":{"body":55,"breadcrumbs":3,"title":1},"248":{"body":118,"breadcrumbs":4,"title":2},"249":{"body":18,"breadcrumbs":4,"title":2},"25":{"body":41,"breadcrumbs":5,"title":2},"250":{"body":16,"breadcrumbs":4,"title":2},"251":{"body":26,"breadcrumbs":6,"title":4},"252":{"body":74,"breadcrumbs":4,"title":2},"253":{"body":52,"breadcrumbs":3,"title":1},"254":{"body":181,"breadcrumbs":4,"title":2},"255":{"body":45,"breadcrumbs":5,"title":3},"256":{"body":145,"breadcrumbs":5,"title":3},"257":{"body":115,"breadcrumbs":4,"title":2},"258":{"body":50,"breadcrumbs":3,"title":1},"259":{"body":37,"breadcrumbs":5,"title":3},"26":{"body":41,"breadcrumbs":5,"title":2},"260":{"body":90,"breadcrumbs":4,"title":2},"261":{"body":156,"breadcrumbs":4,"title":2},"262":{"body":30,"breadcrumbs":4,"title":2},"263":{"body":72,"breadcrumbs":6,"title":4},"264":{"body":116,"breadcrumbs":7,"title":5},"265":{"body":7,"breadcrumbs":8,"title":6},"266":{"body":21,"breadcrumbs":4,"title":2},"267":{"body":49,"breadcrumbs":7,"title":5},"268":{"body":88,"breadcrumbs":6,"title":4},"269":{"body":11,"breadcrumbs":7,"title":5},"27":{"body":5,"breadcrumbs":4,"title":1},"270":{"body":34,"breadcrumbs":7,"title":5},"271":{"body":40,"breadcrumbs":8,"title":6},"272":{"body":22,"breadcrumbs":5,"title":3},"273":{"body":268,"breadcrumbs":9,"title":7},"274":{"body":8,"breadcrumbs":3,"title":1},"275":{"body":10,"breadcrumbs":2,"title":1},"276":{"body":22,"breadcrumbs":4,"title":3},"277":{"body":27,"breadcrumbs":4,"title":3},"278":{"body":27,"breadcrumbs":5,"title":4},"279":{"body":34,"breadcrumbs":3,"title":2},"28":{"body":18,"breadcrumbs":4,"title":2},"280":{"body":33,"breadcrumbs":5,"title":4},"281":{"body":23,"breadcrumbs":5,"title":4},"282":{"body":21,"breadcrumbs":5,"title":4},"283":{"body":15,"breadcrumbs":4,"title":3},"284":{"body":115,"breadcrumbs":5,"title":4},"285":{"body":17,"breadcrumbs":7,"title":3},"286":{"body":8,"breadcrumbs":6,"title":2},"287":{"body":29,"breadcrumbs":5,"title":1},"288":{"body":60,"breadcrumbs":5,"title":1},"289":{"body":8,"breadcrumbs":6,"title":2},"29":{"body":18,"breadcrumbs":4,"title":2},"290":{"body":35,"breadcrumbs":5,"title":1},"291":{"body":19,"breadcrumbs":6,"title":2},"292":{"body":35,"breadcrumbs":6,"title":2},"293":{"body":30,"breadcrumbs":5,"title":1},"294":{"body":112,"breadcrumbs":6,"title":2},"295":{"body":42,"breadcrumbs":2,"title":1},"296":{"body":91,"breadcrumbs":3,"title":2},"297":{"body":268,"breadcrumbs":5,"title":4},"298":{"body":92,"breadcrumbs":3,"title":2},"299":{"body":44,"breadcrumbs":4,"title":3},"3":{"body":106,"breadcrumbs":3,"title":1},"30":{"body":38,"breadcrumbs":3,"title":1},"300":{"body":7,"breadcrumbs":3,"title":2},"301":{"body":79,"breadcrumbs":4,"title":3},"302":{"body":7,"breadcrumbs":2,"title":1},"303":{"body":10,"breadcrumbs":3,"title":2},"304":{"body":35,"breadcrumbs":3,"title":2},"305":{"body":21,"breadcrumbs":4,"title":3},"306":{"body":7,"breadcrumbs":4,"title":3},"307":{"body":24,"breadcrumbs":3,"title":2},"308":{"body":17,"breadcrumbs":3,"title":2},"309":{"body":11,"breadcrumbs":5,"title":2},"31":{"body":55,"breadcrumbs":4,"title":2},"310":{"body":117,"breadcrumbs":5,"title":2},"311":{"body":70,"breadcrumbs":5,"title":2},"312":{"body":115,"breadcrumbs":5,"title":2},"313":{"body":44,"breadcrumbs":5,"title":2},"314":{"body":107,"breadcrumbs":7,"title":4},"315":{"body":53,"breadcrumbs":5,"title":2},"316":{"body":37,"breadcrumbs":2,"title":1},"317":{"body":17,"breadcrumbs":3,"title":2},"318":{"body":114,"breadcrumbs":4,"title":3},"319":{"body":83,"breadcrumbs":2,"title":1},"32":{"body":16,"breadcrumbs":2,"title":1},"320":{"body":44,"breadcrumbs":3,"title":2},"321":{"body":11,"breadcrumbs":4,"title":3},"322":{"body":278,"breadcrumbs":3,"title":2},"323":{"body":79,"breadcrumbs":3,"title":2},"324":{"body":0,"breadcrumbs":3,"title":2},"325":{"body":39,"breadcrumbs":3,"title":2},"326":{"body":144,"breadcrumbs":3,"title":2},"327":{"body":22,"breadcrumbs":3,"title":2},"328":{"body":37,"breadcrumbs":2,"title":1},"329":{"body":45,"breadcrumbs":2,"title":1},"33":{"body":28,"breadcrumbs":3,"title":2},"330":{"body":76,"breadcrumbs":3,"title":2},"331":{"body":78,"breadcrumbs":3,"title":2},"332":{"body":0,"breadcrumbs":4,"title":3},"333":{"body":80,"breadcrumbs":5,"title":4},"334":{"body":66,"breadcrumbs":4,"title":3},"335":{"body":33,"breadcrumbs":5,"title":4},"336":{"body":90,"breadcrumbs":4,"title":3},"337":{"body":22,"breadcrumbs":5,"title":4},"338":{"body":6,"breadcrumbs":6,"title":5},"339":{"body":35,"breadcrumbs":3,"title":2},"34":{"body":92,"breadcrumbs":3,"title":2},"340":{"body":72,"breadcrumbs":4,"title":3},"341":{"body":35,"breadcrumbs":3,"title":2},"342":{"body":8,"breadcrumbs":3,"title":2},"343":{"body":6,"breadcrumbs":4,"title":3},"344":{"body":15,"breadcrumbs":2,"title":1},"345":{"body":140,"breadcrumbs":4,"title":3},"346":{"body":7,"breadcrumbs":4,"title":3},"347":{"body":78,"breadcrumbs":6,"title":5},"348":{"body":49,"breadcrumbs":5,"title":4},"349":{"body":10,"breadcrumbs":3,"title":2},"35":{"body":95,"breadcrumbs":5,"title":4},"350":{"body":36,"breadcrumbs":3,"title":2},"351":{"body":41,"breadcrumbs":4,"title":3},"352":{"body":0,"breadcrumbs":4,"title":3},"353":{"body":58,"breadcrumbs":4,"title":3},"354":{"body":62,"breadcrumbs":4,"title":3},"355":{"body":9,"breadcrumbs":4,"title":3},"356":{"body":45,"breadcrumbs":3,"title":2},"357":{"body":48,"breadcrumbs":7,"title":6},"358":{"body":0,"breadcrumbs":21,"title":18},"359":{"body":26,"breadcrumbs":5,"title":2},"36":{"body":74,"breadcrumbs":3,"title":2},"360":{"body":30,"breadcrumbs":6,"title":3},"361":{"body":100,"breadcrumbs":5,"title":2},"362":{"body":139,"breadcrumbs":5,"title":2},"363":{"body":41,"breadcrumbs":5,"title":2},"364":{"body":69,"breadcrumbs":5,"title":2},"365":{"body":73,"breadcrumbs":5,"title":2},"366":{"body":53,"breadcrumbs":5,"title":2},"367":{"body":50,"breadcrumbs":7,"title":4},"368":{"body":70,"breadcrumbs":4,"title":1},"369":{"body":65,"breadcrumbs":5,"title":2},"37":{"body":43,"breadcrumbs":3,"title":2},"370":{"body":23,"breadcrumbs":5,"title":2},"371":{"body":28,"breadcrumbs":4,"title":2},"372":{"body":45,"breadcrumbs":2,"title":0},"373":{"body":74,"breadcrumbs":6,"title":4},"374":{"body":47,"breadcrumbs":6,"title":4},"375":{"body":34,"breadcrumbs":4,"title":2},"376":{"body":115,"breadcrumbs":4,"title":2},"377":{"body":77,"breadcrumbs":7,"title":5},"378":{"body":106,"breadcrumbs":5,"title":3},"379":{"body":39,"breadcrumbs":5,"title":3},"38":{"body":3,"breadcrumbs":2,"title":1},"380":{"body":92,"breadcrumbs":4,"title":2},"381":{"body":27,"breadcrumbs":4,"title":2},"382":{"body":0,"breadcrumbs":5,"title":3},"383":{"body":30,"breadcrumbs":4,"title":2},"384":{"body":22,"breadcrumbs":3,"title":1},"385":{"body":7,"breadcrumbs":4,"title":2},"386":{"body":26,"breadcrumbs":5,"title":3},"387":{"body":16,"breadcrumbs":4,"title":3},"388":{"body":34,"breadcrumbs":3,"title":2},"389":{"body":119,"breadcrumbs":2,"title":1},"39":{"body":63,"breadcrumbs":2,"title":1},"390":{"body":58,"breadcrumbs":2,"title":1},"391":{"body":75,"breadcrumbs":3,"title":2},"392":{"body":79,"breadcrumbs":4,"title":3},"393":{"body":36,"breadcrumbs":3,"title":2},"394":{"body":82,"breadcrumbs":4,"title":3},"395":{"body":88,"breadcrumbs":4,"title":3},"396":{"body":31,"breadcrumbs":3,"title":2},"397":{"body":37,"breadcrumbs":3,"title":2},"398":{"body":0,"breadcrumbs":20,"title":17},"399":{"body":34,"breadcrumbs":5,"title":2},"4":{"body":27,"breadcrumbs":4,"title":2},"40":{"body":296,"breadcrumbs":4,"title":3},"400":{"body":37,"breadcrumbs":6,"title":3},"401":{"body":28,"breadcrumbs":5,"title":2},"402":{"body":65,"breadcrumbs":6,"title":3},"403":{"body":66,"breadcrumbs":6,"title":3},"404":{"body":52,"breadcrumbs":6,"title":3},"405":{"body":0,"breadcrumbs":6,"title":3},"406":{"body":42,"breadcrumbs":7,"title":4},"407":{"body":21,"breadcrumbs":6,"title":3},"408":{"body":19,"breadcrumbs":6,"title":3},"409":{"body":35,"breadcrumbs":7,"title":4},"41":{"body":95,"breadcrumbs":3,"title":2},"410":{"body":32,"breadcrumbs":6,"title":3},"411":{"body":70,"breadcrumbs":5,"title":2},"412":{"body":56,"breadcrumbs":5,"title":2},"413":{"body":39,"breadcrumbs":5,"title":2},"414":{"body":60,"breadcrumbs":5,"title":2},"415":{"body":22,"breadcrumbs":5,"title":2},"416":{"body":84,"breadcrumbs":2,"title":1},"417":{"body":51,"breadcrumbs":3,"title":2},"418":{"body":22,"breadcrumbs":2,"title":1},"419":{"body":23,"breadcrumbs":4,"title":3},"42":{"body":70,"breadcrumbs":4,"title":3},"420":{"body":19,"breadcrumbs":3,"title":2},"421":{"body":2,"breadcrumbs":2,"title":1},"422":{"body":0,"breadcrumbs":21,"title":18},"423":{"body":74,"breadcrumbs":5,"title":2},"424":{"body":58,"breadcrumbs":5,"title":2},"425":{"body":0,"breadcrumbs":5,"title":2},"426":{"body":18,"breadcrumbs":5,"title":2},"427":{"body":71,"breadcrumbs":5,"title":2},"428":{"body":86,"breadcrumbs":5,"title":2},"429":{"body":60,"breadcrumbs":7,"title":4},"43":{"body":132,"breadcrumbs":2,"title":1},"430":{"body":86,"breadcrumbs":7,"title":4},"431":{"body":53,"breadcrumbs":6,"title":3},"432":{"body":0,"breadcrumbs":6,"title":3},"433":{"body":51,"breadcrumbs":7,"title":4},"434":{"body":104,"breadcrumbs":7,"title":4},"435":{"body":57,"breadcrumbs":6,"title":3},"436":{"body":93,"breadcrumbs":7,"title":4},"437":{"body":18,"breadcrumbs":5,"title":2},"438":{"body":26,"breadcrumbs":5,"title":3},"439":{"body":44,"breadcrumbs":4,"title":2},"44":{"body":0,"breadcrumbs":4,"title":3},"440":{"body":130,"breadcrumbs":5,"title":3},"441":{"body":50,"breadcrumbs":4,"title":2},"442":{"body":99,"breadcrumbs":4,"title":2},"443":{"body":30,"breadcrumbs":3,"title":1},"444":{"body":0,"breadcrumbs":4,"title":2},"445":{"body":28,"breadcrumbs":6,"title":4},"446":{"body":4,"breadcrumbs":5,"title":3},"447":{"body":21,"breadcrumbs":5,"title":3},"448":{"body":10,"breadcrumbs":6,"title":4},"449":{"body":15,"breadcrumbs":8,"title":6},"45":{"body":46,"breadcrumbs":3,"title":2},"450":{"body":254,"breadcrumbs":5,"title":3},"451":{"body":64,"breadcrumbs":5,"title":3},"452":{"body":18,"breadcrumbs":4,"title":2},"453":{"body":94,"breadcrumbs":4,"title":2},"454":{"body":224,"breadcrumbs":4,"title":2},"455":{"body":346,"breadcrumbs":4,"title":2},"456":{"body":182,"breadcrumbs":4,"title":2},"457":{"body":116,"breadcrumbs":6,"title":4},"458":{"body":61,"breadcrumbs":3,"title":1},"459":{"body":69,"breadcrumbs":4,"title":2},"46":{"body":103,"breadcrumbs":2,"title":1},"460":{"body":39,"breadcrumbs":3,"title":1},"461":{"body":45,"breadcrumbs":4,"title":2},"462":{"body":214,"breadcrumbs":3,"title":1},"463":{"body":46,"breadcrumbs":4,"title":2},"464":{"body":47,"breadcrumbs":3,"title":1},"465":{"body":67,"breadcrumbs":5,"title":3},"466":{"body":186,"breadcrumbs":5,"title":3},"467":{"body":20,"breadcrumbs":7,"title":4},"468":{"body":50,"breadcrumbs":6,"title":3},"469":{"body":100,"breadcrumbs":6,"title":3},"47":{"body":158,"breadcrumbs":6,"title":5},"470":{"body":87,"breadcrumbs":5,"title":2},"471":{"body":94,"breadcrumbs":8,"title":5},"472":{"body":156,"breadcrumbs":4,"title":1},"473":{"body":62,"breadcrumbs":6,"title":3},"474":{"body":34,"breadcrumbs":9,"title":6},"475":{"body":67,"breadcrumbs":7,"title":4},"476":{"body":166,"breadcrumbs":7,"title":4},"477":{"body":89,"breadcrumbs":6,"title":3},"478":{"body":0,"breadcrumbs":6,"title":3},"479":{"body":120,"breadcrumbs":5,"title":2},"48":{"body":37,"breadcrumbs":3,"title":2},"480":{"body":79,"breadcrumbs":6,"title":3},"481":{"body":14,"breadcrumbs":5,"title":2},"482":{"body":0,"breadcrumbs":6,"title":3},"483":{"body":52,"breadcrumbs":6,"title":3},"484":{"body":19,"breadcrumbs":4,"title":1},"485":{"body":16,"breadcrumbs":5,"title":2},"486":{"body":134,"breadcrumbs":5,"title":2},"487":{"body":75,"breadcrumbs":5,"title":2},"488":{"body":38,"breadcrumbs":6,"title":3},"489":{"body":49,"breadcrumbs":2,"title":1},"49":{"body":110,"breadcrumbs":4,"title":3},"490":{"body":105,"breadcrumbs":2,"title":1},"491":{"body":26,"breadcrumbs":2,"title":1},"492":{"body":14,"breadcrumbs":2,"title":1},"493":{"body":34,"breadcrumbs":6,"title":3},"494":{"body":62,"breadcrumbs":5,"title":2},"495":{"body":64,"breadcrumbs":5,"title":2},"496":{"body":46,"breadcrumbs":5,"title":2},"497":{"body":137,"breadcrumbs":6,"title":3},"498":{"body":0,"breadcrumbs":4,"title":1},"499":{"body":12,"breadcrumbs":7,"title":4},"5":{"body":23,"breadcrumbs":4,"title":1},"50":{"body":36,"breadcrumbs":4,"title":3},"500":{"body":25,"breadcrumbs":4,"title":1},"501":{"body":35,"breadcrumbs":5,"title":2},"502":{"body":45,"breadcrumbs":4,"title":1},"503":{"body":0,"breadcrumbs":6,"title":3},"504":{"body":25,"breadcrumbs":5,"title":2},"505":{"body":23,"breadcrumbs":4,"title":1},"506":{"body":24,"breadcrumbs":5,"title":2},"507":{"body":21,"breadcrumbs":6,"title":3},"508":{"body":19,"breadcrumbs":5,"title":2},"509":{"body":18,"breadcrumbs":4,"title":2},"51":{"body":46,"breadcrumbs":3,"title":2},"510":{"body":73,"breadcrumbs":4,"title":2},"511":{"body":4,"breadcrumbs":3,"title":1},"512":{"body":29,"breadcrumbs":4,"title":2},"513":{"body":34,"breadcrumbs":5,"title":3},"514":{"body":50,"breadcrumbs":3,"title":1},"515":{"body":41,"breadcrumbs":4,"title":2},"516":{"body":59,"breadcrumbs":7,"title":5},"517":{"body":23,"breadcrumbs":6,"title":4},"518":{"body":16,"breadcrumbs":3,"title":1},"519":{"body":19,"breadcrumbs":4,"title":2},"52":{"body":283,"breadcrumbs":2,"title":1},"520":{"body":3,"breadcrumbs":3,"title":1},"521":{"body":33,"breadcrumbs":2,"title":1},"522":{"body":45,"breadcrumbs":2,"title":1},"523":{"body":72,"breadcrumbs":6,"title":5},"524":{"body":28,"breadcrumbs":4,"title":3},"525":{"body":56,"breadcrumbs":4,"title":3},"526":{"body":111,"breadcrumbs":3,"title":2},"527":{"body":37,"breadcrumbs":2,"title":1},"528":{"body":24,"breadcrumbs":2,"title":1},"529":{"body":53,"breadcrumbs":2,"title":1},"53":{"body":737,"breadcrumbs":5,"title":4},"530":{"body":30,"breadcrumbs":3,"title":2},"531":{"body":168,"breadcrumbs":4,"title":3},"532":{"body":50,"breadcrumbs":3,"title":2},"533":{"body":151,"breadcrumbs":4,"title":3},"534":{"body":119,"breadcrumbs":4,"title":3},"535":{"body":167,"breadcrumbs":4,"title":3},"536":{"body":183,"breadcrumbs":6,"title":5},"537":{"body":78,"breadcrumbs":3,"title":2},"538":{"body":92,"breadcrumbs":6,"title":5},"539":{"body":6,"breadcrumbs":2,"title":1},"54":{"body":203,"breadcrumbs":3,"title":2},"540":{"body":0,"breadcrumbs":6,"title":3},"541":{"body":64,"breadcrumbs":4,"title":1},"542":{"body":93,"breadcrumbs":5,"title":2},"543":{"body":182,"breadcrumbs":5,"title":2},"544":{"body":98,"breadcrumbs":7,"title":4},"545":{"body":41,"breadcrumbs":6,"title":3},"546":{"body":8,"breadcrumbs":4,"title":1},"547":{"body":75,"breadcrumbs":10,"title":7},"548":{"body":21,"breadcrumbs":4,"title":1},"549":{"body":7,"breadcrumbs":4,"title":1},"55":{"body":282,"breadcrumbs":2,"title":1},"550":{"body":15,"breadcrumbs":3,"title":1},"551":{"body":51,"breadcrumbs":4,"title":2},"552":{"body":45,"breadcrumbs":4,"title":2},"553":{"body":124,"breadcrumbs":4,"title":2},"554":{"body":20,"breadcrumbs":3,"title":1},"555":{"body":6,"breadcrumbs":3,"title":1},"556":{"body":52,"breadcrumbs":2,"title":1},"557":{"body":13,"breadcrumbs":2,"title":1},"558":{"body":56,"breadcrumbs":2,"title":1},"559":{"body":53,"breadcrumbs":3,"title":2},"56":{"body":37,"breadcrumbs":2,"title":1},"560":{"body":20,"breadcrumbs":3,"title":2},"561":{"body":42,"breadcrumbs":3,"title":2},"562":{"body":18,"breadcrumbs":4,"title":3},"563":{"body":0,"breadcrumbs":4,"title":2},"564":{"body":36,"breadcrumbs":4,"title":2},"565":{"body":56,"breadcrumbs":5,"title":3},"566":{"body":16,"breadcrumbs":2,"title":1},"567":{"body":124,"breadcrumbs":3,"title":2},"568":{"body":104,"breadcrumbs":7,"title":6},"569":{"body":185,"breadcrumbs":5,"title":4},"57":{"body":51,"breadcrumbs":3,"title":2},"570":{"body":69,"breadcrumbs":5,"title":4},"571":{"body":80,"breadcrumbs":3,"title":2},"572":{"body":113,"breadcrumbs":4,"title":3},"573":{"body":94,"breadcrumbs":4,"title":3},"574":{"body":38,"breadcrumbs":4,"title":3},"575":{"body":58,"breadcrumbs":5,"title":4},"576":{"body":246,"breadcrumbs":7,"title":6},"577":{"body":119,"breadcrumbs":4,"title":3},"578":{"body":133,"breadcrumbs":5,"title":4},"579":{"body":104,"breadcrumbs":4,"title":3},"58":{"body":144,"breadcrumbs":6,"title":5},"580":{"body":93,"breadcrumbs":3,"title":2},"581":{"body":50,"breadcrumbs":5,"title":4},"582":{"body":128,"breadcrumbs":4,"title":3},"583":{"body":46,"breadcrumbs":5,"title":4},"584":{"body":107,"breadcrumbs":5,"title":4},"585":{"body":24,"breadcrumbs":2,"title":1},"586":{"body":33,"breadcrumbs":5,"title":4},"587":{"body":10,"breadcrumbs":2,"title":1},"588":{"body":17,"breadcrumbs":5,"title":2},"589":{"body":65,"breadcrumbs":7,"title":4},"59":{"body":41,"breadcrumbs":3,"title":2},"590":{"body":43,"breadcrumbs":6,"title":3},"591":{"body":11,"breadcrumbs":4,"title":1},"592":{"body":0,"breadcrumbs":9,"title":4},"593":{"body":23,"breadcrumbs":6,"title":1},"594":{"body":27,"breadcrumbs":6,"title":1},"595":{"body":12,"breadcrumbs":6,"title":1},"596":{"body":14,"breadcrumbs":6,"title":1},"597":{"body":8,"breadcrumbs":6,"title":1},"598":{"body":6,"breadcrumbs":6,"title":1},"599":{"body":31,"breadcrumbs":4,"title":2},"6":{"body":68,"breadcrumbs":5,"title":2},"60":{"body":65,"breadcrumbs":3,"title":2},"600":{"body":53,"breadcrumbs":4,"title":2},"601":{"body":27,"breadcrumbs":5,"title":3},"602":{"body":42,"breadcrumbs":5,"title":3},"603":{"body":57,"breadcrumbs":4,"title":2},"604":{"body":54,"breadcrumbs":5,"title":3},"605":{"body":35,"breadcrumbs":6,"title":4},"606":{"body":112,"breadcrumbs":5,"title":3},"607":{"body":82,"breadcrumbs":5,"title":3},"608":{"body":25,"breadcrumbs":4,"title":2},"609":{"body":109,"breadcrumbs":2,"title":1},"61":{"body":84,"breadcrumbs":4,"title":3},"610":{"body":20,"breadcrumbs":5,"title":2},"611":{"body":73,"breadcrumbs":5,"title":2},"612":{"body":225,"breadcrumbs":5,"title":2},"613":{"body":324,"breadcrumbs":5,"title":2},"614":{"body":3,"breadcrumbs":5,"title":2},"615":{"body":102,"breadcrumbs":5,"title":2},"616":{"body":126,"breadcrumbs":6,"title":3},"617":{"body":124,"breadcrumbs":5,"title":2},"618":{"body":108,"breadcrumbs":6,"title":3},"619":{"body":154,"breadcrumbs":5,"title":2},"62":{"body":35,"breadcrumbs":4,"title":3},"620":{"body":276,"breadcrumbs":6,"title":3},"621":{"body":64,"breadcrumbs":6,"title":3},"622":{"body":39,"breadcrumbs":5,"title":2},"623":{"body":81,"breadcrumbs":4,"title":1},"624":{"body":55,"breadcrumbs":6,"title":3},"625":{"body":199,"breadcrumbs":4,"title":1},"626":{"body":32,"breadcrumbs":5,"title":2},"627":{"body":41,"breadcrumbs":5,"title":2},"628":{"body":36,"breadcrumbs":5,"title":2},"629":{"body":27,"breadcrumbs":5,"title":2},"63":{"body":73,"breadcrumbs":5,"title":4},"630":{"body":29,"breadcrumbs":5,"title":2},"631":{"body":137,"breadcrumbs":7,"title":4},"632":{"body":27,"breadcrumbs":5,"title":2},"633":{"body":11,"breadcrumbs":6,"title":3},"634":{"body":40,"breadcrumbs":4,"title":1},"635":{"body":33,"breadcrumbs":5,"title":2},"636":{"body":190,"breadcrumbs":4,"title":1},"637":{"body":36,"breadcrumbs":4,"title":1},"638":{"body":102,"breadcrumbs":4,"title":1},"639":{"body":23,"breadcrumbs":5,"title":2},"64":{"body":77,"breadcrumbs":3,"title":2},"640":{"body":14,"breadcrumbs":5,"title":2},"641":{"body":76,"breadcrumbs":7,"title":4},"642":{"body":18,"breadcrumbs":4,"title":1},"643":{"body":60,"breadcrumbs":5,"title":2},"644":{"body":318,"breadcrumbs":6,"title":3},"645":{"body":54,"breadcrumbs":6,"title":3},"646":{"body":50,"breadcrumbs":5,"title":2},"647":{"body":218,"breadcrumbs":5,"title":2},"648":{"body":81,"breadcrumbs":4,"title":1},"649":{"body":77,"breadcrumbs":5,"title":2},"65":{"body":68,"breadcrumbs":4,"title":3},"650":{"body":50,"breadcrumbs":7,"title":4},"651":{"body":49,"breadcrumbs":5,"title":2},"652":{"body":45,"breadcrumbs":5,"title":2},"653":{"body":63,"breadcrumbs":4,"title":1},"654":{"body":51,"breadcrumbs":7,"title":4},"655":{"body":26,"breadcrumbs":4,"title":1},"656":{"body":72,"breadcrumbs":5,"title":2},"657":{"body":95,"breadcrumbs":5,"title":2},"658":{"body":38,"breadcrumbs":7,"title":3},"659":{"body":171,"breadcrumbs":7,"title":3},"66":{"body":38,"breadcrumbs":5,"title":4},"660":{"body":9,"breadcrumbs":7,"title":3},"661":{"body":717,"breadcrumbs":7,"title":3},"662":{"body":34,"breadcrumbs":6,"title":2},"663":{"body":176,"breadcrumbs":11,"title":7},"664":{"body":0,"breadcrumbs":9,"title":5},"665":{"body":193,"breadcrumbs":10,"title":6},"666":{"body":107,"breadcrumbs":6,"title":2},"667":{"body":43,"breadcrumbs":7,"title":3},"668":{"body":80,"breadcrumbs":7,"title":3},"669":{"body":27,"breadcrumbs":5,"title":1},"67":{"body":15,"breadcrumbs":2,"title":1},"670":{"body":25,"breadcrumbs":7,"title":3},"671":{"body":153,"breadcrumbs":9,"title":5},"672":{"body":20,"breadcrumbs":7,"title":3},"673":{"body":36,"breadcrumbs":8,"title":4},"674":{"body":44,"breadcrumbs":7,"title":3},"675":{"body":0,"breadcrumbs":8,"title":4},"676":{"body":103,"breadcrumbs":7,"title":3},"677":{"body":128,"breadcrumbs":9,"title":5},"678":{"body":14,"breadcrumbs":9,"title":5},"679":{"body":149,"breadcrumbs":9,"title":5},"68":{"body":54,"breadcrumbs":3,"title":2},"680":{"body":1035,"breadcrumbs":8,"title":4},"681":{"body":71,"breadcrumbs":9,"title":5},"682":{"body":67,"breadcrumbs":8,"title":4},"683":{"body":62,"breadcrumbs":10,"title":6},"684":{"body":156,"breadcrumbs":14,"title":10},"685":{"body":34,"breadcrumbs":8,"title":4},"686":{"body":80,"breadcrumbs":7,"title":3},"687":{"body":130,"breadcrumbs":3,"title":1},"688":{"body":154,"breadcrumbs":4,"title":2},"689":{"body":51,"breadcrumbs":5,"title":3},"69":{"body":36,"breadcrumbs":3,"title":2},"690":{"body":73,"breadcrumbs":5,"title":3},"691":{"body":117,"breadcrumbs":5,"title":3},"692":{"body":148,"breadcrumbs":3,"title":1},"693":{"body":81,"breadcrumbs":4,"title":2},"694":{"body":161,"breadcrumbs":4,"title":2},"695":{"body":76,"breadcrumbs":3,"title":1},"696":{"body":121,"breadcrumbs":4,"title":2},"697":{"body":52,"breadcrumbs":4,"title":2},"698":{"body":73,"breadcrumbs":7,"title":5},"699":{"body":92,"breadcrumbs":5,"title":3},"7":{"body":8,"breadcrumbs":4,"title":1},"70":{"body":46,"breadcrumbs":4,"title":3},"700":{"body":100,"breadcrumbs":5,"title":3},"701":{"body":127,"breadcrumbs":5,"title":3},"702":{"body":29,"breadcrumbs":4,"title":2},"703":{"body":408,"breadcrumbs":3,"title":1},"704":{"body":88,"breadcrumbs":3,"title":1},"705":{"body":262,"breadcrumbs":3,"title":1},"706":{"body":241,"breadcrumbs":3,"title":1},"707":{"body":291,"breadcrumbs":4,"title":2},"708":{"body":240,"breadcrumbs":4,"title":2},"709":{"body":1377,"breadcrumbs":3,"title":1},"71":{"body":24,"breadcrumbs":4,"title":3},"710":{"body":275,"breadcrumbs":5,"title":3},"711":{"body":98,"breadcrumbs":5,"title":3},"712":{"body":83,"breadcrumbs":6,"title":4},"713":{"body":454,"breadcrumbs":5,"title":3},"714":{"body":353,"breadcrumbs":4,"title":2},"715":{"body":176,"breadcrumbs":3,"title":1},"716":{"body":232,"breadcrumbs":4,"title":2},"717":{"body":164,"breadcrumbs":4,"title":2},"718":{"body":44,"breadcrumbs":3,"title":1},"719":{"body":14,"breadcrumbs":4,"title":2},"72":{"body":26,"breadcrumbs":3,"title":2},"720":{"body":35,"breadcrumbs":5,"title":3},"721":{"body":144,"breadcrumbs":5,"title":3},"722":{"body":0,"breadcrumbs":3,"title":1},"723":{"body":26,"breadcrumbs":5,"title":3},"724":{"body":19,"breadcrumbs":7,"title":5},"725":{"body":51,"breadcrumbs":7,"title":5},"726":{"body":18,"breadcrumbs":8,"title":6},"727":{"body":21,"breadcrumbs":8,"title":6},"728":{"body":24,"breadcrumbs":6,"title":4},"729":{"body":36,"breadcrumbs":6,"title":4},"73":{"body":16,"breadcrumbs":3,"title":2},"730":{"body":16,"breadcrumbs":8,"title":6},"731":{"body":74,"breadcrumbs":5,"title":2},"732":{"body":0,"breadcrumbs":4,"title":1},"733":{"body":53,"breadcrumbs":4,"title":1},"734":{"body":133,"breadcrumbs":5,"title":2},"735":{"body":406,"breadcrumbs":4,"title":1},"736":{"body":0,"breadcrumbs":4,"title":1},"737":{"body":237,"breadcrumbs":4,"title":1},"738":{"body":27,"breadcrumbs":4,"title":1},"739":{"body":203,"breadcrumbs":4,"title":1},"74":{"body":18,"breadcrumbs":4,"title":2},"740":{"body":301,"breadcrumbs":4,"title":1},"741":{"body":94,"breadcrumbs":4,"title":1},"742":{"body":213,"breadcrumbs":5,"title":2},"743":{"body":73,"breadcrumbs":4,"title":1},"744":{"body":39,"breadcrumbs":4,"title":1},"745":{"body":27,"breadcrumbs":4,"title":1},"746":{"body":0,"breadcrumbs":4,"title":1},"747":{"body":151,"breadcrumbs":4,"title":1},"748":{"body":94,"breadcrumbs":4,"title":1},"749":{"body":65,"breadcrumbs":4,"title":1},"75":{"body":90,"breadcrumbs":4,"title":2},"750":{"body":75,"breadcrumbs":4,"title":1},"751":{"body":88,"breadcrumbs":4,"title":1},"752":{"body":76,"breadcrumbs":5,"title":2},"753":{"body":143,"breadcrumbs":5,"title":2},"754":{"body":79,"breadcrumbs":4,"title":1},"755":{"body":26,"breadcrumbs":5,"title":2},"756":{"body":23,"breadcrumbs":4,"title":1},"757":{"body":20,"breadcrumbs":4,"title":1},"758":{"body":0,"breadcrumbs":4,"title":1},"759":{"body":152,"breadcrumbs":4,"title":1},"76":{"body":54,"breadcrumbs":5,"title":3},"760":{"body":85,"breadcrumbs":4,"title":1},"761":{"body":136,"breadcrumbs":4,"title":1},"762":{"body":69,"breadcrumbs":4,"title":1},"763":{"body":0,"breadcrumbs":4,"title":1},"764":{"body":187,"breadcrumbs":4,"title":1},"765":{"body":56,"breadcrumbs":5,"title":2},"766":{"body":45,"breadcrumbs":5,"title":2},"767":{"body":0,"breadcrumbs":4,"title":1},"768":{"body":34,"breadcrumbs":4,"title":1},"769":{"body":81,"breadcrumbs":6,"title":3},"77":{"body":97,"breadcrumbs":5,"title":3},"770":{"body":0,"breadcrumbs":4,"title":1},"771":{"body":51,"breadcrumbs":4,"title":1},"772":{"body":292,"breadcrumbs":4,"title":1},"773":{"body":181,"breadcrumbs":4,"title":1},"774":{"body":0,"breadcrumbs":4,"title":1},"775":{"body":78,"breadcrumbs":4,"title":1},"776":{"body":46,"breadcrumbs":4,"title":1},"777":{"body":126,"breadcrumbs":5,"title":2},"778":{"body":89,"breadcrumbs":4,"title":1},"779":{"body":0,"breadcrumbs":4,"title":1},"78":{"body":72,"breadcrumbs":6,"title":4},"780":{"body":190,"breadcrumbs":4,"title":1},"781":{"body":170,"breadcrumbs":5,"title":2},"782":{"body":0,"breadcrumbs":4,"title":1},"783":{"body":222,"breadcrumbs":5,"title":2},"784":{"body":63,"breadcrumbs":4,"title":1},"785":{"body":76,"breadcrumbs":5,"title":2},"786":{"body":64,"breadcrumbs":4,"title":1},"787":{"body":197,"breadcrumbs":5,"title":2},"788":{"body":124,"breadcrumbs":4,"title":1},"789":{"body":125,"breadcrumbs":5,"title":2},"79":{"body":102,"breadcrumbs":4,"title":2},"790":{"body":49,"breadcrumbs":4,"title":1},"791":{"body":0,"breadcrumbs":4,"title":1},"792":{"body":115,"breadcrumbs":4,"title":1},"793":{"body":31,"breadcrumbs":5,"title":2},"794":{"body":0,"breadcrumbs":4,"title":1},"795":{"body":161,"breadcrumbs":4,"title":1},"796":{"body":252,"breadcrumbs":4,"title":1},"797":{"body":138,"breadcrumbs":4,"title":1},"798":{"body":150,"breadcrumbs":5,"title":2},"799":{"body":118,"breadcrumbs":4,"title":1},"8":{"body":5,"breadcrumbs":4,"title":1},"80":{"body":63,"breadcrumbs":4,"title":2},"800":{"body":25,"breadcrumbs":4,"title":1},"801":{"body":0,"breadcrumbs":4,"title":1},"802":{"body":35,"breadcrumbs":4,"title":1},"803":{"body":487,"breadcrumbs":4,"title":1},"804":{"body":68,"breadcrumbs":4,"title":1},"805":{"body":36,"breadcrumbs":4,"title":1},"806":{"body":148,"breadcrumbs":5,"title":2},"807":{"body":91,"breadcrumbs":4,"title":1},"808":{"body":44,"breadcrumbs":4,"title":1},"809":{"body":93,"breadcrumbs":4,"title":1},"81":{"body":42,"breadcrumbs":4,"title":2},"810":{"body":0,"breadcrumbs":4,"title":1},"811":{"body":209,"breadcrumbs":4,"title":1},"812":{"body":59,"breadcrumbs":5,"title":2},"813":{"body":40,"breadcrumbs":5,"title":2},"814":{"body":0,"breadcrumbs":4,"title":1},"815":{"body":53,"breadcrumbs":4,"title":1},"816":{"body":43,"breadcrumbs":4,"title":1},"817":{"body":65,"breadcrumbs":5,"title":2},"818":{"body":526,"breadcrumbs":5,"title":2},"819":{"body":11,"breadcrumbs":7,"title":5},"82":{"body":109,"breadcrumbs":5,"title":3},"820":{"body":54,"breadcrumbs":5,"title":3},"821":{"body":31,"breadcrumbs":7,"title":5},"822":{"body":54,"breadcrumbs":5,"title":3},"823":{"body":141,"breadcrumbs":5,"title":3},"824":{"body":20,"breadcrumbs":6,"title":4},"825":{"body":45,"breadcrumbs":5,"title":3},"826":{"body":0,"breadcrumbs":5,"title":3},"827":{"body":11,"breadcrumbs":8,"title":6},"828":{"body":11,"breadcrumbs":7,"title":5},"829":{"body":13,"breadcrumbs":3,"title":1},"83":{"body":16,"breadcrumbs":5,"title":3},"830":{"body":6,"breadcrumbs":5,"title":3},"831":{"body":124,"breadcrumbs":4,"title":2},"832":{"body":54,"breadcrumbs":5,"title":3},"833":{"body":58,"breadcrumbs":7,"title":5},"834":{"body":26,"breadcrumbs":6,"title":4},"835":{"body":106,"breadcrumbs":5,"title":3},"836":{"body":72,"breadcrumbs":5,"title":3},"837":{"body":0,"breadcrumbs":5,"title":3},"838":{"body":55,"breadcrumbs":5,"title":3},"839":{"body":34,"breadcrumbs":5,"title":3},"84":{"body":37,"breadcrumbs":3,"title":1},"840":{"body":78,"breadcrumbs":6,"title":4},"841":{"body":68,"breadcrumbs":5,"title":3},"842":{"body":75,"breadcrumbs":5,"title":3},"843":{"body":109,"breadcrumbs":6,"title":4},"844":{"body":20,"breadcrumbs":5,"title":3},"845":{"body":77,"breadcrumbs":7,"title":5},"846":{"body":62,"breadcrumbs":4,"title":2},"847":{"body":22,"breadcrumbs":6,"title":4},"848":{"body":0,"breadcrumbs":5,"title":3},"849":{"body":58,"breadcrumbs":5,"title":3},"85":{"body":15,"breadcrumbs":6,"title":4},"850":{"body":101,"breadcrumbs":6,"title":4},"851":{"body":238,"breadcrumbs":5,"title":3},"852":{"body":64,"breadcrumbs":5,"title":3},"853":{"body":21,"breadcrumbs":8,"title":6},"854":{"body":36,"breadcrumbs":4,"title":2},"855":{"body":24,"breadcrumbs":4,"title":2},"856":{"body":26,"breadcrumbs":6,"title":4},"857":{"body":14,"breadcrumbs":4,"title":2},"858":{"body":52,"breadcrumbs":6,"title":4},"859":{"body":547,"breadcrumbs":5,"title":3},"86":{"body":37,"breadcrumbs":3,"title":1},"860":{"body":68,"breadcrumbs":6,"title":4},"861":{"body":202,"breadcrumbs":6,"title":4},"862":{"body":0,"breadcrumbs":4,"title":2},"863":{"body":161,"breadcrumbs":7,"title":5},"864":{"body":36,"breadcrumbs":7,"title":5},"865":{"body":20,"breadcrumbs":8,"title":6},"866":{"body":26,"breadcrumbs":5,"title":3},"867":{"body":17,"breadcrumbs":4,"title":2},"868":{"body":125,"breadcrumbs":3,"title":1},"869":{"body":118,"breadcrumbs":4,"title":2},"87":{"body":41,"breadcrumbs":5,"title":3},"870":{"body":139,"breadcrumbs":4,"title":2},"871":{"body":91,"breadcrumbs":9,"title":7},"872":{"body":79,"breadcrumbs":5,"title":3},"873":{"body":46,"breadcrumbs":6,"title":4},"874":{"body":60,"breadcrumbs":4,"title":2},"875":{"body":254,"breadcrumbs":5,"title":3},"876":{"body":172,"breadcrumbs":5,"title":3},"877":{"body":81,"breadcrumbs":5,"title":3},"878":{"body":76,"breadcrumbs":5,"title":3},"879":{"body":66,"breadcrumbs":6,"title":4},"88":{"body":17,"breadcrumbs":4,"title":2},"880":{"body":110,"breadcrumbs":5,"title":3},"881":{"body":114,"breadcrumbs":8,"title":6},"882":{"body":127,"breadcrumbs":5,"title":3},"883":{"body":197,"breadcrumbs":3,"title":1},"884":{"body":39,"breadcrumbs":6,"title":3},"885":{"body":147,"breadcrumbs":6,"title":3},"886":{"body":81,"breadcrumbs":4,"title":1},"887":{"body":33,"breadcrumbs":6,"title":3},"888":{"body":89,"breadcrumbs":5,"title":2},"889":{"body":61,"breadcrumbs":6,"title":3},"89":{"body":23,"breadcrumbs":4,"title":2},"890":{"body":100,"breadcrumbs":5,"title":2},"891":{"body":27,"breadcrumbs":5,"title":2},"892":{"body":35,"breadcrumbs":5,"title":2},"893":{"body":116,"breadcrumbs":6,"title":3},"894":{"body":6,"breadcrumbs":5,"title":2},"895":{"body":178,"breadcrumbs":4,"title":1},"896":{"body":145,"breadcrumbs":4,"title":1},"897":{"body":0,"breadcrumbs":7,"title":4},"898":{"body":14,"breadcrumbs":7,"title":4},"899":{"body":10,"breadcrumbs":8,"title":5},"9":{"body":0,"breadcrumbs":4,"title":1},"90":{"body":23,"breadcrumbs":5,"title":3},"900":{"body":28,"breadcrumbs":8,"title":5},"901":{"body":24,"breadcrumbs":8,"title":5},"902":{"body":5,"breadcrumbs":6,"title":3},"903":{"body":108,"breadcrumbs":6,"title":3},"904":{"body":144,"breadcrumbs":7,"title":4},"905":{"body":88,"breadcrumbs":6,"title":3},"906":{"body":99,"breadcrumbs":6,"title":3},"907":{"body":24,"breadcrumbs":4,"title":1},"908":{"body":70,"breadcrumbs":9,"title":6},"909":{"body":48,"breadcrumbs":7,"title":4},"91":{"body":45,"breadcrumbs":6,"title":4},"910":{"body":44,"breadcrumbs":5,"title":2},"911":{"body":0,"breadcrumbs":4,"title":1},"912":{"body":6,"breadcrumbs":10,"title":7},"913":{"body":9,"breadcrumbs":9,"title":6},"914":{"body":26,"breadcrumbs":9,"title":6},"915":{"body":29,"breadcrumbs":9,"title":6},"916":{"body":83,"breadcrumbs":8,"title":5},"917":{"body":24,"breadcrumbs":4,"title":1},"918":{"body":97,"breadcrumbs":6,"title":3},"919":{"body":91,"breadcrumbs":4,"title":1},"92":{"body":40,"breadcrumbs":3,"title":1},"920":{"body":153,"breadcrumbs":6,"title":3},"921":{"body":85,"breadcrumbs":6,"title":3},"922":{"body":189,"breadcrumbs":8,"title":5},"923":{"body":139,"breadcrumbs":5,"title":2},"924":{"body":345,"breadcrumbs":5,"title":2},"925":{"body":37,"breadcrumbs":7,"title":4},"926":{"body":68,"breadcrumbs":6,"title":3},"927":{"body":198,"breadcrumbs":5,"title":2},"928":{"body":97,"breadcrumbs":4,"title":1},"929":{"body":25,"breadcrumbs":5,"title":2},"93":{"body":8,"breadcrumbs":3,"title":1},"930":{"body":59,"breadcrumbs":4,"title":1},"931":{"body":56,"breadcrumbs":4,"title":1},"932":{"body":61,"breadcrumbs":5,"title":2},"933":{"body":52,"breadcrumbs":5,"title":2},"934":{"body":9,"breadcrumbs":4,"title":2},"935":{"body":39,"breadcrumbs":5,"title":3},"936":{"body":70,"breadcrumbs":5,"title":3},"937":{"body":4,"breadcrumbs":6,"title":4},"938":{"body":11,"breadcrumbs":6,"title":4},"939":{"body":7,"breadcrumbs":5,"title":3},"94":{"body":19,"breadcrumbs":4,"title":2},"940":{"body":5,"breadcrumbs":5,"title":3},"941":{"body":10,"breadcrumbs":4,"title":2},"942":{"body":12,"breadcrumbs":7,"title":5},"943":{"body":8,"breadcrumbs":5,"title":3},"944":{"body":13,"breadcrumbs":7,"title":5},"945":{"body":0,"breadcrumbs":4,"title":2},"946":{"body":75,"breadcrumbs":6,"title":4},"947":{"body":59,"breadcrumbs":4,"title":2},"948":{"body":16,"breadcrumbs":5,"title":3},"949":{"body":149,"breadcrumbs":5,"title":3},"95":{"body":93,"breadcrumbs":3,"title":1},"950":{"body":49,"breadcrumbs":7,"title":5},"951":{"body":57,"breadcrumbs":5,"title":3},"952":{"body":30,"breadcrumbs":6,"title":4},"953":{"body":0,"breadcrumbs":5,"title":3},"954":{"body":58,"breadcrumbs":5,"title":3},"955":{"body":22,"breadcrumbs":5,"title":3},"956":{"body":24,"breadcrumbs":5,"title":3},"957":{"body":65,"breadcrumbs":5,"title":3},"958":{"body":20,"breadcrumbs":6,"title":4},"959":{"body":44,"breadcrumbs":5,"title":3},"96":{"body":20,"breadcrumbs":5,"title":3},"960":{"body":170,"breadcrumbs":6,"title":4},"961":{"body":55,"breadcrumbs":5,"title":3},"962":{"body":45,"breadcrumbs":5,"title":3},"963":{"body":11,"breadcrumbs":4,"title":1},"964":{"body":62,"breadcrumbs":5,"title":2},"965":{"body":29,"breadcrumbs":5,"title":2},"966":{"body":71,"breadcrumbs":5,"title":2},"967":{"body":66,"breadcrumbs":5,"title":2},"968":{"body":14,"breadcrumbs":5,"title":2},"969":{"body":5,"breadcrumbs":5,"title":2},"97":{"body":54,"breadcrumbs":4,"title":2},"970":{"body":40,"breadcrumbs":5,"title":2},"971":{"body":22,"breadcrumbs":5,"title":2},"972":{"body":48,"breadcrumbs":7,"title":4},"973":{"body":0,"breadcrumbs":7,"title":4},"974":{"body":25,"breadcrumbs":7,"title":4},"975":{"body":5,"breadcrumbs":6,"title":3},"976":{"body":35,"breadcrumbs":7,"title":4},"977":{"body":29,"breadcrumbs":5,"title":2},"978":{"body":25,"breadcrumbs":6,"title":3},"979":{"body":0,"breadcrumbs":6,"title":3},"98":{"body":54,"breadcrumbs":4,"title":2},"980":{"body":25,"breadcrumbs":6,"title":3},"981":{"body":26,"breadcrumbs":5,"title":2},"982":{"body":87,"breadcrumbs":6,"title":3},"983":{"body":42,"breadcrumbs":6,"title":3},"984":{"body":30,"breadcrumbs":6,"title":3},"985":{"body":0,"breadcrumbs":6,"title":3},"986":{"body":10,"breadcrumbs":5,"title":2},"987":{"body":25,"breadcrumbs":5,"title":2},"988":{"body":0,"breadcrumbs":6,"title":3},"989":{"body":48,"breadcrumbs":7,"title":4},"99":{"body":33,"breadcrumbs":8,"title":3},"990":{"body":33,"breadcrumbs":8,"title":5},"991":{"body":25,"breadcrumbs":9,"title":6},"992":{"body":44,"breadcrumbs":10,"title":7},"993":{"body":34,"breadcrumbs":5,"title":2},"994":{"body":34,"breadcrumbs":3,"title":1},"995":{"body":28,"breadcrumbs":4,"title":2},"996":{"body":0,"breadcrumbs":6,"title":4},"997":{"body":32,"breadcrumbs":4,"title":2},"998":{"body":68,"breadcrumbs":4,"title":2},"999":{"body":93,"breadcrumbs":4,"title":2}},"docs":{"0":{"body":"Veyyon is a coding agent that runs in a terminal. Give it credentials for a model\\nprovider and it works inside a project: it reads files, runs tools, and edits code in\\nplace. Every step that touches the tree goes through an approval tier.","breadcrumbs":"The Veyyon handbook » The Veyyon handbook","id":"0","title":"The Veyyon handbook"},"1":{"body":"curl -fsSL https://get.veyyon.dev | sh On Windows: irm https://veyyon.dev/install.ps1 | iex The installer downloads a release binary and verifies its checksum. To build from\\nsource, clone the repository and run bun run setup && bun dev in that checkout. See Install for platforms, pinned releases, updates, and uninstall. The binary is veyyon, aliased to vey. Configuration lives under ~/.veyyon; the\\ndefault profile keeps its state in ~/.veyyon/profiles/default/agent/.","breadcrumbs":"The Veyyon handbook » Install","id":"1","title":"Install"},"10":{"body":"The edit and write tools accept hashline patches, which are addressed by content rather than by line number. Before writing, the native layer checks the patch against the current file. If they do not match, the tool fails and returns recovery context to the model instead of writing a corrupted file. See Editing and repair and The hashline edit engine.","breadcrumbs":"Design and mechanisms » Mechanisms » Hashline edits","id":"10","title":"Hashline edits"},"100":{"body":"start session (veyyon / veyyon \\"prompt\\" / resume) │ ▼\\ncompose prompt ──► turn begins │ ├─ assemble context (instructions, goal card, recent history, tools) ├─ call model ├─ dispatch / repair tool calls (edit, exec, MCP, …) ├─ approval-mode gate └─ final reply ──► turn ends (or Esc abort) │ ▼\\nappend rollout entry ──► update active leaf │ ├─ next user message ──► next turn ├─ /compact when the window is tight └─ /fork / /branch / /tree when exploring branches Interactive veyyon, a non-interactive veyyon \\"prompt\\" run, and resume paths all share this loop.\\nThe difference is who supplies the next prompt and whether a TUI is attached.","breadcrumbs":"Core concepts » Sessions, turns, and threads » Lifecycle of a run","id":"100","title":"Lifecycle of a run"},"1000":{"body":"The claude, codex, agents, opencode, claude-plugins, and github\\nskill providers are still registered, but they are not in the ambient\\nallowlist, so they never contribute skills to a session. They exist to feed the\\nonboarding import scan ( scanForeignConfig in src/discovery/import-scan.ts),\\nwhich enumerates user-level foreign skills so you can copy the ones you want into\\nthe active profile. An imported skill becomes a profile-native native skill.","breadcrumbs":"Reference » Skills » Foreign providers are import-only","id":"1000","title":"Foreign providers are import-only"},"1001":{"body":"Beyond the allowlist, loadSkills() applies these name-based controls: disabledExtensions entries with skill: ignoredSkills (exclude; glob patterns) includeSkills (include allowlist; glob patterns; empty means include all) Filter order is: not disabled by disabledExtensions, then not ignored, then included (if an include list is present). There are no per-source toggles.","breadcrumbs":"Reference » Skills » Filtering","id":"1001","title":"Filtering"},"1002":{"body":"Capability dedup already keeps the first skill per name (highest-precedence provider) extensibility/skills.ts additionally: de-duplicates identical files by realpath (symlink-safe) emits collision warnings when a later skill name conflicts keeps the convenience loadSkillsFromDir({ dir, source }) API as a thin adapter over scanSkillsFromDir","breadcrumbs":"Reference » Skills » Collision and duplicate handling","id":"1002","title":"Collision and duplicate handling"},"1003":{"body":"","breadcrumbs":"Reference » Skills » Runtime usage behavior","id":"1003","title":"Runtime usage behavior"},"1004":{"body":"System prompt construction ( src/system-prompt.ts) uses discovered skills as follows: if read tool is available: include discovered skills list in prompt, excluding skills with hide: true otherwise: omit discovered list hide: true does not disable the skill. Hidden skills are still loaded and remain reachable through skill:// and /skill: when skill commands are enabled. Task tool subagents receive the session’s discovered/provided skills list via normal session creation; there is no per-task skill pinning override.","breadcrumbs":"Reference » Skills » System prompt exposure","id":"1004","title":"System prompt exposure"},"1005":{"body":"If skills.enableSkillCommands is true, interactive mode registers one slash command per discovered skill. /skill: [args] behavior: reads the skill file directly from filePath strips frontmatter injects skill body as a custom message delivery mode follows the submission keybinding: Enter → invokes the skill on the steer queue while streaming (matches free-text Enter, which also steers), or as a normal idle prompt when the agent is not streaming Ctrl+Enter ( app.message.followUp) → invokes the skill on the followUp queue while streaming, or as a normal idle prompt when the agent is not streaming appends metadata ( [Skill directory: ], optional User: ) There is no flag, mode-selector, or frontmatter knob to override this, the keybinding is the choice, identical to how free text is routed during streaming (Enter steers, Ctrl+Enter queues a follow-up; both dispatch through #invokeSkillCommand in packages/coding-agent/src/modes/controllers/input-controller.ts).","breadcrumbs":"Reference » Skills » Interactive /skill: commands","id":"1005","title":"Interactive /skill: commands"},"1006":{"body":"src/internal-urls/skill-protocol.ts supports: skill:// → resolves to that skill’s SKILL.md skill:/// → resolves inside that skill directory skill:// URL resolution skill://pdf -> /SKILL.md skill://pdf/references/tables.md -> /references/tables.md Guards:\\n- reject absolute paths\\n- reject `..` traversal\\n- reject any resolved path escaping Resolution details: skill name must match exactly relative paths are URL-decoded absolute paths are rejected path traversal ( ..) is rejected resolved path must remain within baseDir missing files return an explicit File not found error Content type: .md => text/markdown .json => application/json everything else => text/plain No fallback search is performed for missing assets.","breadcrumbs":"Reference » Skills » skill:// URL behavior","id":"1006","title":"skill:// URL behavior"},"1007":{"body":"","breadcrumbs":"Reference » Skills » Skills vs AGENTS.md, commands, tools, hooks","id":"1007","title":"Skills vs AGENTS.md, commands, tools, hooks"},"1008":{"body":"Skills: named, optional capability packs selected by task context or explicitly requested AGENTS.md/context files: persistent instruction files loaded as context-file capability and merged by level/depth rules src/discovery/agents-md.ts specifically walks ancestor directories from cwd to discover standalone AGENTS.md files (stopping at the repo root, or home when no repo root is known), skipping files whose containing directory name starts with a dot.","breadcrumbs":"Reference » Skills » Skills vs AGENTS.md","id":"1008","title":"Skills vs AGENTS.md"},"1009":{"body":"Skills: model-readable knowledge/workflow content Slash commands: user-invoked command entry points /skill: is a convenience wrapper that injects skill text; it does not change skill discovery semantics","breadcrumbs":"Reference » Skills » Skills vs slash commands","id":"1009","title":"Skills vs slash commands"},"101":{"body":"A session is the unit of interactive work. Start one with veyyon in the repository you want to change. The session records every turn, tool call, approval, edit, and verification result. Sessions are stored as append-oriented rollout JSONL files. Each history entry has an id and a parentId, which makes the rollout a tree rather than a linear chat log. New history is appended. Header or representation maintenance may atomically rewrite the file, but it preserves every history entry.","breadcrumbs":"Core concepts » Sessions, turns, and threads » What a session is","id":"101","title":"What a session is"},"1010":{"body":"Skills: documentation/workflow content loaded through prompt context and read Custom tools: executable tool APIs callable by the model with schemas and runtime side effects","breadcrumbs":"Reference » Skills » Skills vs custom tools","id":"1010","title":"Skills vs custom tools"},"1011":{"body":"Skills: passive content Hooks: event-driven runtime interceptors that can block/modify behavior during execution","breadcrumbs":"Reference » Skills » Skills vs hooks","id":"1011","title":"Skills vs hooks"},"1012":{"body":"Put each skill in its own directory: //SKILL.md Always include explicit name and description frontmatter Keep referenced assets under the same skill directory and access with skill:///... Put every skill in the active profile’s skills/ dir; there is no nested-taxonomy or custom-directory scanning Avoid duplicate skill names; the first match wins by provider precedence (authored beats managed)","breadcrumbs":"Reference » Skills » Practical authoring guidance tied to discovery logic","id":"1012","title":"Practical authoring guidance tied to discovery logic"},"1013":{"body":"/tree opens the interactive session tree navigator. Selecting an entry moves the active leaf in the current session file and continues from that point. This is an in-file leaf move, not a new session export.","breadcrumbs":"Reference » The /tree command » /tree Command Reference","id":"1013","title":"/tree Command Reference"},"1014":{"body":"Builds a tree from current session entries ( SessionManager.getTree()) Opens TreeSelectorComponent with keyboard navigation, filters, and search On selection, calls AgentSession.navigateTree(targetId, { summarize, customInstructions }) Rebuilds visible chat from the new leaf path Optionally prefills editor text when selecting a user/custom message Primary implementation: src/slash-commands/builtin-registry.ts ( /tree, /branch command routing) src/modes/controllers/input-controller.ts (keybinding wiring, double-escape behavior) src/modes/controllers/selector-controller.ts (tree UI launch + summary prompt flow) src/modes/components/tree-selector.ts (navigation, filters, search, labels, rendering) src/session/agent-session.ts ( navigateTree leaf switching + optional summary) src/session/session-manager.ts ( getTree, branch, branchWithSummary, resetLeaf, label persistence)","breadcrumbs":"Reference » The /tree command » What /tree does","id":"1014","title":"What /tree does"},"1015":{"body":"Any of the following opens the same selector: /tree configured keybinding for the app.session.tree action double-escape on empty editor when doubleEscapeAction = \\"tree\\" (default) /branch when doubleEscapeAction = \\"tree\\" (routes to tree selector instead of user-only branch picker)","breadcrumbs":"Reference » The /tree command » How to open it","id":"1015","title":"How to open it"},"1016":{"body":"The tree is rendered from session entry parent pointers ( id / parentId). Children are sorted by timestamp ascending (older first, newer lower) Active branch (path from root to current leaf) is marked with a bullet Labels (if present) render as [label] before node text If multiple roots exist (orphaned/broken parent chains), they are shown under a virtual branching root Example tree view (active path marked with •): ├─ user: \\"Start task\\"\\n│ └─ assistant: \\"Plan\\"\\n│ ├─ • user: \\"Try approach A\\"\\n│ │ └─ • assistant: \\"A result\\"\\n│ │ └─ • [milestone] user: \\"Continue A\\"\\n│ └─ user: \\"Try approach B\\"\\n│ └─ assistant: \\"B result\\" The selector recenters around current selection and shows up to: max(5, floor(terminalHeight / 2)) rows","breadcrumbs":"Reference » The /tree command » Tree UI model","id":"1016","title":"Tree UI model"},"1017":{"body":"Up / Down: move selection (wraps) Left / Right: page up / page down Enter: select node Esc: clear search if active; otherwise close selector Ctrl+C: close selector Type: append to search query Backspace: delete search character Shift+L: edit/clear label on selected entry Ctrl+O: cycle filter forward Shift+Ctrl+O: cycle filter backward Alt+D/T/U/L/A: jump directly to specific filter mode","breadcrumbs":"Reference » The /tree command » Keybindings inside tree selector","id":"1017","title":"Keybindings inside tree selector"},"1018":{"body":"Filter modes ( TreeList): default no-tools user-only labeled-only all","breadcrumbs":"Reference » The /tree command » Filters and search semantics","id":"1018","title":"Filters and search semantics"},"1019":{"body":"Shows conversational nodes plus any entry types not explicitly suppressed. It hides these setting/bookkeeping entry types: label custom model_change thinking_level_change Other internal entry types that are not rendered specially may appear as blank rows in current code.","breadcrumbs":"Reference » The /tree command » default","id":"1019","title":"default"},"102":{"body":"A turn is one user request plus the agent loop that responds to it. The loop calls the model, dispatches any tool calls, and produces the final reply. A turn ends when the model stops or when the harness decides to stop it. While a turn runs you can steer it with Enter or queue a follow-up with ctrl+q or ctrl+enter. A queued follow-up becomes a new turn after the current one finishes. Interrupting with Esc aborts the turn and returns queued messages to the composer.","breadcrumbs":"Core concepts » Sessions, turns, and threads » What a turn is","id":"102","title":"What a turn is"},"1020":{"body":"Same as default, plus hides toolResult messages.","breadcrumbs":"Reference » The /tree command » no-tools","id":"1020","title":"no-tools"},"1021":{"body":"Only message entries where role is user.","breadcrumbs":"Reference » The /tree command » user-only","id":"1021","title":"user-only"},"1022":{"body":"Only entries that currently resolve to a label.","breadcrumbs":"Reference » The /tree command » labeled-only","id":"1022","title":"labeled-only"},"1023":{"body":"Everything in the session tree, including bookkeeping/custom entries.","breadcrumbs":"Reference » The /tree command » all","id":"1023","title":"all"},"1024":{"body":"Assistant messages that contain only tool calls (no text) are hidden by default in all filtered views unless: message is error/aborted ( stopReason not stop/ toolUse), or it is the current leaf (always kept visible)","breadcrumbs":"Reference » The /tree command » Tool-only assistant node behavior","id":"1024","title":"Tool-only assistant node behavior"},"1025":{"body":"Query is tokenized by spaces Matching is fuzzy (subsequence) and case-insensitive ( fuzzyMatch) All tokens must match (AND semantics) Searchable text includes label, role, and type-specific content (message text, branch summary text, custom type, tool command snippets, etc.)","breadcrumbs":"Reference » The /tree command » Search behavior","id":"1025","title":"Search behavior"},"1026":{"body":"navigateTree computes new leaf behavior from selected entry type:","breadcrumbs":"Reference » The /tree command » Selection outcomes (important)","id":"1026","title":"Selection outcomes (important)"},"1027":{"body":"New leaf becomes selected entry’s parentId If parent is null (root user message), leaf resets to root ( resetLeaf()) Selected message text is copied to editor for editing/resubmit","breadcrumbs":"Reference » The /tree command » Selecting user message","id":"1027","title":"Selecting user message"},"1028":{"body":"Same leaf rule as user messages ( parentId) Text content is extracted and copied to editor","breadcrumbs":"Reference » The /tree command » Selecting custom_message","id":"1028","title":"Selecting custom_message"},"1029":{"body":"New leaf becomes selected node id Editor is not prefilled","breadcrumbs":"Reference » The /tree command » Selecting non-user node (assistant/tool/summary/compaction/custom bookkeeping/etc.)","id":"1029","title":"Selecting non-user node (assistant/tool/summary/compaction/custom bookkeeping/etc.)"},"103":{"body":"At any moment, one path through the session tree is active. That path is the thread. The active leaf is the current entry at the end of that path. Branching creates siblings in the tree. /tree browses every entry, including abandoned branches. /branch copies history up to a chosen user message into a new session file. /fork duplicates the entire current session into a new file (no entry picker). There is no /clone command. The\\noriginal session is never modified.","breadcrumbs":"Core concepts » Sessions, turns, and threads » Threads and the active leaf","id":"103","title":"Threads and the active leaf"},"1030":{"body":"No-op; selector closes with “Already at this point” Selection decision (simplified): selected node │ ├─ is current leaf? ── yes ──> close selector (no-op) │ ├─ is user/custom_message? ── yes ──> leaf := parentId (or resetLeaf for root) │ + prefill editor text │ └─ otherwise ──> leaf := selected node id + no editor prefill","breadcrumbs":"Reference » The /tree command » Selecting current leaf","id":"1030","title":"Selecting current leaf"},"1031":{"body":"Summary prompt is controlled by branchSummary.enabled (default: false). When enabled, after picking a node the UI prompts: No summary Summarize Summarize with custom prompt Flow details: Escape in summary prompt reopens tree selector Custom prompt cancellation returns to summary choice loop During summarization, UI shows loader and binds Esc to abortBranchSummary() If summarization aborts, tree selector reopens and no move is applied navigateTree internals: Collects abandoned-branch entries from old leaf to common ancestor Emits session_before_tree (extensions can cancel or inject summary) Uses default summarizer only if requested and needed Applies move with: branchWithSummary(...) when summary exists branch(newLeafId) for non-root move without summary resetLeaf() for root move without summary Replaces agent conversation with rebuilt session context Emits session_tree Note: if user requests summary but there is nothing to summarize, navigation proceeds without creating a summary entry.","breadcrumbs":"Reference » The /tree command » Summary-on-switch flow","id":"1031","title":"Summary-on-switch flow"},"1032":{"body":"Label edits in tree UI call appendLabelChange(targetId, label). non-empty label sets/updates resolved label empty label clears it labels are stored as append-only label entries tree nodes display resolved label state, not raw label-entry history","breadcrumbs":"Reference » The /tree command » Labels","id":"1032","title":"Labels"},"1033":{"body":"Operation Scope Result /tree Current session file Moves leaf to selected point (same file) /branch Usually current session file -> new session file By default branches from selected user message into a new session file; if doubleEscapeAction = \\"tree\\", /branch opens tree navigation UI instead /fork Whole current session Duplicates session into a new persisted session file /resume Session list Switches to another session file Key distinction: /tree is a navigation/repositioning tool inside one session file. /branch, /fork, and /resume all change session-file context.","breadcrumbs":"Reference » The /tree command » /tree vs adjacent operations","id":"1033","title":"/tree vs adjacent operations"},"1034":{"body":"","breadcrumbs":"Reference » The /tree command » Operator workflows","id":"1034","title":"Operator workflows"},"1035":{"body":"/tree search/select earlier user message choose No summary (or summarize if needed) edit prefilled text in editor submit Effect: new branch grows from selected point within same session file.","breadcrumbs":"Reference » The /tree command » Re-run from an earlier user prompt without losing current branch","id":"1035","title":"Re-run from an earlier user prompt without losing current branch"},"1036":{"body":"enable branchSummary.enabled /tree and select target node choose Summarize (or custom prompt) Effect: a branch_summary entry is appended at the target position before continuing.","breadcrumbs":"Reference » The /tree command » Leave current branch with context breadcrumb","id":"1036","title":"Leave current branch with context breadcrumb"},"1037":{"body":"/tree press Alt+A (all) search for model, thinking, custom, or labels Effect: inspect full internal timeline, not just conversational nodes.","breadcrumbs":"Reference » The /tree command » Investigate hidden bookkeeping entries","id":"1037","title":"Investigate hidden bookkeeping entries"},"1038":{"body":"/tree move to entry Shift+L and set label later use Alt+L ( labeled-only) to jump quickly Effect: fast navigation among durable branch landmarks.","breadcrumbs":"Reference » The /tree command » Bookmark pivot points for later jumps","id":"1038","title":"Bookmark pivot points for later jumps"},"1039":{"body":"RPC mode runs the coding agent as a newline-delimited JSON protocol over stdio. stdin: commands ( RpcCommand), extension UI responses, and host-tool updates/results stdout: a ready frame, command responses ( RpcResponse), session/agent events, extension UI requests, host-tool requests/cancellations Primary implementation: src/modes/rpc/rpc-mode.ts src/modes/rpc/rpc-types.ts src/session/agent-session.ts packages/agent/src/agent.ts packages/agent/src/agent-loop.ts","breadcrumbs":"Reference » The RPC surface » RPC Protocol Reference","id":"1039","title":"RPC Protocol Reference"},"104":{"body":"Models have a finite token window. As a session grows, the raw transcript may no longer fit. Veyyon compacts history into a smaller, information-preserving summary instead of truncating it. Compaction preserves the goal card, active user instructions, recent turns, and a deterministic working-set of files touched so a resumed session does not require the full raw transcript. Prefer /compact when you need a summary to retain state. Prefer the /new command when prior transcript is no longer useful and you want a clean session without summarization. See Slash commands.","breadcrumbs":"Core concepts » Sessions, turns, and threads » Context pressure and compaction","id":"104","title":"Context pressure and compaction"},"1040":{"body":"veyyon --mode rpc [regular CLI options] Behavior notes: @file CLI arguments are rejected in RPC mode. RPC mode disables automatic session title generation by default to avoid an extra model call. RPC mode host-defaults a small set of settings so embedders inherit Veyyon’s neutral defaults: subagent.isolation.mode/ merge/ commits, subagent.delegation, subagent.batch, subagent.maxConcurrency, subagent.maxNestedSpawnDepth, subagent.agents, memory.backend, and memories.enabled, plus async.enabled, async.maxJobs, bash.autoBackground.enabled, and bash.autoBackground.thresholdMs. The default is only applied when the path is unset: any explicit configuration (caller overrides, --config overlays, or the profile config.yml) is preserved. todo.* settings are always caller-controlled in protocol modes. The process reads stdin as JSONL ( readJsonl(Bun.stdin.stream())). At startup it writes { \\"type\\": \\"ready\\" } before processing commands. When stdin closes, pending host-tool calls and host-URI requests are rejected and the process exits with code 0. Responses/events are written as one JSON object per line.","breadcrumbs":"Reference » The RPC surface » Startup","id":"1040","title":"Startup"},"1041":{"body":"Each frame is a single JSON object followed by \\\\n. There is no envelope beyond the object shape itself.","breadcrumbs":"Reference » The RPC surface » Transport and Framing","id":"1041","title":"Transport and Framing"},"1042":{"body":"Ready frame ( { type: \\"ready\\" }) RpcResponse ( { type: \\"response\\", ... }) AgentSessionEvent objects ( agent_start, message_update, etc.) RpcExtensionUIRequest ( { type: \\"extension_ui_request\\", ... }) Host tool requests/cancellations ( host_tool_call, host_tool_cancel) Host URI requests/cancellations ( host_uri_request, host_uri_cancel) Extension errors ( { type: \\"extension_error\\", extensionPath, event, error }) Available-commands updates ( { type: \\"available_commands_update\\", commands }), emitted at startup and whenever command metadata changes Prompt lifecycle hints ( { type: \\"prompt_result\\", id?, agentInvoked }) for scheduled prompts that later resolve without invoking the agent Subagent frames ( subagent_lifecycle, subagent_progress, subagent_event), gated by set_subagent_subscription Builtin slash-command side channels ( command_output, session_info_update, config_update)","breadcrumbs":"Reference » The RPC surface » Outbound frame categories (stdout)","id":"1042","title":"Outbound frame categories (stdout)"},"1043":{"body":"RpcCommand RpcExtensionUIResponse ( { type: \\"extension_ui_response\\", ... }) Host tool updates/results ( host_tool_update, host_tool_result) Host URI results ( host_uri_result)","breadcrumbs":"Reference » The RPC surface » Inbound frame categories (stdin)","id":"1043","title":"Inbound frame categories (stdin)"},"1044":{"body":"All commands accept optional id?: string. If provided, normal command responses echo the same id. RpcClient relies on this for pending-request resolution. Important edge behavior from runtime: Unknown command responses are emitted with id: undefined (even if the request had an id). Parse/handler exceptions in the input loop emit command: \\"parse\\" with id: undefined. prompt and abort_and_prompt return immediate success, then may emit a later error response with the same id if async prompt scheduling fails. prompt success responses may include data.agentInvoked. false means the prompt completed locally without an agent turn; true means the prompt produced agent lifecycle events; omitted means the host must rely on session events for completion. abort_and_prompt does not currently emit data.agentInvoked or prompt_result; hosts should treat it as the legacy abort-then-schedule path and rely on session events or same-id scheduling errors.","breadcrumbs":"Reference » The RPC surface » Request/Response Correlation","id":"1044","title":"Request/Response Correlation"},"1045":{"body":"RpcCommand is defined in src/modes/rpc/rpc-types.ts:","breadcrumbs":"Reference » The RPC surface » Command Schema (canonical)","id":"1045","title":"Command Schema (canonical)"},"1046":{"body":"{ id?, type: \\"prompt\\", message: string, images?: ImageContent[], streamingBehavior?: \\"steer\\" | \\"followUp\\" } { id?, type: \\"steer\\", message: string, images?: ImageContent[] } { id?, type: \\"follow_up\\", message: string, images?: ImageContent[] } { id?, type: \\"abort\\" } { id?, type: \\"abort_and_prompt\\", message: string, images?: ImageContent[] } { id?, type: \\"new_session\\", parentSession?: string }","breadcrumbs":"Reference » The RPC surface » Prompting","id":"1046","title":"Prompting"},"1047":{"body":"{ id?, type: \\"get_state\\" } { id?, type: \\"get_available_commands\\" } { id?, type: \\"set_todos\\", phases: TodoPhase[] } { id?, type: \\"set_host_tools\\", tools: RpcHostToolDefinition[] } { id?, type: \\"set_host_uri_schemes\\", schemes: RpcHostUriSchemeDefinition[] } { id?, type: \\"set_subagent_subscription\\", level: \\"off\\" | \\"progress\\" | \\"events\\" } { id?, type: \\"get_subagents\\" } { id?, type: \\"get_subagent_messages\\", subagentId?: string, sessionFile?: string, fromByte?: number }","breadcrumbs":"Reference » The RPC surface » State","id":"1047","title":"State"},"1048":{"body":"{ id?, type: \\"set_model\\", provider: string, modelId: string } { id?, type: \\"cycle_model\\" } { id?, type: \\"get_available_models\\" }","breadcrumbs":"Reference » The RPC surface » Model","id":"1048","title":"Model"},"1049":{"body":"{ id?, type: \\"set_thinking_level\\", level: ThinkingLevel } { id?, type: \\"cycle_thinking_level\\" }","breadcrumbs":"Reference » The RPC surface » Thinking","id":"1049","title":"Thinking"},"105":{"body":"Session history lives in one layer: the JSONL rollout. Every event is one line: a user message, an agent response, a tool call, a compaction, a goal update, or a branch summary. The rollout is the only source of truth. Listing and resume read it through the active storage backend, including indexed Redis and SQL storage. A non-empty rollout without a valid header is rejected without changing its bytes; malformed later records are skipped with an operator-visible path, line, byte, and shape warning. Goal cards persist as rollout entries. There is no separate session-state database.","breadcrumbs":"Core concepts » Sessions, turns, and threads » The rollout","id":"105","title":"The rollout"},"1050":{"body":"{ id?, type: \\"set_steering_mode\\", mode: \\"all\\" | \\"one-at-a-time\\" } { id?, type: \\"set_follow_up_mode\\", mode: \\"all\\" | \\"one-at-a-time\\" } { id?, type: \\"set_interrupt_mode\\", mode: \\"immediate\\" | \\"wait\\" }","breadcrumbs":"Reference » The RPC surface » Queue modes","id":"1050","title":"Queue modes"},"1051":{"body":"{ id?, type: \\"compact\\", customInstructions?: string } { id?, type: \\"set_auto_compaction\\", enabled: boolean }","breadcrumbs":"Reference » The RPC surface » Compaction","id":"1051","title":"Compaction"},"1052":{"body":"{ id?, type: \\"set_auto_retry\\", enabled: boolean } { id?, type: \\"abort_retry\\" }","breadcrumbs":"Reference » The RPC surface » Retry","id":"1052","title":"Retry"},"1053":{"body":"{ id?, type: \\"bash\\", command: string } { id?, type: \\"abort_bash\\" } bash is dispatched concurrently: the RPC server continues reading commands\\nwhile the shell command runs, so abort_bash (or any other command) sent\\nduring a long-running bash is handled without waiting for it to finish on\\nits own. The bash response is emitted when the command completes; hosts\\ncorrelate it via id. Ordering across concurrent commands is not guaranteed\\n, clients MUST match responses on id, not on emission order.","breadcrumbs":"Reference » The RPC surface » Bash","id":"1053","title":"Bash"},"1054":{"body":"{ id?, type: \\"get_session_stats\\" } { id?, type: \\"export_html\\", outputPath?: string } { id?, type: \\"switch_session\\", sessionPath: string } { id?, type: \\"branch\\", entryId: string } { id?, type: \\"get_branch_messages\\" } { id?, type: \\"get_last_assistant_text\\" } { id?, type: \\"set_session_name\\", name: string } { id?, type: \\"handoff\\", customInstructions?: string }","breadcrumbs":"Reference » The RPC surface » Session","id":"1054","title":"Session"},"1055":{"body":"{ id?, type: \\"get_messages\\" }","breadcrumbs":"Reference » The RPC surface » Messages","id":"1055","title":"Messages"},"1056":{"body":"{ id?, type: \\"get_login_providers\\" } { id?, type: \\"login\\", providerId: string }","breadcrumbs":"Reference » The RPC surface » Login","id":"1056","title":"Login"},"1057":{"body":"All command results use RpcResponse: Success: { id?, type: \\"response\\", command: , success: true, data?: ... } Failure: { id?, type: \\"response\\", command: string, success: false, error: string } Data payloads are command-specific and defined in rpc-types.ts.","breadcrumbs":"Reference » The RPC surface » Response Schema","id":"1057","title":"Response Schema"},"1058":{"body":"prompt is acknowledged after the command is accepted, not after a model turn finishes: { \\"id\\": \\"req_1\\", \\"type\\": \\"response\\", \\"command\\": \\"prompt\\", \\"success\\": true, \\"data\\": { \\"agentInvoked\\": false }\\n} data.agentInvoked: false is a completion signal for local-only prompts, including slash commands that produce output without starting an agent turn. data.agentInvoked: true means the prompt produced agent lifecycle events; those events can be emitted before or after the prompt response depending on the command path. Older runtimes may omit data; hosts should then rely on agent_end, custom message completion, or prompt_result. prompt_result is emitted when a prompt was accepted immediately but later resolves as local-only: { \\"type\\": \\"prompt_result\\", \\"id\\": \\"req_1\\", \\"agentInvoked\\": false } Local-only slash commands may emit command_output frames before completing via data.agentInvoked: false or a later prompt_result. They do not emit agent_end.","breadcrumbs":"Reference » The RPC surface » prompt payload","id":"1058","title":"prompt payload"},"1059":{"body":"{ \\"model\\": { \\"provider\\": \\"...\\", \\"id\\": \\"...\\" }, \\"thinkingLevel\\": \\"off|minimal|low|medium|high|xhigh|max\\", \\"isStreaming\\": false, \\"isCompacting\\": false, \\"steeringMode\\": \\"all|one-at-a-time\\", \\"followUpMode\\": \\"all|one-at-a-time\\", \\"interruptMode\\": \\"immediate|wait\\", \\"sessionFile\\": \\"...\\", \\"sessionId\\": \\"...\\", \\"sessionName\\": \\"...\\", \\"autoCompactionEnabled\\": true, \\"messageCount\\": 0, \\"queuedMessageCount\\": 0, \\"todoPhases\\": [ { \\"id\\": \\"phase-1\\", \\"name\\": \\"Todos\\", \\"tasks\\": [ { \\"id\\": \\"task-1\\", \\"content\\": \\"Map the tool surface\\", \\"status\\": \\"in_progress\\" } ] } ], \\"systemPrompt\\": [\\"...\\"], \\"dumpTools\\": [ { \\"name\\": \\"read\\", \\"description\\": \\"Read files and URLs\\", \\"parameters\\": {} } ], \\"contextUsage\\": { \\"tokens\\": 1100, \\"contextWindow\\": 200000, \\"percent\\": 0.55 }\\n}","breadcrumbs":"Reference » The RPC surface » get_state payload","id":"1059","title":"get_state payload"},"106":{"body":"A session contains one or more threads and stores them on disk. A thread is a path through the session’s tree of turns. A turn is one step on that path. The rollout is the append-only log that holds every turn, branch, and system event. The goal card is a separate context slot that contains the current objective across turns and compactions. See Goal state and long sessions.","breadcrumbs":"Core concepts » Sessions, turns, and threads » How the pieces relate","id":"106","title":"How the pieces relate"},"1060":{"body":"Replaces the in-memory todo state for the current session and returns the normalized phase list: { \\"id\\": \\"req_2\\", \\"type\\": \\"set_todos\\", \\"phases\\": [ { \\"id\\": \\"phase-1\\", \\"name\\": \\"Evaluation\\", \\"tasks\\": [ { \\"id\\": \\"task-1\\", \\"content\\": \\"Map the read tool surface\\", \\"status\\": \\"in_progress\\" }, { \\"id\\": \\"task-2\\", \\"content\\": \\"Exercise edit operations\\", \\"status\\": \\"pending\\" } ] } ]\\n} This is useful for hosts that want to pre-seed a plan before the first prompt.","breadcrumbs":"Reference » The RPC surface » set_todos payload","id":"1060","title":"set_todos payload"},"1061":{"body":"Replaces the current set of host-owned tools that the RPC server may call back\\ninto over stdio: { \\"id\\": \\"req_3\\", \\"type\\": \\"set_host_tools\\", \\"tools\\": [ { \\"name\\": \\"echo_host\\", \\"label\\": \\"Echo Host\\", \\"description\\": \\"Echo a value from the embedding host\\", \\"parameters\\": { \\"type\\": \\"object\\", \\"properties\\": { \\"message\\": { \\"type\\": \\"string\\" } }, \\"required\\": [\\"message\\"], \\"additionalProperties\\": false } } ]\\n} The response payload is: { \\"toolNames\\": [\\"echo_host\\"]\\n} These tools are added to the active session tool registry before the next model\\ncall. Re-sending set_host_tools replaces the previous host-owned set.","breadcrumbs":"Reference » The RPC surface » set_host_tools payload","id":"1061","title":"set_host_tools payload"},"1062":{"body":"Replaces the current set of host-owned URL schemes the RPC server should\\ndispatch reads/writes through: { \\"id\\": \\"req_4\\", \\"type\\": \\"set_host_uri_schemes\\", \\"schemes\\": [ { \\"scheme\\": \\"db\\", \\"description\\": \\"Virtual db row files\\", \\"writable\\": true, \\"immutable\\": false } ]\\n} The response payload is: { \\"schemes\\": [\\"db\\"]\\n} Schemes are case-insensitive on the wire and normalized to lowercase before\\nthe response is sent. Re-sending set_host_uri_schemes replaces the entire\\nprevious set, schemes missing from the new list are unregistered.","breadcrumbs":"Reference » The RPC surface » set_host_uri_schemes payload","id":"1062","title":"set_host_uri_schemes payload"},"1063":{"body":"RPC mode forwards AgentSessionEvent objects from AgentSession.subscribe(...). Common event types: agent_start, agent_end turn_start, turn_end message_start, message_update, message_end tool_execution_start, tool_execution_update, tool_execution_end auto_compaction_start, auto_compaction_end auto_retry_start, auto_retry_end ttsr_triggered todo_reminder todo_auto_clear Extension runner errors are emitted separately as: { \\"type\\": \\"extension_error\\", \\"extensionPath\\": \\"...\\", \\"event\\": \\"...\\", \\"error\\": \\"...\\"\\n} message_update includes streaming deltas in assistantMessageEvent (text/thinking/toolcall deltas).","breadcrumbs":"Reference » The RPC surface » Event Stream Schema","id":"1063","title":"Event Stream Schema"},"1064":{"body":"This is the most important operational behavior.","breadcrumbs":"Reference » The RPC surface » Prompt/Queue Concurrency and Ordering","id":"1064","title":"Prompt/Queue Concurrency and Ordering"},"1065":{"body":"prompt and abort_and_prompt are acknowledged immediately: { \\"id\\": \\"req_1\\", \\"type\\": \\"response\\", \\"command\\": \\"prompt\\", \\"success\\": true } That means: command acceptance != run completion agent turns complete via agent_end local-only prompts complete via data.agentInvoked: false on the response or via a later prompt_result","breadcrumbs":"Reference » The RPC surface » Immediate ack vs completion","id":"1065","title":"Immediate ack vs completion"},"1066":{"body":"AgentSession.prompt() requires streamingBehavior during active streaming: \\"steer\\" => queued steering message (interrupt path) \\"followUp\\" => queued follow-up message (post-turn path) If omitted during streaming, prompt fails.","breadcrumbs":"Reference » The RPC surface » While streaming","id":"1066","title":"While streaming"},"1067":{"body":"From packages/agent/src/agent.ts defaults: steeringMode: \\"one-at-a-time\\" followUpMode: \\"one-at-a-time\\" interruptMode: \\"immediate\\"","breadcrumbs":"Reference » The RPC surface » Queue defaults","id":"1067","title":"Queue defaults"},"1068":{"body":"set_steering_mode / set_follow_up_mode \\"one-at-a-time\\": dequeue one queued message per turn \\"all\\": dequeue entire queue at once set_interrupt_mode \\"immediate\\": tool execution checks steering between tool calls; pending steering can abort remaining tool calls in the turn \\"wait\\": defer steering until turn completion","breadcrumbs":"Reference » The RPC surface » Mode semantics","id":"1068","title":"Mode semantics"},"1069":{"body":"Extensions in RPC mode use request/response UI frames.","breadcrumbs":"Reference » The RPC surface » Extension UI Sub-Protocol","id":"1069","title":"Extension UI Sub-Protocol"},"107":{"body":"For session commands and storage, see Sessions. For branching, forking, and cloning, see Session branching. For plan mode and goal tracking, see Plan mode and goals. For how compaction works, see Compaction and project memory. For goal state and long sessions, see Goal state and long sessions. For contributor-facing internals, see Session and turn internals.","breadcrumbs":"Core concepts » Sessions, turns, and threads » Where the details live","id":"107","title":"Where the details live"},"1070":{"body":"RpcExtensionUIRequest ( type: \\"extension_ui_request\\") methods: select, confirm, input, editor, cancel notify, setStatus, setWidget, setTitle, set_editor_text open_url (emitted by RPC login flows) Runtime note: Automatic session title generation is disabled in RPC mode, and setTitle UI\\nrequests are also suppressed by default because most hosts do not have a\\nmeaningful terminal-title surface. Set VEYYON_RPC_EMIT_TITLE=1 to opt back in to\\nthe UI event only. Example: { \\"type\\": \\"extension_ui_request\\", \\"id\\": \\"123\\", \\"method\\": \\"confirm\\", \\"title\\": \\"Confirm\\", \\"message\\": \\"Continue?\\", \\"timeout\\": 30000\\n}","breadcrumbs":"Reference » The RPC surface » Outbound request","id":"1070","title":"Outbound request"},"1071":{"body":"RpcExtensionUIResponse ( type: \\"extension_ui_response\\"): { type: \\"extension_ui_response\\", id: string, value: string } { type: \\"extension_ui_response\\", id: string, confirmed: boolean } { type: \\"extension_ui_response\\", id: string, cancelled: true, timedOut?: boolean } If a dialog has a timeout, RPC mode resolves to a default value when timeout/abort fires.","breadcrumbs":"Reference » The RPC surface » Inbound response","id":"1071","title":"Inbound response"},"1072":{"body":"RPC hosts can expose custom tools to the agent by sending set_host_tools, then\\nserving execution requests over the same transport.","breadcrumbs":"Reference » The RPC surface » Host Tool Sub-Protocol","id":"1072","title":"Host Tool Sub-Protocol"},"1073":{"body":"When the agent wants the host to execute one of those tools, RPC mode emits: { \\"type\\": \\"host_tool_call\\", \\"id\\": \\"host_1\\", \\"toolCallId\\": \\"toolu_123\\", \\"toolName\\": \\"echo_host\\", \\"arguments\\": { \\"message\\": \\"hello\\" }\\n} If the tool execution is later aborted, RPC mode emits: { \\"type\\": \\"host_tool_cancel\\", \\"id\\": \\"host_cancel_1\\", \\"targetId\\": \\"host_1\\"\\n}","breadcrumbs":"Reference » The RPC surface » Outbound request","id":"1073","title":"Outbound request"},"1074":{"body":"Hosts can optionally stream progress: { \\"type\\": \\"host_tool_update\\", \\"id\\": \\"host_1\\", \\"partialResult\\": { \\"content\\": [{ \\"type\\": \\"text\\", \\"text\\": \\"working\\" }] }\\n} Completion uses: { \\"type\\": \\"host_tool_result\\", \\"id\\": \\"host_1\\", \\"result\\": { \\"content\\": [{ \\"type\\": \\"text\\", \\"text\\": \\"done\\" }] }\\n} Set top-level isError: true on host_tool_result to reject the pending host tool call and surface the returned text content as a tool error.","breadcrumbs":"Reference » The RPC surface » Inbound updates and completion","id":"1074","title":"Inbound updates and completion"},"1075":{"body":"RPC hosts can also own custom URL schemes (virtual files). After set_host_uri_schemes, every read of ://… and write of ://… (when registered as writable) is bounced back to the host\\nover the same transport.","breadcrumbs":"Reference » The RPC surface » Host URI Sub-Protocol","id":"1075","title":"Host URI Sub-Protocol"},"1076":{"body":"When a session tool resolves a host-owned URL, RPC mode emits: { \\"type\\": \\"host_uri_request\\", \\"id\\": \\"uri_1\\", \\"operation\\": \\"read\\", \\"url\\": \\"db://users/42\\"\\n} Writes look the same with \\"operation\\": \\"write\\" and an additional \\"content\\": \\"...\\" field containing the full replacement bytes. If the request is later aborted (caller cancels, session ends), RPC mode\\nemits: { \\"type\\": \\"host_uri_cancel\\", \\"id\\": \\"uri_cancel_1\\", \\"targetId\\": \\"uri_1\\"\\n}","breadcrumbs":"Reference » The RPC surface » Outbound request","id":"1076","title":"Outbound request"},"1077":{"body":"For successful reads: { \\"type\\": \\"host_uri_result\\", \\"id\\": \\"uri_1\\", \\"content\\": \\"id=42\\\\nname=Alice\\\\n\\", \\"contentType\\": \\"text/plain\\", \\"notes\\": [\\"fresh from cache\\"], \\"immutable\\": false\\n} For successful writes, omit content: { \\"type\\": \\"host_uri_result\\", \\"id\\": \\"uri_1\\" } To reject the request, set isError: true and either populate error with\\na message or fall back to content for textual error surfacing: { \\"type\\": \\"host_uri_result\\", \\"id\\": \\"uri_1\\", \\"isError\\": true, \\"error\\": \\"row 42 not found\\"\\n}","breadcrumbs":"Reference » The RPC surface » Inbound result","id":"1077","title":"Inbound result"},"1078":{"body":"The agent’s edit tool does not target host URIs. Hosts that want to\\nmutate virtual files expose write and let the model use the write tool\\nwith replacement content. Schemes are global to the process; set_host_uri_schemes replaces the\\nprevious set, unregistering anything not in the new list. Schemes are normalized to lowercase before registration.","breadcrumbs":"Reference » The RPC surface » Constraints","id":"1078","title":"Constraints"},"1079":{"body":"","breadcrumbs":"Reference » The RPC surface » Error Model and Recoverability","id":"1079","title":"Error Model and Recoverability"},"108":{"body":"Every tool the model attempts to run passes through one gate: the approval mode. The\\napproval mode sets whether a tool runs on its own or waits for you to say yes. You\\nset it once in config, and you can change it for a single run from the command line. One setting controls this: tools.approvalMode. Nothing else confines what a command\\ncan do once it runs. Veyyon does not wrap commands in an operating-system sandbox\\n(Landlock, seccomp, Seatbelt, or bubblewrap), so the approval mode is the boundary. Treat\\nit as the boundary.","breadcrumbs":"Core concepts » Permission model » Permission model","id":"108","title":"Permission model"},"1080":{"body":"Failures are success: false with string error. { \\"id\\": \\"req_2\\", \\"type\\": \\"response\\", \\"command\\": \\"set_model\\", \\"success\\": false, \\"error\\": \\"Model not found: provider/model\\"\\n}","breadcrumbs":"Reference » The RPC surface » Command-level failures","id":"1080","title":"Command-level failures"},"1081":{"body":"Most command failures are recoverable; process remains alive. Malformed JSONL / parse-loop exceptions emit a parse error response and continue reading subsequent lines. Empty set_session_name is rejected ( Session name cannot be empty). Extension UI responses with unknown id are ignored. Process termination conditions are stdin close or explicit extension-triggered shutdown after the current command.","breadcrumbs":"Reference » The RPC surface » Recoverability expectations","id":"1081","title":"Recoverability expectations"},"1082":{"body":"","breadcrumbs":"Reference » The RPC surface » Compact Command Flows","id":"1082","title":"Compact Command Flows"},"1083":{"body":"stdin: { \\"id\\": \\"req_1\\", \\"type\\": \\"prompt\\", \\"message\\": \\"Summarize this repo\\" } stdout sequence (typical): { \\"id\\": \\"req_1\\", \\"type\\": \\"response\\", \\"command\\": \\"prompt\\", \\"success\\": true }\\n{ \\"type\\": \\"agent_start\\" }\\n{ \\"type\\": \\"message_update\\", \\"assistantMessageEvent\\": { \\"type\\": \\"text_delta\\", \\"delta\\": \\"...\\" }, \\"message\\": { \\"role\\": \\"assistant\\", \\"content\\": [] } }\\n{ \\"type\\": \\"agent_end\\", \\"messages\\": [] }","breadcrumbs":"Reference » The RPC surface » 1) Prompt and stream","id":"1083","title":"1) Prompt and stream"},"1084":{"body":"stdin: { \\"id\\": \\"req_2\\", \\"type\\": \\"prompt\\", \\"message\\": \\"Also include risks\\", \\"streamingBehavior\\": \\"followUp\\"\\n}","breadcrumbs":"Reference » The RPC surface » 2) Prompt during streaming with explicit queue policy","id":"1084","title":"2) Prompt during streaming with explicit queue policy"},"1085":{"body":"stdin: { \\"id\\": \\"q1\\", \\"type\\": \\"get_state\\" }\\n{ \\"id\\": \\"q2\\", \\"type\\": \\"set_steering_mode\\", \\"mode\\": \\"all\\" }\\n{ \\"id\\": \\"q3\\", \\"type\\": \\"set_interrupt_mode\\", \\"mode\\": \\"wait\\" }","breadcrumbs":"Reference » The RPC surface » 3) Inspect and tune queue behavior","id":"1085","title":"3) Inspect and tune queue behavior"},"1086":{"body":"stdout: { \\"type\\": \\"extension_ui_request\\", \\"id\\": \\"ui_7\\", \\"method\\": \\"input\\", \\"title\\": \\"Branch name\\", \\"placeholder\\": \\"feature/...\\"\\n} stdin: { \\"type\\": \\"extension_ui_response\\", \\"id\\": \\"ui_7\\", \\"value\\": \\"feature/rpc-host\\" }","breadcrumbs":"Reference » The RPC surface » 4) Extension UI round trip","id":"1086","title":"4) Extension UI round trip"},"1087":{"body":"src/modes/rpc/rpc-client.ts is a convenience wrapper, not the protocol definition. Current helper characteristics: Spawns bun --mode rpc Correlates responses by generated req_ ids Dispatches recognized core AgentEvent types to listeners Supports host-owned custom tools via setCustomTools() and automatic handling of host_tool_call / host_tool_cancel Wraps common protocol commands including OAuth getLoginProviders() / login(...); use raw protocol frames for any surface not wrapped by the helper. Use raw protocol frames if you need complete surface coverage.","breadcrumbs":"Reference » The RPC surface » Notes on RpcClient helper","id":"1087","title":"Notes on RpcClient helper"},"1088":{"body":"The SDK is the in-process integration surface for @veyyon/coding-agent.\\nUse it when you want direct access to agent state, event streaming, tool wiring, and session control from your own Bun/Node process. If you need cross-language/process isolation, use RPC mode instead.","breadcrumbs":"Reference » The SDK » SDK","id":"1088","title":"SDK"},"1089":{"body":"Veyyon ships through GitHub only. Its packages are not on npm or any other\\nregistry, and they cannot be: they depend on each other with Bun’s workspace:* and catalog: protocols, which resolve only inside a checkout. bun add @veyyon/coding-agent fails, and so does every other registry install. You consume the SDK from a checkout, linked into your project. Clone the repository and install its dependencies: git clone https://github.com/santhreal/veyyon.git\\ncd veyyon\\nbun install If you already have a Veyyon checkout, use it instead of cloning again. The\\ninstaller does not create one, so this is a clone you made yourself. Register the package with Bun, from the checkout: bun --cwd=packages/coding-agent link Then link it into your own project: cd /path/to/your-project\\nbun link @veyyon/coding-agent Your project now resolves @veyyon/coding-agent to the checkout. To move to a\\nnewer version, update the checkout ( git pull && bun install); the link keeps\\npointing at it.","breadcrumbs":"Reference » The SDK » Installation","id":"1089","title":"Installation"},"109":{"body":"Every tool belongs to one of three tiers, ordered by how much it can change: read looks but does not touch: read, grep, glob, and directory listing. write changes files: edit and write. exec runs commands: bash and anything else that executes a program. A mode approves whole tiers, not individual tools. That is why the tiers come first: once\\nyou know which tier a tool is in, the mode determines whether it runs.","breadcrumbs":"Core concepts » Permission model » Tool tiers","id":"109","title":"Tool tiers"},"1090":{"body":"@veyyon/coding-agent exports the SDK APIs from the package root (and also via @veyyon/coding-agent/sdk). Core exports for embedders: createAgentSession SessionManager Settings AuthStorage ModelRegistry discoverAuthStorage Discovery helpers ( discoverExtensions, discoverSkills, discoverContextFiles, discoverPromptTemplates, discoverSlashCommands, discoverCustomTSCommands, discoverMCPServers) Tool factory surface ( createTools, BUILTIN_TOOLS, tool classes)","breadcrumbs":"Reference » The SDK » Entry points","id":"1090","title":"Entry points"},"1091":{"body":"import { createAgentSession } from \\"@veyyon/coding-agent\\"; const { session, modelFallbackMessage } = await createAgentSession(); if (modelFallbackMessage) { process.stderr.write(`${modelFallbackMessage}\\\\n`);\\n} const unsubscribe = session.subscribe((event) => { if ( event.type === \\"message_update\\" && event.assistantMessageEvent.type === \\"text_delta\\" ) { process.stdout.write(event.assistantMessageEvent.delta); }\\n}); await session.prompt(\\"Summarize this repository in 3 bullets.\\");\\nunsubscribe();\\nawait session.dispose();","breadcrumbs":"Reference » The SDK » Quick start (auto-discovery defaults)","id":"1091","title":"Quick start (auto-discovery defaults)"},"1092":{"body":"createAgentSession() follows “provide to override, omit to discover”. If omitted, it resolves: cwd: getProjectDir() agentDir: active profile agent dir via getAgentDir() (default ~/.veyyon/profiles/default/agent; named profile ~/.veyyon/profiles//agent) globalConfigRoot: cross-profile vault/key root via getGlobalConfigRootDir() (normally ~/.veyyon) authStorage: discoverAuthStorage(agentDir) modelRegistry: new ModelRegistry(authStorage) + background refreshInBackground() when the registry is not provided settings: await Settings.init({ cwd, agentDir }) sessionManager: SessionManager.create(cwd) (file-backed) skills/context files/prompt templates/slash commands/extensions/custom TS commands built-in tools via createTools(...) MCP tools (enabled by default; Exa MCP servers are folded into native Exa integration, and browser automation MCP servers are filtered when the built-in browser tool is enabled) LSP integration (enabled by default) eventBus: new EventBus() unless supplied","breadcrumbs":"Reference » The SDK » What createAgentSession() discovers by default","id":"1092","title":"What createAgentSession() discovers by default"},"1093":{"body":"Typically you must provide only what you want to control: Must provide: nothing for a minimal session Usually provide explicitly in embedders: sessionManager (if you need in-memory or custom location) authStorage + modelRegistry (if you own credential/model lifecycle) model or modelPattern (if deterministic model selection matters) settings (if you need isolated/test config) For a multi-tenant, test, or otherwise isolated SDK host, set globalConfigRoot to a private\\ndirectory so the session cannot read or write the host user’s cross-profile secret vault or vault\\nkey. The override affects vault/key resolution only; agentDir continues to control profile-local\\nconfiguration. When omitted, globalConfigRoot defaults to getGlobalConfigRootDir() exactly as it\\ndoes for the CLI. import { chmod, mkdtemp } from \\"node:fs/promises\\";\\nimport { tmpdir } from \\"node:os\\";\\nimport path from \\"node:path\\";\\nimport { createAgentSession } from \\"@veyyon/coding-agent\\"; const privateVaultRoot = await mkdtemp(path.join(tmpdir(), \\"veyyon-sdk-\\"));\\nawait chmod(privateVaultRoot, 0o700); const { session } = await createAgentSession({ globalConfigRoot: privateVaultRoot, // Other isolated-host options...\\n});","breadcrumbs":"Reference » The SDK » Required vs optional inputs","id":"1093","title":"Required vs optional inputs"},"1094":{"body":"AgentSession always uses a SessionManager; behavior depends on which factory you use.","breadcrumbs":"Reference » The SDK » Session manager behavior (persistent vs in-memory)","id":"1094","title":"Session manager behavior (persistent vs in-memory)"},"1095":{"body":"import { createAgentSession, SessionManager } from \\"@veyyon/coding-agent\\"; const { session } = await createAgentSession({ sessionManager: SessionManager.create(process.cwd()),\\n}); console.log(session.sessionFile); // absolute .jsonl path Persists conversation/messages/state deltas to session files. Supports resume/open/list/fork workflows. sessionFile is defined on the session options.","breadcrumbs":"Reference » The SDK » File-backed (default)","id":"1095","title":"File-backed (default)"},"1096":{"body":"import { createAgentSession, SessionManager } from \\"@veyyon/coding-agent\\"; const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(),\\n}); console.log(session.sessionFile); // undefined No filesystem persistence. Useful for tests, ephemeral workers, request-scoped agents. Session methods still work, but persistence-specific behaviors (file resume/fork paths) are naturally limited.","breadcrumbs":"Reference » The SDK » In-memory","id":"1096","title":"In-memory"},"1097":{"body":"import { SessionManager } from \\"@veyyon/coding-agent\\"; const recent = await SessionManager.continueRecent(process.cwd());\\nconst listed = await SessionManager.list(process.cwd());\\nconst opened = listed[0] ? await SessionManager.open(listed[0].path) : null;","breadcrumbs":"Reference » The SDK » Resume/open/list helpers","id":"1097","title":"Resume/open/list helpers"},"1098":{"body":"createAgentSession() uses ModelRegistry + AuthStorage for model selection and API key resolution.","breadcrumbs":"Reference » The SDK » Model and auth wiring","id":"1098","title":"Model and auth wiring"},"1099":{"body":"import { createAgentSession, discoverAuthStorage, ModelRegistry, SessionManager,\\n} from \\"@veyyon/coding-agent\\"; const authStorage = await discoverAuthStorage();\\nconst modelRegistry = new ModelRegistry(authStorage);\\nawait modelRegistry.refresh(); const available = modelRegistry.getAvailable();\\nif (available.length === 0) throw new Error(\\"No authenticated models available\\"); const { session } = await createAgentSession({ authStorage, modelRegistry, model: available[0], thinkingLevel: \\"medium\\", sessionManager: SessionManager.inMemory(),\\n});","breadcrumbs":"Reference » The SDK » Explicit wiring","id":"1099","title":"Explicit wiring"},"11":{"body":"tools.approvalMode is one of plan, ask, ask-command, auto, or yolo. auto is the default. Older names map onto those: always-ask to ask, write\\nand auto-edit to ask-command. The tiers are read, write, and exec. Four guards sit above the mode and no mode lifts them: Per-tool overrides in tools.approval. The working-directory boundary. The secret-use boundary. bash-guard.ts, which forces a prompt on a destructive command such as a\\nrecursive delete of the home directory. yolo does not lift it. bashInterceptor.enabled adds a user-configured layer on top, off by default,\\nmatching bashInterceptor.patterns. See Approvals and /settings -> Interaction ->\\nApprovals.","breadcrumbs":"Design and mechanisms » Mechanisms » Tool approval tiers","id":"11","title":"Tool approval tiers"},"110":{"body":"A mode is a named choice of which tiers run without asking. There are five: Mode Auto-approves Prompts for plan read write with an active plan-mode session; write and exec are otherwise denied ask nothing read, write, exec ask-command read + write exec auto all tiers a per-tool policy, the working-directory boundary, credential use, a tool’s own flagged calls yolo all tiers a blatantly destructive command, and a per-tool deny or prompt The schema default is auto. Three older names still work: always-ask maps to ask, and write and auto-edit both map to ask-command. Set the mode in config, or override it for one run: $ veyyon --approval-mode ask-command \\"run the tests and fix failures\\" The launch flags --yolo and --plan-yolo set yolo and a plan-mode variant of it.","breadcrumbs":"Core concepts » Permission model » Modes","id":"110","title":"Modes"},"1100":{"body":"When no explicit model/ modelPattern is provided: restore model from existing session (if restorable + key available) settings default model role ( default) first available model with valid auth If restore fails, modelFallbackMessage explains fallback.","breadcrumbs":"Reference » The SDK » Selection order when model is omitted","id":"1100","title":"Selection order when model is omitted"},"1101":{"body":"AuthStorage.getApiKey(...) resolves in this order: runtime override ( setRuntimeApiKey, used by CLI --api-key) config-sourced API key override ( models.yml provider apiKey) stored OAuth credential, including refresh when needed stored login-sourced API-key credential provider environment variables stored non-login API-key credential (may be a stale broker-migrated copy) custom-provider resolver fallback","breadcrumbs":"Reference » The SDK » Auth priority","id":"1101","title":"Auth priority"},"1102":{"body":"Subscribe with session.subscribe(listener); it returns an unsubscribe function. const unsubscribe = session.subscribe((event) => { switch (event.type) { case \\"agent_start\\": case \\"turn_start\\": case \\"tool_execution_start\\": break; case \\"message_update\\": if (event.assistantMessageEvent.type === \\"text_delta\\") { process.stdout.write(event.assistantMessageEvent.delta); } break; }\\n}); AgentSessionEvent includes core AgentEvent plus session-level events: auto_compaction_start / auto_compaction_end auto_retry_start / auto_retry_end retry_fallback_applied / retry_fallback_succeeded ttsr_triggered todo_reminder / todo_auto_clear irc_message","breadcrumbs":"Reference » The SDK » Event subscription model","id":"1102","title":"Event subscription model"},"1103":{"body":"session.prompt(text, options?) is the primary entry point. Behavior: optional command/template expansion ( / commands, custom commands, file slash commands, prompt templates) if currently streaming: streamingBehavior: \\"steer\\" | \\"followUp\\" chooses how prompt() queues extension sendUserMessage(content) defaults to steer when deliverAs is omitted queued messages are preserved instead of throwing work away if idle: validates model + API key appends user message starts agent turn Related APIs: sendUserMessage(content, { deliverAs? }) steer(text, images?) followUp(text, images?) sendCustomMessage({ customType, content, ... }, { deliverAs?, triggerTurn? }) abort()","breadcrumbs":"Reference » The SDK » Prompt lifecycle","id":"1103","title":"Prompt lifecycle"},"1104":{"body":"","breadcrumbs":"Reference » The SDK » Tools and extension integration","id":"1104","title":"Tools and extension integration"},"1105":{"body":"Built-ins come from createTools(...) and BUILTIN_TOOLS. toolNames acts as an allowlist for built-ins. customTools and extension-registered tools are still included. Hidden tools (for example yield) are opt-in unless required by options. const { session } = await createAgentSession({ toolNames: [\\"read\\", \\"grep\\", \\"glob\\", \\"write\\"], requireYieldTool: true,\\n});","breadcrumbs":"Reference » The SDK » Built-ins and filtering","id":"1105","title":"Built-ins and filtering"},"1106":{"body":"Every tool name is declared once, in packages/coding-agent/src/tools/builtin-names.ts: BUILTIN_TOOL_NAMES for the tools offered by default, HIDDEN_TOOL_NAMES for the ones a caller or\\na mode turns on, and TOOL, a map derived from both. Use TOOL inside the package rather than writing the name again: import { TOOL } from \\"./tools/builtin-names\\"; if (!requestedTools.includes(TOOL.yield)) requestedTools.push(TOOL.yield); TOOL.yield has the literal type \\"yield\\", so it fits anywhere the string did. The reason to\\nprefer it is what happens when a tool is renamed: the key disappears and every site that used it\\nstops compiling. A hand-written \\"yield\\" keeps compiling and quietly stops matching, and the only\\nsymptom is a tool that is no longer there. A few strings in the package share a spelling with a tool while naming something else, such as the \\"task\\" agent id, the \\"write\\" approval tier, and the subagent.output: \\"yield\\" setting value.\\nThose stay literals and carry a // not-a-tool-name: comment saying which they are. The test test/tools/tool-name-literals-have-one-owner.test.ts reads the selection sites and fails on any\\nunmarked tool-name literal. As a caller of the SDK you keep passing plain strings: toolNames takes the names as text, and\\nlegacy spellings ( search for grep, find for glob) are still normalized for you.","breadcrumbs":"Reference » The SDK » Tool names have one owner","id":"1106","title":"Tool names have one owner"},"1107":{"body":"extensions: inline ExtensionFactory[] additionalExtensionPaths: load extra extension files disableExtensionDiscovery: disable automatic extension scanning preloadedExtensions: reuse already loaded extension set","breadcrumbs":"Reference » The SDK » Extensions","id":"1107","title":"Extensions"},"1108":{"body":"AgentSession supports runtime activation updates: getActiveToolNames() getAllToolNames() setActiveToolsByName(names) refreshMCPTools(mcpTools) System prompt is rebuilt to reflect active tool changes.","breadcrumbs":"Reference » The SDK » Runtime tool set changes","id":"1108","title":"Runtime tool set changes"},"1109":{"body":"Use these when you want partial control without recreating internal discovery logic: discoverAuthStorage(agentDir?) discoverExtensions(cwd?) discoverSkills(cwd?, _agentDir?, settings?) discoverContextFiles(cwd?, _agentDir?) discoverPromptTemplates(cwd?, agentDir?) discoverSlashCommands(cwd?) discoverCustomTSCommands(cwd?, agentDir?) discoverMCPServers(cwd?) buildSystemPrompt(options?)","breadcrumbs":"Reference » The SDK » Discovery helpers","id":"1109","title":"Discovery helpers"},"111":{"body":"A tier describes what kind of thing a tool does. It does not identify which file the\\ntool is about to touch. In ask-command and auto, the write tier is approved, so write runs without asking whether the target is src/main.ts or a file in your home\\ndirectory. The working-directory boundary is the second question, asked after the tier: Does this call touch a path outside the session working directory? If it does, the call requires approval even though its tier would have allowed it. This\\nholds in plan, ask, ask-command and auto, so the shipped default is inside it. It\\ndoes not hold in yolo, which turns off permission entirely. Say you launched in ~/projects/api and the model runs this: $ veyyon --approval-mode ask-command \\"update the config\\" Writing ~/projects/api/config.yml runs without asking, because it is inside the\\nworking directory and write is an approved tier. Writing ~/.ssh/config prompts, because\\nit is outside, even though the tier is the same. The check looks at where a path really leads, not at how it is spelled. A path written\\nentirely inside the working directory that reaches outside it through a symlink counts\\nas outside. A path that cannot be resolved at all also counts as outside, because\\ntreating an unreadable path as safe is the assumption you least want to be wrong about. These tools take part: read, write, edit, ast_edit, grep, glob, ast_grep, inspect_image, and set_cwd. set_cwd is on that list because it changes the working directory itself. If it were\\nnot bound, you could move the boundary instead of obeying it: re-root to the parent\\ndirectory, and every later write counts as inside. So re-rooting outward prompts, the same\\nas writing outward. Re-rooting into a subdirectory does not prompt, because that narrows\\nwhat the session can reach rather than widening it. When no interactive prompt is available, such as a headless or ACP run, a call that\\nneeds approval fails instead of proceeding. The error states the path that crossed the\\nboundary, so you can see why the run stopped.","breadcrumbs":"Core concepts » Permission model » The working-directory boundary","id":"111","title":"The working-directory boundary"},"1110":{"body":"For SDK consumers building orchestrators (similar to task executor flow): outputSchema: passes structured output expectation into tool context requireYieldTool: forces yield tool inclusion taskDepth: recursion-depth context for nested task sessions parentTaskPrefix: artifact naming prefix for nested task outputs These are optional for normal single-agent embedding.","breadcrumbs":"Reference » The SDK » Subagent-oriented options","id":"1110","title":"Subagent-oriented options"},"1111":{"body":"type CreateAgentSessionResult = { session: AgentSession; extensionsResult: LoadExtensionsResult; setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void; mcpManager?: MCPManager; modelFallbackMessage?: string; lspServers?: Array<{ name: string; status: \\"connecting\\" | \\"ready\\" | \\"error\\" | \\"available\\"; fileTypes: string[]; error?: string; }>; eventBus: EventBus;\\n}; Use setToolUIContext(...) only if your embedder provides UI capabilities that tools/extensions should call into.","breadcrumbs":"Reference » The SDK » createAgentSession() return value","id":"1111","title":"createAgentSession() return value"},"1112":{"body":"createAgentSession() runs two background optimizations to overlap I/O with the rest of session setup: Model-host preconnect. As soon as the model is resolved, the SDK fires a best-effort fetch.preconnect() call against the model host so DNS + TCP + TLS + HTTP/2 to the provider’s host happens in parallel with extension/skill load, tool registry build, and system-prompt assembly. The first real fetch(...) then reuses the warm connection, saving 100–300 ms on transcontinental hops (e.g. residential IP → api.anthropic.com). Implementation lives in preconnectModelHost() in packages/coding-agent/src/sdk.ts. If Bun’s preconnect is unavailable (non-Bun runtime) or the call throws, the optimization is silently skipped: never a hard dependency. Applies to every mode (interactive, print, RPC, ACP). Conditional LSP warmup. Startup LSP servers (those returned by discoverStartupLspServers(cwd)) are only warmed when all of these hold: enableLsp !== false on the session options, and options.hasUI === true (interactive TUI), and the lsp.lazy setting is disabled (it defaults to true). With lsp.lazy enabled, the default, no language servers are launched at startup at all; each server cold-starts on first use, i.e. when the agent invokes the lsp tool or an edit/write touches a file whose extension matches the server’s fileTypes. Print / script / RPC / ACP invocations ( hasUI=false) skip the warmup regardless of the setting: they don’t render the warmup status indicator and typically finish before the language servers would stabilize, so warming them just spends CPU parsing big initialize responses concurrently with the LLM stream consumer and jitters perceived latency. Tools that actually need an LSP server still spin one up on demand through getOrCreateClient(), only the startup warmup is skipped. The returned lspServers field in CreateAgentSessionResult is still populated for UI sessions in lazy mode, recognized servers are discovered (no processes spawned) and reported with status \\"available\\" so the welcome screen and /status can list them; it is undefined only when enableLsp === false or hasUI === false.","breadcrumbs":"Reference » The SDK » Startup performance","id":"1112","title":"Startup performance"},"1113":{"body":"import { createAgentSession, discoverAuthStorage, ModelRegistry, SessionManager, Settings,\\n} from \\"@veyyon/coding-agent\\"; const authStorage = await discoverAuthStorage();\\nconst modelRegistry = new ModelRegistry(authStorage);\\nawait modelRegistry.refresh(); const settings = Settings.isolated({ \\"compaction.enabled\\": true, \\"retry.enabled\\": true,\\n}); const { session } = await createAgentSession({ authStorage, modelRegistry, settings, sessionManager: SessionManager.inMemory(), toolNames: [\\"read\\", \\"grep\\", \\"glob\\", \\"edit\\", \\"write\\"], enableMCP: false, enableLsp: true,\\n}); session.subscribe((event) => { if ( event.type === \\"message_update\\" && event.assistantMessageEvent.type === \\"text_delta\\" ) { process.stdout.write(event.assistantMessageEvent.delta); }\\n}); await session.prompt(\\"Find all TODO comments in this repo and propose fixes.\\");\\nawait session.dispose(); Call session.dispose() once when the host no longer needs the session. Repeated or concurrent calls share the first disposal transaction. The first call’s shutdown options are authoritative, and cleanup still detaches SDK listeners if audit flushing reports an error.","breadcrumbs":"Reference » The SDK » Minimal controlled embed example","id":"1113","title":"Minimal controlled embed example"},"1114":{"body":"Exit codes follow common shell conventions for scripts and CI. Code Meaning 0 Success. 1 A Veyyon runtime error: bad config, auth failure, no such session, an unrecoverable runtime error, or the fallback when a child process ended without a reportable status. 2 A command-line usage error, following the conventional shell meaning of code 2. You get it for an unrecognized flag, a bad flag value, a missing required argument, a mistyped subcommand, or a single-shot run ( --print) with no prompt to send. It is the same code whether the mistake is in a root flag ( veyyon --nope) or in a subcommand’s arguments ( veyyon config --nope, veyyon completions tcsh, veyyon config get). Veyyon fails before starting a session, so no LLM call or MCP connection happens. 130 Veyyon hard-aborted on a Ctrl+C that arrived while it was already shutting down, at 128 + SIGINT ( 128 + 2), rather than waiting on a teardown step that is stuck. An ordinary exit through the normal shutdown, including the double Ctrl+C or Ctrl+D that starts it, completes and returns 0. N When Veyyon runs a child process (for example a shell tool command), the child’s own exit code passes through unchanged. 128 + signal On Unix, a child killed by a signal is reported as 128 + signal (the POSIX shell convention): SIGKILL (9) → 137, SIGTERM (15) → 143. Two guarantees hold everywhere: A failure is never reported as 0. An unknown or missing child status falls back to 1, never\\nsuccess. A signal death is surfaced as a distinct non-zero code, never swallowed. The most useful distinction for a script is the one between 1 and 2. A 1 means the invocation\\nwas valid and the attempt failed, so a retry may succeed. A 2 means the command line itself was\\nwrong, so an identical retry cannot succeed. Check for 2 before you loop: veyyon --print \\"$prompt\\"\\nstatus=$?\\nif [ \\"$status\\" -eq 2 ]; then echo \\"fix the command line before retrying\\" >&2 exit 2\\nfi 0, 1, 2 and 130 come from packages/coding-agent/src/cli/exit-codes.ts, and a test asserts\\nthat this table and that module agree. The command framework in packages/utils/src/cli.ts declares\\nthe same 2 as CLI_EXIT_USAGE, because it rejects a subcommand’s arguments before that module is\\non the startup path; a test asserts the two numbers are equal. If you add a code, add it to the\\ntable and to exit-codes.ts. For the machine-readable event stream (including per-turn and per-tool outcomes), use the\\nAgent Client Protocol mode ( veyyon acp); see the CLI reference.","breadcrumbs":"Reference » Exit codes » Exit codes","id":"1114","title":"Exit codes"},"1115":{"body":"Everything Veyyon stores lives under the config home, ~/.veyyon by default on every platform.\\nOverride the directory name with VEYYON_CONFIG_DIR;\\non Linux/macOS the XDG layout is available after veyyon config init-xdg.","breadcrumbs":"Reference » File locations » File locations","id":"1115","title":"File locations"},"1116":{"body":"The root itself holds only global, cross-profile state. Everything else is per-profile: Path Contents config.yml Global settings that apply across profiles: defaultProfile (which profile a bare veyyon launches), profileSharing, and the auth-broker keys authBrokerUrl / authBrokerToken. Not to be confused with a profile’s own config.yml (below). shared-auth/ Shared credential store, used when profileSharing is on: agent.db (SQLite OAuth/API-key storage shared across profiles). AGENTS.md Global instructions loaded into every profile’s session. Veyyon creates it on first run with a stripped-before-load guidance header. Keep profile-specific rules in the profile’s own AGENTS.md (below). See Instruction layers. install-id Persistent per-install UUID. Shared by every profile. profiles/ One directory per profile, including profiles/default/, see below.","breadcrumbs":"Reference » File locations » The config home ( ~/.veyyon/)","id":"1116","title":"The config home ( ~/.veyyon/)"},"1117":{"body":"Every profile, including default, is a directory under profiles/ with the same shape.\\nA profile has two layers: Profile root ( profiles//), operational state: Path Contents logs/ Log files ( veyyon.YYYY-MM-DD.log). plugins/ Installed plugins ( node_modules/, manifest, lockfile). wt/ Agent-managed git worktrees (PR checkouts, task isolation). cache/ Caches: GitHub view cache, fastembed models, auth-broker snapshot. natives/, puppeteer/, python-env/ Downloaded native binaries, Puppeteer browser cache, managed Python venv. stats.db, autoqa.db, gpu_cache.json Usage stats, auto-QA state, GPU probe cache. reports/, remote/, remote-host/, ssh-control/, autoresearch/ Reports, remote mounts, SSH control sockets, autoresearch state. Agent dir ( profiles//agent/), identity and conversation state: Path Contents config.yml This profile’s settings ( config.yaml also accepted). See Configuration. agent.db Settings + auth storage (SQLite). sessions/ Saved session transcripts, one per thread. blobs/ Content-addressed attachment/blob store. history.db, models.db Composer history, model cache. skills/, commands/, prompts/, tools/, themes/, modules/ Skills, slash commands, prompt templates, custom tools, themes, Python modules. mcp.json, ssh.json MCP server and SSH target config. keybindings.yml This profile’s keybindings ( keybindings.yaml accepted; legacy keybindings.json migrates on load). AGENTS.md Profile-specific context appended to the assembled prompt. RULES.md Sticky profile rules reattached near each turn. TITLE_SYSTEM.md Optional system prompt for automatic session-title calls. PROMPT_SECTIONS/ Persistent replacements or additions for named assembled-prompt sections. memories/, terminal-sessions/ Memory store, terminal session state. cache/ Agent-scoped caches (tiny title models, document conversions). Overriding the agent dir directly ( VEYYON_CODING_AGENT_DIR) applies to the default profile only;\\na named profile always derives its own agent dir.","breadcrumbs":"Reference » File locations » Profiles ( ~/.veyyon/profiles//)","id":"1117","title":"Profiles ( ~/.veyyon/profiles//)"},"1118":{"body":"Resolution order for every veyyon / vey invocation: --profile on the command line. VEYYON_PROFILE. An explicitly empty VEYYON_PROFILE= forces the default profile, bypassing step 3. defaultProfile in the global ~/.veyyon/config.yml: set it with veyyon profile default . The default profile. The name default always addresses profiles/default/ and cannot be removed.","breadcrumbs":"Reference » File locations » Which profile launches","id":"1118","title":"Which profile launches"},"1119":{"body":"Before this layout, the default profile lived bare in the config root ( ~/.veyyon/agent/, ~/.veyyon/logs/, …). On first launch Veyyon migrates that state into profiles/default/\\nonce, and will not guess if both layouts are present, the error states the exact\\ndirectories to reconcile.","breadcrumbs":"Reference » File locations » Legacy layout migration","id":"1119","title":"Legacy layout migration"},"112":{"body":"A tier does not tell you whether a call is about to spend a credential either. The\\nsecret-use boundary is the third question, asked the same way and in the same modes: Do this call’s arguments carry a stored secret? Your secrets reach a tool as real values. The model works with placeholders such as #GITHUB_TOKEN#, and Veyyon substitutes the credential just before the tool runs, so the\\nmodel can use a secret it never reads. That substitution used to be recorded and never\\nasked about: secrets.auditLog could report afterwards which credential an agent had\\nspent, and nothing prompted you first. Now a call whose arguments carry a real credential requires approval in plan, ask, ask-command and auto, even when its tier would have allowed it. The prompt states the\\nsecret and never shows its value: Allow tool: bash\\nReason: This call uses stored secret: GITHUB_TOKEN. Approving it runs the call with the\\nreal credential. As with the working-directory boundary, yolo turns permission off entirely and turns\\nthis off with it. Every other rung keeps it, the shipped auto included. A call that\\nmentions a placeholder without expanding it, such as one made while secrets.enabled is\\nfalse, is not a credential reference and does not prompt.","breadcrumbs":"Core concepts » Permission model » Secrets in arguments","id":"112","title":"Secrets in arguments"},"1120":{"body":"Auth tokens live in the profile’s agent.db (or the OS keyring, depending on the configured\\ncredential store). BYOK provider keys never land in plaintext config.yml; see Signing in.","breadcrumbs":"Reference » File locations » Credential storage","id":"1120","title":"Credential storage"},"1121":{"body":"Alongside your project (not under the config home): Path Purpose AGENTS.md / CLAUDE.md Project instructions Veyyon auto-loads. These are the only configuration-shaped files a repository contributes. See AGENTS.md. .veyyon/ Project-scoped data that follows the working directory: prompt templates ( prompts/), personalities ( personalities/), the project secret vault, and project-scope plugin installs. Settings, MCP servers, rules, hooks, tools, commands, skills, and agents are never read from it, because a checked-in file must not configure the agent.","breadcrumbs":"Reference » File locations » Project-local files","id":"1121","title":"Project-local files"},"1122":{"body":"Veyyon is a Bun/TypeScript coding agent (fork of oh-my-pi) with Rust hot paths (native grep, PTY, tree-sitter/AST via crates/veyyon-natives). The shipped CLI binary is veyyon. There is no separate app-server daemon in\\nthe product surface.","breadcrumbs":"Architecture overview » Architecture overview","id":"1122","title":"Architecture overview"},"1123":{"body":"prompt ──► veyyon │ ▼ AgentSession turn loop │ ▼ model stream + tool calls │ ▼ tool handlers (read, bash, edit, …) ──► results back to model Interactive mode runs in the TUI. Non-interactive work uses veyyon with a prompt or subcommands such\\nas commit, grep, and models.","breadcrumbs":"Architecture overview » The request path","id":"1123","title":"The request path"},"1124":{"body":"Area Responsibility Handbook Sessions JSONL trees, resume, fork, compact Sessions Edit Hashline patches (default) Edit engine Approvals Approval-mode gating on tool tiers Approvals Config Layered config.yml, profiles Config MCP External tool servers MCP Providers Model registry + auth Providers Memory off / local / mnemopi / hindsight Memory Not part of the product surface: a standalone exec-server process or a separate backends.toml catalog. The table above is the subsystem map.","breadcrumbs":"Architecture overview » Subsystem map","id":"1124","title":"Subsystem map"},"1125":{"body":"Approvals decide when a tool or shell command runs on its own and when Veyyon pauses for the user.\\nThere is no OS-level command sandbox: Veyyon does not confine commands with Landlock, seccomp,\\nSeatbelt, or bubblewrap. The boundary is policy the agent loop enforces before dispatch.","breadcrumbs":"Architecture overview » Approvals internals » Approvals","id":"1125","title":"Approvals"},"1126":{"body":"Map the approval mode ( tools.approvalMode) to a per-tier decision (read / write / exec) for bash, edit, write, and related tools. Apply per-tool overrides ( tools.approval → allow / deny / prompt) on top of the mode. Apply the two argument-level boundaries the tier cannot see: a filesystem target outside the session working directory, and a call whose arguments carry a stored credential. Both prompt on every rung except yolo, the shipped auto included. Force a prompt for hard-coded flagged bash patterns. The destructive ones (recursive deletion of the home directory or a system directory, fork bombs, disk destruction, writes to the system account files) prompt on every rung, yolo included: that is a floor rather than an ordinary prompt, and only an explicit tools.approval.bash: allow lifts it, while deny remains a hard block. The merely dangerous ones ( curl | sh, reboot, nc -e) prompt on every rung below yolo. Surface the approval prompt in the TUI before a gated command or edit runs.","breadcrumbs":"Architecture overview » Approvals internals » Responsibility","id":"1126","title":"Responsibility"},"1127":{"body":"tools.approvalMode in config.yml and the launch flags ( --approval-mode, --auto-approve / --yolo, --plan-yolo) resolve to a decision applied to the bash, edit, and write tools, with\\nplan-mode guards on top. Commands run in-process after policy resolution, there is no standalone\\nexec-server process in the shipped product.","breadcrumbs":"Architecture overview » Approvals internals » Public boundary","id":"1127","title":"Public boundary"},"1128":{"body":"Concept Meaning Approval mode Which tool tiers run without asking ( plan, ask, ask-command, auto (default), yolo; legacy always-ask → ask, write and auto-edit → ask-command). Per-tool policy tools.approval overrides the mode for a named tool. Flagged bash patterns Hard-coded command shapes recorded as destructive or dangerous. The destructive ones prompt on every rung including yolo, and below yolo a per-tool allow does not lift them. The dangerous ones prompt on every rung below yolo. Plan mode Restricts mutating tools until the plan is approved ( /plan). User-facing guide: Approvals.","breadcrumbs":"Architecture overview » Approvals internals » Key concepts","id":"1128","title":"Key concepts"},"1129":{"body":"Sessions are JSONL conversation trees; each turn is one user prompt through model streaming,\\ntool calls, and the final assistant message.","breadcrumbs":"Architecture overview » Session and turn internals » Session and turn","id":"1129","title":"Session and turn"},"113":{"body":"When you want one tool to behave differently from its tier, name it under tools.approval. Each entry maps a tool to allow, deny, or prompt, and that choice\\nwins for that tool whatever the mode is, with one exception: while a plan-mode session is\\nactive, a per-tool allow does not let an exec-tier tool run. Plan mode is a cap rather\\nthan a default, so it outranks both the configured mode and the per-tool setting. A deny\\nis a hard block in every direction. # ~/.veyyon/profiles/default/agent/config.yml\\ntools: approvalMode: ask-command approval: bash: prompt read: allow Here the mode is ask-command, so writes run without a prompt. The override then pulls bash\\nback to prompt, so commands still stop for your approval.","breadcrumbs":"Core concepts » Permission model » Per-tool overrides","id":"113","title":"Per-tool overrides"},"1130":{"body":"Persist append-only session entries with id / parentId linkage Track the active leaf for branching ( /tree, /branch, /fork) Drive compaction when context limits approach ( /compact, auto-compact settings) Coordinate tool execution, approvals, and subagent spawns per turn","breadcrumbs":"Architecture overview » Session and turn internals » Responsibility","id":"1130","title":"Responsibility"},"1131":{"body":"The AgentSession runs the turn loop. On-disk layout: ~/.veyyon/profiles/default/agent/sessions//_.jsonl Blob store: ~/.veyyon/profiles/default/agent/blobs/ Sessions run in-process; there is no separate session daemon. User guide: Sessions.","breadcrumbs":"Architecture overview » Session and turn internals » Public boundary","id":"1131","title":"Public boundary"},"1132":{"body":"Configuration controls models, approvals, memory, MCP, extensions, and TUI behavior. Veyyon loads\\nlayered YAML/JSON from the user agent directories. A working tree never supplies configuration:\\na checked-in .veyyon/config.yml is not read, because a repository is content you may not have\\nwritten. Operator guide: Configuration. Every setting, by name: Settings, Settings reference.","breadcrumbs":"Architecture overview » Config layering » Config","id":"1132","title":"Config"},"1133":{"body":"Resolve config roots (the active profile’s agent dir, plus Claude/Codex/Gemini compatibility paths at user level) Merge profile settings with --config overlays and runtime overrides; apply profiles ( veyyon --profile ) Validate against settings-schema.ts; support --config YAML overlay files (repeatable) Feed resolved settings to sessions, tools, and discovery (skills, hooks, MCP, extensions)","breadcrumbs":"Architecture overview » Config layering » Responsibility","id":"1133","title":"Responsibility"},"1134":{"body":"Primary user file: ~/.veyyon/profiles/default/agent/config.yml (or profile path under ~/.veyyon/profiles/) CLI: veyyon config list|get|set, /settings, /reload-plugins (re-read without restart)","breadcrumbs":"Architecture overview » Config layering » Public boundary","id":"1134","title":"Public boundary"},"1135":{"body":"Roots scanned, precedence, and consumption by settings, skills, hooks, tools, and extensions.","breadcrumbs":"Architecture overview » Config layering » How configuration resolves","id":"1135","title":"How configuration resolves"},"1136":{"body":"Primary implementation: packages/coding-agent/src/config.ts packages/coding-agent/src/config/config-file.ts (re-exported from config.ts) packages/coding-agent/src/config/settings.ts packages/coding-agent/src/config/settings-schema.ts packages/coding-agent/src/discovery/builtin.ts packages/coding-agent/src/discovery/helpers.ts Key integration points: packages/coding-agent/src/capability/index.ts packages/coding-agent/src/discovery/index.ts packages/coding-agent/src/extensibility/skills.ts packages/coding-agent/src/extensibility/hooks/loader.ts packages/coding-agent/src/extensibility/custom-tools/loader.ts packages/coding-agent/src/extensibility/extensions/loader.ts","breadcrumbs":"Architecture overview » Config layering » Scope","id":"1136","title":"Scope"},"1137":{"body":"Generic helper order (`config.ts`)\\n┌───────────────────────────────────────┐\\n│ 1) ~/.veyyon/profiles/default/agent, ~/.claude, ... │\\n│ 2) /.veyyon, /.claude, ... │\\n└───────────────────────────────────────┘ │ ▼ capability providers enumerate items (capability discovery reads HOME only: a working tree is untrusted input and contributes nothing but context files; the project bases above survive only in the generic helper for the callers that still use it, such as TITLE_SYSTEM.md) │ ▼ provider priority sort + capability dedup │ ▼ subsystem-specific consumption (settings, skills, hooks, tools, extensions)","breadcrumbs":"Architecture overview » Config layering » Resolution flow (visual)","id":"1137","title":"Resolution flow (visual)"},"1138":{"body":"","breadcrumbs":"Architecture overview » Config layering » 1) Config roots and source order","id":"1138","title":"1) Config roots and source order"},"1139":{"body":"src/config.ts defines a fixed source priority list: .veyyon (native) .claude .codex .gemini User-level bases: ~/.veyyon/profiles/default/agent ~/.claude ~/.codex ~/.gemini Project-level bases: /.veyyon /.claude /.codex /.gemini The project bases exist in the generic helper, but capability discovery no longer uses them: a checked-out working tree is untrusted input, so a repository contributes context files ( AGENTS.md / CLAUDE.md) and nothing else. The remaining caller of the project bases is TITLE_SYSTEM.md discovery (see Session title prompt override). CONFIG_DIR_NAME is .veyyon ( packages/utils/src/dirs.ts).","breadcrumbs":"Architecture overview » Config layering » Canonical roots","id":"1139","title":"Canonical roots"},"114":{"body":"Within the exec tier, a guard ( packages/coding-agent/src/tools/bash-guard.ts) forces a prompt in plan, ask, ask-command and auto, even over a per-tool allow. It has two halves. The first half judges what a command would DELETE, and it judges the paths after expansion rather\\nthan the command as text. A tilde and $HOME are resolved, so rm -rf ~/ and rm -rf \\"$HOME\\"/\\nare recognized as the home directory. Every target is judged, not just the first, so rm -rf tests/ / is caught. Recursive deletes of the home directory, of any directory containing\\nit, of the system directories, and of the directories that hold your credentials all stop for\\napproval. So does a recursive delete whose target the guard cannot resolve, such as rm -rf \\"$dir\\"/*: if $dir is empty that command starts at the root, and nothing in the command\\ntext states whether it is. The same half stops a truncating redirect into a directory that holds credentials, because echo x > ~/.ssh/id_ed25519 destroys a private key as thoroughly as a delete does. Appending with >> is left alone, since that is how you add a key to authorized_keys. Deletes inside your workspace are not affected. rm -rf node_modules, rm -rf dist, and rm -rf /tmp/build-1234 run without a prompt, and so does a delete inside a protected directory\\nthat does not hold credentials, such as rm -rf ~/.config/some-app. Ordinary redirects, such as bun test > /tmp/results.txt, are not affected either. The second half is a pattern list ( FLAGGED_BASH_PATTERNS, in the same file) for the shapes that\\nare about text rather than paths, and each entry is recorded as one of two strengths. Destructive\\ncovers fork bombs, disk destruction, and writes to system credential files. Dangerous covers a\\nremote fetch piped to a shell, host control commands such as reboot, and a shell wired to a\\nnetwork socket: they run code nobody read, or take the machine down, without destroying data. Both halves ship with Veyyon and cannot be narrowed. You can widen the first half with tools.protectedPaths, a list of absolute paths (a leading ~ is expanded) that a recursive\\ndelete must also stop for: tools: protectedPaths: - /mnt/photos - ~/Documents That setting only adds. Nothing in the built-in judgement reads configuration, so no value you\\nwrite there can stop the guard refusing your home directory, the system roots, or your credentials.\\nAn entry that is not an absolute or ~-relative path is ignored, because resolving it against a\\nguessed working directory would protect somewhere other than what you wrote. The first half and the destructive patterns stop for approval in yolo too, and the /yolo session\\nbypass does not lift them. That is the one place yolo is not absolute, and it is deliberate:\\nwithout it, the commands the guard considers most destructive would be the ones most likely to run\\nin the mode that skips the check. The dangerous patterns are an ordinary prompt instead: every rung\\nbelow yolo stops on them, and yolo does not, because a rung whose whole promise is that it stops\\nasking cannot be asking about an install command the operator typed. To turn the floor off, set tools.approval.bash to allow, which is read as a decision you made on purpose. Setting it to deny remains a hard block. The guard reasons about what a command will do, and that reasoning can be wrong: a shell function,\\nan eval, or a script invoked by name defeats any parser. Treat it as a seatbelt, not as\\ncontainment.","breadcrumbs":"Core concepts » Permission model » Critical bash commands","id":"114","title":"Critical bash commands"},"1140":{"body":"A named profile ( veyyon --profile , /profile in the TUI, or VEYYON_PROFILE) selects which profile agent dir is active. The default profile is ~/.veyyon/profiles/default/agent/; profile is ~/.veyyon/profiles//agent/. Paths written in this document as ~/.veyyon/profiles/default/agent/... mean the active profile’s agent directory. The relocation is uniform across the native provider ( builtin.ts) and the generic config.ts helpers. It covers slash commands, sticky rules, prompts, instructions, hooks, tools, extensions, settings, skills, MCP, the top-level RULES.md and AGENTS.md files, PROMPT_SECTIONS/, and runtime state (sessions, blobs, agent.db). A profile sees only its own Veyyon config, never the default profile’s ~/.veyyon/profiles/default/agent. Keybindings get a one-time seed rather than a live merge: a new named profile copies the default profile’s ~/.veyyon/profiles/default/agent/keybindings.* once (at profile new, or on first launch of an older profile that has no keybindings file). After that the profile’s own file is the only one read, later edits to the default profile’s keybindings do not flow into other profiles. The other source bases are not profile-scoped and load identically under every profile: the external-tool bases ( ~/.claude, ~/.codex, ~/.gemini) belong to those tools. Throughout this document, read ~/.veyyon/profiles/default/agent as shorthand for the active profile’s agent directory.","breadcrumbs":"Architecture overview » Config layering » Profiles","id":"1140","title":"Profiles"},"1141":{"body":"The generic helpers in src/config.ts do not include .pi in source discovery order.","breadcrumbs":"Architecture overview » Config layering » Important constraint","id":"1141","title":"Important constraint"},"1142":{"body":"","breadcrumbs":"Architecture overview » Config layering » 2) Core discovery helpers ( src/config.ts)","id":"1142","title":"2) Core discovery helpers ( src/config.ts)"},"1143":{"body":"Returns ordered entries: User-level entries first (by source priority) Then project-level entries (by same source priority) Options: user (default true) project (default true) cwd (default getProjectDir()) existingOnly (default false) This API is used for directory-based config lookups (commands, hooks, tools, agents, etc.).","breadcrumbs":"Architecture overview » Config layering » getConfigDirs(subpath, options)","id":"1143","title":"getConfigDirs(subpath, options)"},"1144":{"body":"Searches for the first existing file across ordered bases, returns first match (path-only or path+metadata).","breadcrumbs":"Architecture overview » Config layering » findConfigFile(subpath, options) / findConfigFileWithMeta(...)","id":"1144","title":"findConfigFile(subpath, options) / findConfigFileWithMeta(...)"},"1145":{"body":"Walks parent directories upward and returns the nearest existing directory per source base ( .veyyon, .claude, .codex, .gemini), then sorts results by source priority. This helper predates the untrusted-working-tree rule and survives for the callers that legitimately key on the working directory (plugin install scopes). It is not a path for a repository to configure the agent.","breadcrumbs":"Architecture overview » Config layering » findAllNearestProjectConfigDirs(subpath, cwd)","id":"1145","title":"findAllNearestProjectConfigDirs(subpath, cwd)"},"1146":{"body":"ConfigFile is the schema-validated loader for single config files. Supported formats: .yml / .yaml .json / .jsonc Behavior: Validates parsed data against a provided Zod schema. Caches load result until invalidate(). Returns tri-state result via tryLoad(): ok not-found error ( ConfigError with schema/parse context) Legacy migration still supported: If target path is .yml/ .yaml, a sibling .json is auto-migrated once ( migrateJsonToYml).","breadcrumbs":"Architecture overview » Config layering » 3) File config wrapper ( ConfigFile in src/config/config-file.ts, re-exported from src/config.ts)","id":"1146","title":"3) File config wrapper ( ConfigFile in src/config/config-file.ts, re-exported from src/config.ts)"},"1147":{"body":"The runtime settings model is layered: Profile settings: ~/.veyyon/profiles//agent/config.yml CLI config overlays: veyyon --config / repeated --config files, loaded as config.yml-style YAML for this process only Runtime overrides: in-memory, non-persistent Schema defaults: from SETTINGS_SCHEMA There is no project layer. A .veyyon/config.yml or .veyyon/settings.json inside a working tree is never read, because a checked-in file would let any cloned repository configure the agent (the measured escalation was tools.approvalMode: yolo shipped in a repo’s settings.json). Effective precedence: defaults <- profile <- CLI config overlays <- overrides Write behavior: settings.set(...) writes to the profile layer ( config.yml) and queues background save.","breadcrumbs":"Architecture overview » Config layering » 4) Settings resolution model ( src/config/settings.ts)","id":"1147","title":"4) Settings resolution model ( src/config/settings.ts)"},"1148":{"body":"On startup, if config.yml is missing: Migrate from ~/.veyyon/profiles/default/agent/settings.json (renamed to .bak on success) Merge with legacy DB settings from agent.db Write merged result to config.yml Field-level migrations in #migrateRawSettings: queueMode -> steeringMode ask.timeout milliseconds -> seconds when the old value looks like ms ( > 1000). The threshold is a guess, because nothing on disk records which format a file uses, so the rewrite is logged with both values. Every other migration here is a fixed point; this one is not, which is why packages/coding-agent/test/settings-migration-idempotence.test.ts pins the property. Legacy flat theme: \\"...\\" -> theme.dark/theme.light structure","breadcrumbs":"Architecture overview » Config layering » Migration behavior still active","id":"1148","title":"Migration behavior still active"},"1149":{"body":"Most non-core config loading flows through the capability registry ( src/capability/index.ts + src/discovery/index.ts).","breadcrumbs":"Architecture overview » Config layering » 5) Capability/discovery integration","id":"1149","title":"5) Capability/discovery integration"},"115":{"body":"When a tool is denied, or a policy check fails, Veyyon returns an error to the model. It\\ndoes not retry with more permission. An error never escalates what the model is allowed to\\ndo.","breadcrumbs":"Core concepts » Permission model » On deny","id":"115","title":"On deny"},"1150":{"body":"Providers are sorted by numeric priority (higher first). Example priorities: Native Veyyon ( builtin.ts): 100 Claude: 80 Codex / agents / Claude marketplace: 70 Gemini: 60 Provider precedence (higher wins) native (.veyyon) priority 100\\nclaude priority 80\\ncodex / agents / ... priority 70\\ngemini priority 60","breadcrumbs":"Architecture overview » Config layering » Provider ordering","id":"1150","title":"Provider ordering"},"1151":{"body":"Capabilities define a key(item): same key => first item wins (higher-priority/earlier-loaded item) no key ( undefined) => no dedup, all items retained Relevant keys: skills: name tools: name hooks: ${type}:${tool}:${name} extension modules: name extensions: name settings: no dedup (all items preserved)","breadcrumbs":"Architecture overview » Config layering » Dedup semantics","id":"1151","title":"Dedup semantics"},"1152":{"body":"Native provider ( id: native) reads native config from one place: the active profile’s agent directory, ~/.veyyon/profiles//agent/.... The provider’s config-dir helper resolves HOME only. /.veyyon used to be pushed at level \\"project\\", and six capabilities read it through that one helper (slash commands, rules, prompts, instructions, hooks, tools) plus extension modules and settings; that is gone, because one line in a cloned repo configured the agent. The only thing a repository still contributes is the context-file walk.","breadcrumbs":"Architecture overview » Config layering » 6) Native .veyyon provider behavior ( packages/coding-agent/src/discovery/builtin.ts)","id":"1152","title":"6) Native .veyyon provider behavior ( packages/coding-agent/src/discovery/builtin.ts)"},"1153":{"body":"The profile agent directory is used only when it exists and is non-empty. Skills are loaded only from the active profile’s agent dir ( ~/.veyyon/profiles//agent/skills). Project-local .veyyon/skills directories are deliberately not scanned, so no repository can inject skills into a session by ambient autodiscovery. AGENTS.md has three scopes: the global cross-profile ~/.veyyon/AGENTS.md, the active profile’s first matching instruction file, and the project walk from the working directory to the repository root (one file per directory level: .veyyon/AGENTS.md at the nearest non-empty .veyyon/ claims its level, bare AGENTS.md next, bare CLAUDE.md last). RULES.md is the active profile’s file only; a repository’s .veyyon/RULES.md is not read. Persistent system-prompt changes use PROMPT_SECTIONS/ under the active profile’s agent dir. See docs/handbook/src/models/system-prompt.md.","breadcrumbs":"Architecture overview » Config layering » Directory admission rules","id":"1153","title":"Directory admission rules"},"1154":{"body":"All under the active profile’s agent dir: Skills: skills/*/SKILL.md Slash commands: commands/*.md Rules: rules/*.{md,mdc} Prompts: prompts/*.md Instructions: instructions/*.md Hooks: hooks/pre/*, hooks/post/* are scanned in full, but only .ts and .js entries load; anything else is reported as skipped Tools: tools/*.{json,md,ts,js,sh,bash,py} and tools//index.ts Extension modules: discovered under extensions/ (+ legacy settings.json.extensions string array) Extensions: extensions//gemini-extension.json Settings: config.yml (plus the one-time settings.json migration)","breadcrumbs":"Architecture overview » Config layering » Scope-specific loading","id":"1154","title":"Scope-specific loading"},"1155":{"body":"The native provider’s only project-scope read is the context-file walk: the nearest non-empty .veyyon/ directory’s AGENTS.md claims its own directory level, and every level from the repository root down to the cwd contributes at most one file ( .veyyon/AGENTS.md, else bare AGENTS.md, else bare CLAUDE.md).","breadcrumbs":"Architecture overview » Config layering » Project context-file walk","id":"1155","title":"Project context-file walk"},"1156":{"body":"","breadcrumbs":"Architecture overview » Config layering » 7) How major subsystems consume config","id":"1156","title":"7) How major subsystems consume config"},"1157":{"body":"Settings.init() loads the profile config.yml, the machine-global bindings, CLI --config overlays, and runtime overrides. Nothing is read from the working tree.","breadcrumbs":"Architecture overview » Config layering » Settings subsystem","id":"1157","title":"Settings subsystem"},"1158":{"body":"Create TITLE_SYSTEM.md in a supported config base: # ~/.veyyon/profiles/default/agent/TITLE_SYSTEM.md\\nGenerate a session name using lowercase `:`. Missing TITLE_SYSTEM.md keeps the bundled title prompts. Discovery checks project config bases first, including .veyyon/TITLE_SYSTEM.md, then the active profile’s agent/TITLE_SYSTEM.md and the supported external-tool config bases. The file replaces only the automatic session-title generation system prompt. The agent’s own base prompt is assembled. Use --system-prompt or --append-system-prompt for a one-run override, or PROMPT_SECTIONS/ for persistent section changes. The online path instructs the title model to wrap the title in ... and parses it leniently from text (a plain sentence, a truncated/unclosed tag, or a stray {\\"title\\": \\"...\\"} JSON echo all still work). A TITLE_SYSTEM.md override gets the wrap-in- instruction appended after it. The local tiny-title path keeps the ... prefill/stop wrapper and uses this file as its system turn.","breadcrumbs":"Architecture overview » Config layering » Session title prompt override","id":"1158","title":"Session title prompt override"},"1159":{"body":"extensibility/skills.ts loads via loadCapability(skillCapability.id, { cwd, providers: profileSkillProviderIds() }). The allowlist ( native, veyyon-managed, veyyon-plugins) scopes discovery to the active profile; foreign-tool skill providers are never scanned ambiently (they feed the import scan only). Applies name-based filters only: disabledExtensions, ignoredSkills, includeSkills. There are no per-source toggles and no custom directories.","breadcrumbs":"Architecture overview » Config layering » Skills subsystem","id":"1159","title":"Skills subsystem"},"116":{"body":"Approvals Safety CLI","breadcrumbs":"Core concepts » Permission model » Related","id":"116","title":"Related"},"1160":{"body":"discoverAndLoadHooks() resolves hook paths from hook capability + explicit configured paths. Then loads modules via Bun import.","breadcrumbs":"Architecture overview » Config layering » Hooks subsystem","id":"1160","title":"Hooks subsystem"},"1161":{"body":"discoverAndLoadCustomTools() resolves tool paths from tool capability + plugin tool paths + explicit configured paths. Declarative .md/.json tool files are metadata only; executable loading expects code modules.","breadcrumbs":"Architecture overview » Config layering » Tools subsystem","id":"1161","title":"Tools subsystem"},"1162":{"body":"discoverAndLoadExtensions() resolves extension modules from extension-module capability plus explicit paths. Current implementation intentionally keeps only capability items with _source.provider === \\"native\\" before loading.","breadcrumbs":"Architecture overview » Config layering » Extensions subsystem","id":"1162","title":"Extensions subsystem"},"1163":{"body":"Use this mental model: Source directory ordering from config.ts determines candidate path order. Capability provider priority determines cross-provider precedence. Capability key dedup determines collision behavior (first wins for keyed capabilities). Subsystem-specific merge logic can further change effective precedence (especially settings).","breadcrumbs":"Architecture overview » Config layering » 8) Precedence rules to rely on","id":"1163","title":"8) Precedence rules to rely on"},"1164":{"body":"The settings layers deep-merge in a fixed order (profile, then --config overlays, then runtime overrides). Because merge applies later layer values over earlier values, an overlay’s array replaces the profile array rather than appending to it.","breadcrumbs":"Architecture overview » Config layering » Settings-specific caveat","id":"1164","title":"Settings-specific caveat"},"1165":{"body":"ConfigFile JSON -> YAML migration for YAML-targeted files. Settings migration from settings.json and agent.db to config.yml. Settings key migrations include queueMode, ask.timeout, flat theme, task.isolation.enabled, legacy task.isolation.mode values, the whole task.* group plus modelRoles.task moving to subagent.*, removed edit modes, statusLine.plan_mode, memories.enabled, and hindsight scoping/name fields. The removed per-source skill toggles ( skills.enableCodexUser, skills.enableClaudeUser, skills.enableClaudeProject, skills.enablePiUser, skills.enablePiProject, skills.enableAgentsUser, skills.enableAgentsProject) and skills.customDirectories are no longer read. Skills load only from the active profile. A stale key in an old config.yml is ignored, not an error. If these compatibility paths are removed in code, update this document immediately; several runtime behaviors still depend on them today.","breadcrumbs":"Architecture overview » Config layering » 9) Legacy/compatibility behaviors still present","id":"1165","title":"9) Legacy/compatibility behaviors still present"},"1166":{"body":"Model Context Protocol (MCP) connects Veyyon to external tools and data as an MCP client\\n(consumes configured servers). Editor embedding uses ACP ( veyyon acp), a different protocol.","breadcrumbs":"Architecture overview » MCP internals » MCP","id":"1166","title":"MCP"},"1167":{"body":"Discover MCP servers from the operator’s user and profile config files Connect over stdio or HTTP (streamable HTTP / SSE-style transports) Register tools as namespaced names ( mcp___, e.g. mcp__filesystem_delete) Handle OAuth for remote servers and persist credentials per profile","breadcrumbs":"Architecture overview » MCP internals » Responsibility","id":"1167","title":"Responsibility"},"1168":{"body":"Module Role packages/coding-agent/src/mcp/ Config load, manager, OAuth, tool wiring packages/coding-agent/src/discovery/builtin.ts Profile-scoped mcp.json / .mcp.json discovery packages/coding-agent/src/modes/controllers/mcp-command-controller.ts /mcp TUI commands Primary config files: User: ~/.veyyon/profiles/default/agent/mcp.json (profile-scoped when using --profile) There is no project scope. A checked-out working tree is untrusted input, so /.veyyon/mcp.json, a repo-root mcp.json/ .mcp.json, and the foreign .cursor/mcp.json and .vscode/mcp.json are no longer read. Veyyon also ingests MCP definitions from other tools’ USER-level configs\\n( ~/.claude, ~/.codex, ~/.gemini, ~/.cursor) when discovery is enabled. A config file that exists but does not parse is REPORTED, never skipped: the native provider raises Failed to parse JSON in through the capability\\nwarning channel, which MCPManager.discoverAndConnect puts on its status stream\\nand /mcp list and the boot health zone render. Before that, a mistyped comma in mcp.json produced a session with every configured server missing and no line\\nanywhere saying why. User guide: MCP, MCP setup. Engineering detail: docs/handbook/src/reference/mcp-config.md, docs/internal/mcp-runtime-lifecycle.md, docs/internal/mcp-protocol-transports.md.","breadcrumbs":"Architecture overview » MCP internals » Implementation (TypeScript)","id":"1168","title":"Implementation (TypeScript)"},"1169":{"body":"The providers subsystem connects Veyyon to model APIs and normalizes their\\nauth, request, and response formats.","breadcrumbs":"Architecture overview » Providers internals » Providers","id":"1169","title":"Providers"},"117":{"body":"The terminal engine is provider and API agnostic. You choose an endpoint, choose a model when that\\nendpoint exposes model choice, provide the key, and Veyyon calls that API directly. The endpoint can be\\na local server (Ollama, LM Studio), a direct provider API (OpenAI, Anthropic, Google), or any\\nOpenAI-compatible gateway. The contract between the harness and the model. For copy-paste provider setup, see Configuring providers. For model switching, see Models and providers.","breadcrumbs":"Core concepts » Model contract » Model contract","id":"117","title":"Model contract"},"1170":{"body":"Maintain the catalog of supported model providers and their capabilities. Resolve a model slug to a provider and its ModelInfo. Authenticate requests with API keys, access tokens, or OAuth credentials. Translate between the provider-specific wire format and the engine’s\\nprotocol types.","breadcrumbs":"Architecture overview » Providers internals » Responsibility","id":"1170","title":"Responsibility"},"1171":{"body":"The provider stack lives in the @veyyon/ai package. Component Role Provider adapters Per-provider connection and wire-format adapters API client registry OpenAI-compatible API client registry Provider details Provider metadata, auth mode, and endpoints Model catalog Model catalog and per-model capabilities Model registry Slug resolution to provider + model info","breadcrumbs":"Architecture overview » Providers internals » Implementation","id":"1171","title":"Implementation"},"1172":{"body":"Provider metadata: a provider’s auth mode and endpoint configuration. Model info: per-model capabilities such as context window and vision support. Auth material: resolved from API keys, access tokens, or OAuth credentials. See Models and providers and Provider stack and bring-your-own-key for how to add\\nyour own keys and choose models.","breadcrumbs":"Architecture overview » Providers internals » Key concepts","id":"1172","title":"Key concepts"},"1173":{"body":"Each provider adapter also sets where the request’s cache markers go, and the shapes differ:\\nAnthropic places up to four cache_control breakpoints, Bedrock interleaves cachePoint blocks,\\nthe OpenAI Responses path sends an explicit prompt_cache_breakpoint, and everything else caches\\nimplicitly or not at all. Two settings under Settings → Context → Prompt Cache report and\\noptionally block on a cache the provider demonstrably did not use. The full per-provider account,\\nincluding the breakpoint budget and what invalidates what, is docs/internal/prompt-caching.md.","breadcrumbs":"Architecture overview » Providers internals » Prompt caching","id":"1173","title":"Prompt caching"},"1174":{"body":"A caller declares streamFirstEventTimeoutMs. It is one attempt’s deadline, and\\nit bounds how many stalled attempts a turn pays for: the phase before the first\\nevent ends after two. The first stall is retried, because a single connect that\\nnever produces an event is common and recoverable. A second consecutive stall is\\na dead endpoint, and re-spending the deadline there turned a declared 100s into\\nminutes of silence. The phase ends at the first of these: the first event arrives, after which streamIdleTimeoutMs bounds the turn; the phase budget is spent and the turn fails with the deadline as its reason. utils/first-event-budget.ts in @veyyon/ai defines the shape: Function Use openFirstEventBudget(totalMs) Open a budget for the declared number. A non-positive or absent total is unbounded, matching streamFirstEventTimeoutMs: 0. openStallLadderBudget(perAttemptMs) A phase budget for a retry ladder: the per-attempt deadline times PRE_RESPONSE_STALL_ATTEMPTS (two). What Anthropic and Codex open. openBoundedFirstEventBudget(declaredMs, ceilingMs) The smaller of the caller’s number and a provider’s own ceiling. It can only tighten a deadline. budget.spent() True once nothing is left. A retry ladder checks it before retrying a stall. budget.fence(callerSignal) A signal covering what remains, plus the cancel() that clears its timer. A setup chain fences once and passes that signal to every call. isPreResponseStall(error) True when no byte of a response ever arrived. Four rules keep this narrow. A stall is bounded by the budget; a server-directed wait is bounded by the\\ncap. A 429 or 503 carrying retry-after is the server answering and asking for\\na later attempt, so it is not rejected when the first-event budget is gone. Only a\\nfailure where nothing arrived at all is rejected, which makes the guard a veto\\npredicate rather than a fence around a retry loop. The wait itself is bounded\\nseparately: maxRetryDelayMs is the longest single server-directed wait a caller\\nsits on, DEFAULT_MAX_DELAY_MS (60s) when it declares none, and a hint above\\nthat cap surfaces the refusal instead of sleeping on it. Every retrying path\\nreads the caller’s number: fetchWithRetry for the OpenAI-compatible family,\\nBedrock, Ollama and Codex, and the Anthropic client and provider ladder for their\\nown retry-after-ms handling. A deadline that fires ends the phase. A helper that degrades on its own\\ntimeout (GitLab Duo’s settings PUT, its project lookup, its model list, and the\\ncatalog namespace reader behind them) rethrows when the caller’s deadline is what\\nfired. Reporting it as “nothing found” and continuing spends time nobody granted\\nand reaches the user as a configuration remedy for a network fault. A refusal states its remedy. 401, 404, 429 and 400 have four different\\nanswers: fix the credential, fix the route or the model id, wait, fix the\\nrequest. Two shapes collapse them into one wording. A status read for a debug log\\nand then discarded: Cursor’s Connect stream did this, so every refusal arrived as\\n“stream ended without a turn_ended update”. A handshake that treats a refusal as\\none candidate’s silence and reports the remedy for having found nothing: GitLab\\nDuo’s namespace walk did this with a rejected token. A stream that stopped is not a stream that finished. Every dialect ends a\\nturn with its own marker: finish_reason and [DONE], response.completed, message_stop, finishReason, done: true, messageStop, turn_ended. The\\nend of a body without one is a transport-clean EOF that indicates nothing about the\\nturn. Reporting a normal stop there persists whatever arrived as an answer, and\\nthe model reads it back as history on the next turn. Rejecting every such EOF\\nfails turns that were complete, because several compatible servers do not send\\nthe marker. stopReasonForTerminallessEof in utils/terminalless-eof defines the\\njudgement for every dialect: visible text is a stop, reasoning with no answer is\\na length the session can recover, a tool batch counts only when every call\\nparsed, and anything else is an incomplete-stream failure. A provider that\\nseeds stopReason: \\"stop\\" before the first byte and never consults this rule\\nwrites a blank turn into the session, as Bedrock and Ollama did.","breadcrumbs":"Architecture overview » Providers internals » The first-event budget","id":"1174","title":"The first-event budget"},"1175":{"body":"Provider Bound before the first event OpenAI completions, Responses, OpenRouter, Azure Pre-response fence plus the stream watchdog. Anthropic The same, and the retry ladder retries one stall and rejects the next once the phase budget is spent. Codex The same, on both ladders: fetchWithRetry (no response) and the provider-error reopen (a retryable envelope that is itself a stall). GitLab Duo One setup deadline over the whole REST chain: the caller’s number, or 90s (three REST timeouts), whichever is smaller. Bedrock, Google, Vertex, Gemini CLI, Ollama, Cursor, Devin The registered lazy-stream limits and each transport’s own abort. packages/ai/test/no-api-outlives-the-budget-its-caller-declared.test.ts drives\\nevery API in the union against a silent endpoint and pins the observed class per\\nAPI, so a provider that stops honoring the number turns that suite red. packages/ai/test/every-provider-refusal-names-what-to-do-about-it.test.ts\\ndrives the same fourteen against a refusing transport — 401, 404, 429 with\\na two-minute retry-after, and 400 — and pins the class each one surfaces,\\nplus the invariant that no refusal echoes the api key back into its message. packages/ai/test/a-stream-that-stops-mid-turn-is-never-reported-as-a-finished-one.test.ts\\ndrives all fourteen against a 200 that closes without a terminal marker and\\npins each verdict, so a dialect that starts accepting an empty stream as an\\nanswer turns red rather than shipping a blank turn.","breadcrumbs":"Architecture overview » Providers internals » Where each provider’s deadline sits","id":"1175","title":"Where each provider’s deadline sits"},"1176":{"body":"How secret protection is implemented: the modules, the placeholder grammar, the command shapes, and\\nthe vault on disk. The operator guide is Secrets. Sensitive values (API keys, tokens, passwords) are kept out of LLM provider requests. When enabled, secrets are replaced before any provider-bound prompt, message, schema, replay payload, or nested model request leaves the process. Reversible placeholders are restored for local display. A resumed transcript is sanitized again before it is sent.","breadcrumbs":"Architecture overview » Secrets internals » Secrets internals","id":"1176","title":"Secrets internals"},"1177":{"body":"Disabled by default. Storing a credential with /secret turns it on for you, because storing one for the agent to use is the opt-in, and the confirmation reports it. To turn it on without storing anything, use the /settings UI or config.yml directly: secrets: enabled: true Nothing turns it back off on your behalf. Revoking a credential removes it and leaves protection where it is.","breadcrumbs":"Architecture overview » Secrets internals » Enabling","id":"1177","title":"Enabling"},"1178":{"body":"Secrets are collected from three sources at startup and whenever the live secret runtime is refreshed: Environment variables whose names match a keyword from secrets/env-keywords.yml ( KEY, SECRET, TOKEN, PASSWORD, PASS, PASSPHRASE, AUTH, CREDENTIAL, PRIVATE, OAUTH), with values at least 8 characters. Tier B data: a keyword file at /secret-env-keywords.yml or /.veyyon/secret-env-keywords.yml adds to the list and cannot remove from it. See Env keyword list. secrets.yml files (see below). Encrypted vault entries selected for the current profile and working directory. Outbound strings are replaced before provider dispatch. Named vault values use readable placeholders such as #GITHUB_TOKEN#. Unnamed values use a stable machine-keyed HMAC placeholder such as #0A1B2C3D4E5F678901234567#. The keyed form is stable across restarts without exposing an index or an offline dictionary oracle. The final provider boundary works from raw strings before trimming, truncation, serialization, or other lossy transforms. It resolves the live runtime for every physical attempt, including authentication retries, fallback models, delayed queues, compaction, commit analysis, evaluation, benchmarks, memory services, TTS, and image tools. JSON object keys and values are both covered, and key collisions fail closed. Opaque authenticated replay fields are validated rather than mutated. A live secret in a signature, provider item id, encrypted reasoning block, or provider payload rejects dispatch with a value-free error. Provider-bound images are content-detected, decoded, and canonically re-encoded so EXIF, comments, and other container metadata cannot bypass string obfuscation. URLs that appear to carry credentials bypass cloud reader and enrichment services. Local display restoration expands only live reversible placeholders. Replace-mode substitutions are one-way. Expired and removed values lose expansion rights but retain forward redaction tombstones, so old transcript text cannot become provider-visible. Toggling secret protection and running /secret commands rebuilds the runtime immediately, and the system-prompt inventory of spendable names with it. A working-directory move loads the destination project scope transactionally and drops the source project’s mappings. If loading fails, both the old directory and runtime are restored. Persisted subagents and resumed sessions initialize from their recorded directory. A same-directory refresh retains only forward redaction history for removed values.","breadcrumbs":"Architecture overview » Secrets internals » How it works","id":"1178","title":"How it works"},"1179":{"body":"Substitution runs on tool arguments just before a tool executes, so the model can put #GITHUB_TOKEN# in a shell command and Veyyon supplies the credential it never showed the model.\\nThat is recorded by secrets.auditLog, which answers “which credential did this agent use, and\\nwhere” after the fact. A call whose arguments carry a real credential also needs approval, in the same modes as the\\nworking-directory boundary: plan, ask, and auto-edit. The prompt states the secret and never\\nshows its value, and it is added to whatever the tier already required, so it can only require more\\napproval and never less. yolo opts out of all permission and opts out of this with it, so the\\nshipped default requires nothing extra. An unknown placeholder contains no credential and does not prompt.\\nIf the name was advertised earlier in this process and expansion is later removed or disabled, the\\ntool call is rejected before approval instead of running with stale literal text. See Approval modes.","breadcrumbs":"Architecture overview » Secrets internals » Spending a secret prompts first","id":"1179","title":"Spending a secret prompts first"},"118":{"body":"A BYOK (bring-your-own-key) run needs three facts: Fact What it is Where it lives Endpoint Base URL and API kind A built-in provider, or a custom provider under providers: in ~/.veyyon/profiles/default/agent/models.yml Model The model id the endpoint understands Pinned with --model / /model, or discovered from the provider Key Credential the endpoint accepts A provider environment variable, /login, or a models.yml apiKey For BYOK providers, Veyyon calls the configured endpoint with your credentials (no hosted proxy required).\\nOptional OpenTelemetry export is separate and only when OTEL_EXPORTER_OTLP_* is set.","breadcrumbs":"Core concepts » Model contract » The three things you bring","id":"118","title":"The three things you bring"},"1180":{"body":"Veyyon writes one diagnostic entry when a tool starts, so a session that dies mid-call can tell you\\non resume which call was still running. The entry keeps a truncated copy of the command or path\\nargument and the model’s stated intent. Those arguments are the expanded ones, because expansion has already happened by then. They are\\nredacted before the entry is written, so the session file records printf \'%s\' \'#GITHUB_TOKEN#\'\\nand never the credential. Redaction runs before truncation, so a value sitting across the\\n200-character cut cannot leave a readable prefix behind. The redaction survives a /secret disable:\\nthe tombstone that keeps an old value hidden from providers keeps it out of this entry too. The full arguments the model wrote are persisted with the assistant message, and those hold the\\nplaceholder, since the model never saw anything else. What the command printed is a different matter. Tool output is saved as it was printed and\\nredacted on its way to the provider, not on its way to disk, so a command that echoes a credential\\nputs it in the session file. Veyyon redacts what it records itself; it cannot redact what a command\\nchose to print. Two modes control what happens to each secret: Mode Behavior Reversible obfuscate (default) Replaced with a named or machine-keyed HMAC placeholder Yes, while the entry is live replace Replaced with a deterministic safe same-length string No","breadcrumbs":"Architecture overview » Secrets internals » What the session file records about the call","id":"1180","title":"What the session file records about the call"},"1181":{"body":"obfuscate mode replaces every occurrence of the value, so a very short secret would blank out fragments of ordinary words. Values under 8 characters are therefore rejected rather than protected, and the refusal is loud: A plain obfuscate entry under 8 characters stops startup with an error stating the entry and the fix. It is not skipped. Skipping it would send the value to the provider while the file stated otherwise. Use mode: replace for a short value. Replace is one-way, needs no reversible placeholder, and has no minimum. A regex match under the floor is skipped rather than rejected, because a short match usually means the pattern reached into ordinary prose. The skip is recorded once per pattern so you can see that the pattern is over-matching. If short matches are genuinely secret, set minLength on that entry. An unreadable or malformed secrets.yml also stops startup. A missing file does not: nothing was declared, so there is nothing to protect. The distinction matters because reading a broken file as “no secrets” starts a session that believes it has nothing to hide.","breadcrumbs":"Architecture overview » Secrets internals » The 8-character minimum","id":"1181","title":"The 8-character minimum"},"1182":{"body":"validateEntry rejects a malformed entry instead of warning and dropping it. The default transport set is { file: true } with no console transport ( logger.ts:219), so a warn-and-drop hides the fault in a log file and sends the credential the operator declared to the provider in plain text. Problems accumulate and are reported together, so three typos cost one restart. The message states the entry index, the field and the fix. It never quotes the offending content: on a plain entry that content is the credential. Unknown fields, and fields that do not apply to an entry type, are errors. Regex declarations also reject duplicate or incompatible flags, sticky or zero-width matching, and conservatively detected catastrophic-backtracking forms. These checks run before a session can send provider traffic.","breadcrumbs":"Architecture overview » Secrets internals » Per-entry validation is a refusal, not a skip","id":"1182","title":"Per-entry validation is a refusal, not a skip"},"1183":{"body":"Two stores feed the obfuscator. secrets.yml below is declarative and plaintext. The vault is imperative and encrypted: entries are added at runtime with /secret, are named, and expire.","breadcrumbs":"Architecture overview » Secrets internals » The vault ( /secret)","id":"1183","title":"The vault ( /secret)"},"1184":{"body":"parseSecretCommand(args, surface) reads one of two grammars, and surface alone selects between them. runSecretCommandForSurface passes \\"noninteractive\\" when port.promptForValue is absent, which is the case where the client cannot hide what is typed, and \\"tui\\" otherwise. The branch is on the surface and never on the shape of the input, so nothing an operator types moves them from one grammar to the other. Both grammars require a command first, and what differs is where the value may come from. A first word that is not a command is nothing: Typed Parsed as /secret { subcommand: \\"help\\" }, on both surfaces. /secret rejected. Nothing is stored, and the refusal never repeats the word. /secret ... that subcommand, or a refusal when the rest of the line does not fit its shape. Never a credential. /secret add { subcommand: \\"add\\" }. needsValuePrompt is then true, so the surface opens the masked field. /secret add { subcommand: \\"add\\", value }, sliced from the first token’s start to the last token’s end. A credential may therefore begin with a reserved word. /secret add -- ... rejected, stating the plain word that replaced --. Nothing is stored. /secret from-env VAR [NAME] [7d] [project] { subcommand: \\"from-env\\", fromEnv, name?, ttl?, scope? }. Its own command, not a modifier on add. The name is required on a client and optional in a terminal, where a field prompts for it. The slice rather than a trim drops the whitespace a terminal adds around what was typed and preserves, byte for byte, any whitespace inside the credential, because a passphrase may contain spaces. A value is read in exactly one place, after add. The reserved words are the keys of SECRET_VERB_SPELLINGS: every canonical subcommand plus the second spellings env, remove, delete, wipe, purge, empty, reset, name, replace, move, renew and audit. They are a list of what runs, not a list of what a value may not start with. A reserved word whose remainder does not fit its shape is rejected, because falling back to storage would turn /secret log 50 into a stored credential reported as a success. The refusal states the exposure, and never the bytes. A terminal refusal states that nothing was stored, that the line is exposed, and to rotate the credential, and never echoes the word, because that word is very often the credential. The noninteractive refusal drops the scrollback sentence: its line came from argv rather than a screen. -- is rejected as the first word after add, stating the plain word that replaced it, rather than passed to the value reader, which would slice -- sk-live-x verbatim and store a credential with the dashes attached. That failure is invisible until the credential is spent, and then surfaces as an authentication error with nothing connecting it to a slash command. The match is on the whole first word after add, so a value that merely begins with dashes, a PEM block for instance, is stored byte for byte. No /secret command takes an option. --from-env, --ttl, --scope, --limit and --name are rejected, each stating the plain word that replaced it. A word is read by the POSITION it sits in, or by a CLOSED SET or SHAPE it belongs to. Position covers every required word, so /secret rm PROFILE removes the secret named PROFILE. Shape covers trailing words that may be omitted or reordered, and only where the sets cannot overlap: a vault is one of exactly three words, a lifetime is isTtlWord or any digit-leading word, a limit is digits only, a secret name may not begin with a digit and may not contain a hyphen. Each slot states its own disjointness proof in SECRET_SUBCOMMAND_SHAPES. from-env is a command of its own rather than a modifier on add, and it takes the lifetime and the vault that the value forms cannot: /secret from-env DEPLOY_KEY DEPLOY_TOKEN 30m project is a complete store. A terminal add takes the secrets.defaultTtl lifetime and the default profile vault, because everything after add is the credential; /secret extend sets a different lifetime afterwards and /secret scope moves it, from the same prompt. The terminal form takes no name. /secret add had no unique reading once the value was arbitrary text, and /secret add ghp_realToken stored a live credential as a NAME with no value attached. The value is the whole line, and the name is prompted afterwards. The name is prompted last. runSecretCommandForSurface calls port.promptForName() once a value is in hand, for a pasted value, a masked one and a from-env one alike, because all three arrive without a name. That field is visible: a label is not a credential, and maskedPromptTitle states “value, not a name” because the masked field is the one place the two can be confused. An empty answer keeps the generated name. Escape abandons the store rather than falling back to a generated name. One asymmetry. A client with no terminal cannot accept a credential the caller types, so add is rejected there and from-env requires the name a terminal prompts for in a field afterwards. Everything else parses identically: Subcommand Purpose /secret from-env Store the value of an environment variable. The credential is never typed. The only entry form a client with no field accepts: an inline value is rejected, because it would be retained in the client’s request history. In a terminal the name may be omitted and is prompted afterwards. /secret list An aligned table of placeholders, scopes and lifetimes, plus a STATUS column when a row is near expiry. Never values, not even a prefix. /secret rm Remove the entry that is currently in effect, and tell the model that its placeholder is revoked. /secret rename Relabel an entry, keeping its value, creation time and expiry. Refused when the new name is taken. /secret value Replace an entry’s value, keeping its name, scope, creation time and expiry. Takes a masked field, or the trailing pair from-env . /secret scope Move an entry to another vault. Refused when the destination holds that name, and what moves is the lifetime REMAINING. /secret copy Hand the surface #NAME# to put on the clipboard. Never the value. /secret extend 7d Give an entry a fresh lifetime, measured from now, and tell the model the placeholder is still live. /secret log [] [] The expansion log: which placeholder went into which command, when. A name narrows it to one credential; a number sets how many records. Either order, because a name may not begin with a digit. /secret discard Move one vault’s unreadable file aside so that vault works again. Never deletes it. A word neither grammar reserves is rejected where a value cannot be typed, and the refusal prints the whole usage without repeating the word: the caller cannot open a help screen, and the unknown first token is very often the credential itself. Both grammars produce the same SecretCommandRequest and run through the same runSecretCommand, so the two cannot drift into different ideas of what a lifetime or a scope means. secretCommandUsage(surface) picks help to match, and the credential-entry lines are the only text the two help outputs disagree about. They are named once rather than written out twice: a surface with no way to hide what is typed must never advertise typing a credential. Each slot belongs to the commands that read it, and SECRET_SUBCOMMAND_SHAPES is the one owner of that mapping: Word Read by How it is recognised an environment variable from-env (position 1), value (after from-env) position, and a keyword on value because a variable name is arbitrary text a name every command that takes one entry position, except on log where it is the non-numeric trailing word a lifetime from-env, extend 30m, 12h, 7d, 2w, never, or any digit-leading word a vault from-env, rm, clear, scope, discard one of profile, project, global. Position on clear, scope and discard; trailing on from-env and rm a limit log digits only, and a safe integer SECRET_SUBCOMMAND_SHAPES also records how many words each command reads and which are required: one for rm, value, copy, clear and discard, two for rename, scope, extend and from-env, none for list, log and help, and unbounded for a terminal add. add is unbounded because the whole line after it is rejoined into the credential and a passphrase contains spaces, so a word count would reject /secret add gpg my long pass phrase as five arguments when it is two. clear and discard read one word and it is a vault rather than a name, because each acts on a whole file. A word a command does not read is rejected, stating the position it arrived in. An earlier grammar parsed every option for every verb and let each subcommand read only the fields it cared about, so /secret extend NAME --scope global reported success and did nothing about the scope, and /secret rm NAME --scope project read as “the project copy is gone” when the copy in effect had been removed and the others were untouched. The rule covers plain words too: /secret extend TOKEN global is rejected rather than re-dated with the vault word ignored. The refusal states the POSITION and never repeats the word. The common slip is muscle memory for add under another command ( /secret extend TOKEN sk-live-..., /secret rm TOKEN sk-live-..., a value appended to /secret list), so the extra word is very often the credential, and quoting it would write that credential into the scrollback and the saved transcript permanently. A digit-only word is echoed, because a number cannot be a credential and the echo is what makes the hint useful: /secret rm TOKEN 50 can then state what a bare number would have meant. In a terminal the refusal also states the value form. needsValuePrompt sets whether a surface prompts, and it lives in the pure command layer so the TUI and text/ACP paths cannot disagree about when a masked field is warranted. A surface that cannot mask must not substitute an unmasked prompt: absent promptForValue, runSecretCommand rejects the add and names from-env. That same absence is what selects the grammar, so a client is never offered a field it cannot open.","breadcrumbs":"Architecture overview » Secrets internals » Two grammars, selected by surface","id":"1184","title":"Two grammars, selected by surface"},"1185":{"body":"load() skips a scope whose file exists and cannot be read, with a notice, and remove() will not touch one. Between them the operator could start and could not repair: discardUnreadableScope had no caller, so the only route was deleting the file by hand. /secret discard is that route, and it parses on every surface, so one notice states one repair whichever client prints it. It moves the file to a vault.json.unreadable-- sibling rather than deleting it. The file still holds real credentials, sealed with a key that is still on disk, so the damage may be a truncated tail with recoverable entries behind it. The new path is returned and printed: it is the operator’s only route back to those entries, and a message that omitted it would make a recoverable move indistinguishable from a delete. The vault is required here and defaulted everywhere else, the one exception in the table above. Elsewhere the word states where to PUT something, and /secret list shows a wrong guess. Here it selects a file to move aside, so a default would let a bare /secret discard move a working vault out from under the session. It sits at position 1 rather than trailing, because discard takes no name. The refusal contains the usage and states that there is no default, and the guard is repeated at the dispatch as well as the parser, because ACP and other adapters build a request object without going through parseSecretCommand. Two refusals: A scope that reads normally. Checked inside discardUnreadableScope, under the file lock, rather than trusted from an earlier load(), because the file may have been repaired in between. The refusal names /secret rm , which can state what it removed. A scope whose path is also another scope’s vault. A profile directory that is the config root makes the profile and global vaults one file, so moving it aside as one takes the other with it. #scopePathOwner resolves the owner by file identity and the refusal states it, because an operator told only “cannot discard” uses rm on the file and loses both. Afterwards the result carries changed: true, so the surface rebuilds the obfuscator: the scope’s file has stopped existing at the path the loader reads, and until it reloads the session holds the pre-discard view. The moved-aside file keeps mode 0600.","breadcrumbs":"Architecture overview » Secrets internals » discard: the repair for a vault that cannot be read","id":"1185","title":"discard: the repair for a vault that cannot be read"},"1186":{"body":"Input.mask on the shared TUI component is the single place a value becomes something a terminal can show, so masking is one projection applied in render rather than a second text field. maskValue emits one mask character per grapheme and maps the cursor to the grapheme count before it, so an astral character or a combining sequence counts once. getValue still returns what was typed: masking the buffer itself would store a row of bullets as the credential. The masked prompt is showHookInput, which is local only. Unlike the selector and editor dialogs it is never raced against a collab guest, so a masked field cannot be answered from another machine. request.maskedEntry records that a value came from the field rather than the command line, and only the confirmation text depends on it. A scrollback warning that fires when it does not apply is one an operator learns to skip, including on the inline path where it is true.","breadcrumbs":"Architecture overview » Secrets internals » Masked entry","id":"1186","title":"Masked entry"},"1187":{"body":"A vault entry’s placeholder is its name, so the model sees #GITHUB_TOKEN#. That is what lets it choose between several credentials deliberately, and it makes the placeholder stable across sessions. Names are 5 to 64 characters of A-Z, 0-9 and _, starting with a letter. Unnamed HMAC placeholders start with the reserved digit 0, so a name can never collide with one. normaliseSecretName accepts what people type ( github-token, github token, lowercase) and uppercases it. It rejects non-ASCII input before uppercasing, so Unicode case expansion cannot alias an existing name. Entries without a name get a generated name ( SECRET_1), so every vault entry has a placeholder the model can reference. Plain environment and secrets.yml values use the machine-keyed unnamed form.","breadcrumbs":"Architecture overview » Secrets internals » Named and unnamed placeholders","id":"1187","title":"Named and unnamed placeholders"},"1188":{"body":"Argument completion offers the subcommands and nothing else, derived from SECRET_TUI_SUBCOMMANDS, which the parser builds from the same table it routes with. A verb cannot be typeable and unoffered, and a word cannot be offered and unparseable. The operator-facing account is Managing what you stored. No stored NAME is ever offered. Completing one from session.obfuscator.namedSecretNames() renders part of the vault on a keystroke, and accepting a suggestion writes it onto a line whose first word decides between a command and a credential, so a fumbled verb stores the suggestion instead of running it. /secret list is where names are read. The prefix filter keeps the menu out of a paste. A pasted credential arrives as one insert, so the prefix is the whole token and matches nothing; only a hand-typed word that is the start of a subcommand opens the dropdown. Nothing about the vault is read to build it, so completion still works when secret protection is off.","breadcrumbs":"Architecture overview » Secrets internals » Completing /secret","id":"1188","title":"Completing /secret"},"1189":{"body":"The operator-facing account is What the agent knows, and when. Two mechanisms carry it, with different jobs. The inventory is a system-prompt section. SecretObfuscator.namedSecretNames() returns every readable name the live runtime can expand, sorted, and never a value. It calls #forgetExpired() first, so a name stops being answered at the moment it stops working. That list becomes an optional option-backed runtime section registered in RUNTIME_SECTIONS ( system-prompt-builder/section-registry.ts) and supplied where sdk.ts calls the system-prompt builder, beside secretsEnabled. AgentSession.refreshSecrets() reloads the runtime and rebuilds the base prompt, which is what makes a removed or expired name stop appearing. Sorted because the section sits in the cached prompt prefix. Map insertion order would shuffle between refreshes and invalidate the provider’s prompt cache without changing the section text. An optional section renders only when its option is present, so protection being off, or nothing being spendable, produces no section rather than an empty heading. Names are listed in placeholder form, which is the form the model has to write. Index-form secrets are absent, having no name to list. It belongs in the prompt rather than in the conversation because the vault outlives the conversation. Vault entries are profile, project or global scoped and persist across sessions; a notice injected into history does not. Before this, a credential stored yesterday was live this morning and unknown to the model that could spend it. The notice is a developer message. runSecretCommand returns agentNotice for add, rm and extend; list, log and help return none. tellTheAgent ( slash-commands/helpers/secret.ts) appends it to the live agent and to the session file, because only the first leaves a resumed session holding a placeholder it was never introduced to, and only the second withholds the news until the next restart. rm states the revocation rather than leaving it to the name’s disappearance from the inventory. A model does not reliably notice an absence. The tool boundary also keeps the exact retired placeholder name in memory and rejects a later attempt to spend it, while unknown text such as #TODO# remains ordinary input. The removal notice is still delivered even when secrets.enabled is off because it reaches the model and persists in resumable history; the boundary is the local backstop that prevents an ignored notice from becoming a confusing remote authentication failure. A revoked placeholder is already in the history; a new one with protection off has nothing to expand into. No notice contains a lifetime. A duration is accurate when written and wrong afterwards, and the operator reads the exact time left from the terminal confirmation instead. Expiry that no command triggered has no notice at all. The name leaves the inventory on the next rebuild, #forgetPlaceholder has already revoked expansion, and the operator hears about it through OperatorNotices. No path puts a value in front of the model. The inventory carries names, the notices carry names, and substitution happens after the model has written the placeholder.","breadcrumbs":"Architecture overview » Secrets internals » What the model is told about a stored secret","id":"1189","title":"What the model is told about a stored secret"},"119":{"body":"# ~/.veyyon/profiles/default/agent/models.yml\\nproviders: deepseek: baseUrl: https://api.deepseek.com api: openai-completions apiKey: DEEPSEEK_API_KEY # env-var name; unset means no key, not a literal models: - id: deepseek-chat name: DeepSeek Chat contextWindow: 128000 maxTokens: 8192 $ export DEEPSEEK_API_KEY=sk-...\\n$ veyyon --model deepseek/deepseek-chat","breadcrumbs":"Core concepts » Model contract » Example shape","id":"119","title":"Example shape"},"1190":{"body":"Scope Path global ~/.veyyon/vault.json profile (default) /vault.json project /.veyyon/vault.json Narrowest scope wins a name clash. rm and extend walk scopes narrowest-first, so they act on the entry list shows. Encryption is AES-256-GCM with a fresh 12 byte nonce and a full 16 byte authentication tag per write. The key is 32 random bytes at ~/.veyyon/vault.key, created on first use and never stored inside a project tree. On POSIX, the key is mode 0600 and its directory must be owned by you and not writable by another user. On Windows, Veyyon applies and verifies a protected owner-only ACL. Vault updates use a synchronized owner-only temporary file. Kernel no-replace and exchange operations publish the synced inode without overwriting a destination that appeared after the last check. Each transaction keeps the scope directory open and performs file I/O through that descriptor. Replacing the lexical directory during a transaction therefore causes a hard error instead of redirecting the read or write. Read and write paths reject symlinks, hard links, directories, devices, insecure permissions, and other non-regular files. Scope checks resolve real parent directories. The authenticated location includes the semantic scope, canonical path, and physical scope-directory identity. Copying or backing up vault.json preserves confidentiality, but the ciphertext is not a portable restore artifact. Store those entries again after moving or recreating the scope directory. The sealed descriptor is limited to 8 MiB before allocation. Writes also enforce a 6,291,402-byte encoded plaintext limit before JSON serialization, encryption, or Base64 expansion. Failure behavior is fail-closed: Condition Behavior No vault file Empty. Nothing was stored. Vault present, key missing Hard error. Never read as an empty vault. Key of wrong length Hard error, so a new key is not written beside a recoverable one. Unsafe key directory, key file, or vault permissions Hard error stating the permission fix. POSIX ownership and modes and Windows owner-only ACLs are checked. Symlink, hard-linked file, or non-regular key/vault path Hard error. The path is never followed or shared. Ciphertext, nonce, authentication tag, scope, canonical path, or physical scope identity modified Hard error. GCM authenticates the complete envelope and its location. Legacy version 1 envelope Hard error directing the operator to re-add the entry in the bound current format. Unknown envelope version Hard error advising an upgrade rather than deletion. Entry name or value contains ill-formed UTF-16 Hard error before a write, or after authenticated decryption during a read. Existing ciphertext is left unchanged.","breadcrumbs":"Architecture overview » Secrets internals » Storage","id":"1190","title":"Storage"},"1191":{"body":"secrets.defaultTtl sets the default ( 1d). An absent setting uses the built-in default; a setting that does not parse is an error rather than a silent fallback. Expiry has two ordered effects. At use time, the live obfuscator revokes placeholder expansion and installs a forward-only HMAC tombstone for the old raw value. This prevents a transcript containing that value from becoming provider-visible. The hot path performs no vault I/O, so the encrypted entry remains on disk until the next successful vault refresh prunes it. Expiry is enforced at use time as well as at load: The check sits on deobfuscate, hasNamedSecret and knowsPlaceholder, so no path reaches a value without passing it. #nextExpiryAt caches the soonest deadline, so the hot path is one number comparison and the map is scanned only when a deadline is crossed. A lapse calls onExpiry with explicit persisted-deletion state. sdk.ts renders an operator notice that states expansion was revoked and, until a vault refresh succeeds, that encrypted ciphertext remains. A successful vault refresh prunes expired entries before rebuilding the runtime. addNamedSecret takes the deadline, so /secret extend moves the moment substitution stops. #forgetPlaceholder is the one owner of revoking reverse mappings and installing forward redaction tombstones. WARN_AT_FRACTIONS ( [0.5, 0.9]) is the single owner of when a warning fires, as fractions rather than absolute times so one rule serves 1d and 90d alike. expiryWarnings consults warningThresholdCrossed rather than doing its own comparison: it previously held an inline 0.9, which meant two owners disagreeing and a halfway warning that could not fire. The wording reads the urgent threshold off the end of the list for the same reason, so adding a 0.99 would not leave a secret with minutes left described as “over halfway through its lifetime”. expiryUrgency wraps that one comparison and classifies an entry as soon or halfway, so the STATUS column in /secret list reads the same thresholds as the warnings rather than becoming a third owner of the question. Warnings are raised at session startup through OperatorNotices, and the channel collapses repeats so a long-running session is told once. Each line states the /secret extend command that prevents the loss, since expiry is not recoverable after the fact.","breadcrumbs":"Architecture overview » Secrets internals » Lifetimes","id":"1191","title":"Lifetimes"},"1192":{"body":"secrets.auditLog (default on) records each tool call that mentioned a secret, one JSON object per line, to /secret-audit.jsonl. Field Meaning at Epoch milliseconds at expansion. secrets Placeholders substituted, in order of appearance, deduplicated. tool Tool that received them. session Session id. Omitted when the session has none yet. /secret log states how many distinct sessions the shown records came from, since the log is per-profile and two windows append to one file. command The arguments as the model produced them, JSON-encoded. truncated true when command was cut to fit the byte cap. omittedSecrets Number of additional placeholder references omitted to keep the encoded record under the byte cap. Written from the arguments before substitution, which is the form in which every secret is still a placeholder. That ordering is the safety property: there is no redaction step to get wrong and no way for a value to reach the file. buildExpansionRecord receives the pre-expansion arguments and nothing else. MAX_RECORD_BYTES (2048) is a security and concurrency boundary. Every field and placeholder list is bounded before encoding. Placeholder discovery walks JSON string values and object keys in the same order as expansion. A cross-process file lock covers the size check, atomic rotation rename, append, and generation reads, so two sessions cannot overwrite a rotated generation or push a record past the cap. Failure behaviour differs from the vault’s, deliberately. Obfuscation is the preventive control and it fails closed; the log is a detective control, so a failed append raises an operator notice and the command still runs. Refusing to execute a tool because a log file could not be written turns a full disk into an agent outage while nothing is actually unsafe. What is not permitted is silence: a log that stopped recording must not look like a log with nothing to record. The log is written in the profile directory, never the project one, and is written 0600. It states which credentials exist and when they are used, which is reconnaissance even without values. Three properties hold: Rotation. ROTATE_AT_BYTES (2 MiB, about ten thousand uses) atomically moves the file to secret-audit.jsonl.1 and starts a fresh one, keeping two generations. The same cross-process lock covers both sessions that race at the boundary. read spans both generations, so /secret log 20 immediately after a rotation still answers with twenty records. Full validation on the way back in. A parsed line is accepted only when every field the renderer reads has the right type. The check was typeof at === \\"number\\" && Array.isArray(secrets) followed by a cast, so a line missing tool printed undefined in the middle of a security report. Anything that fails is counted as malformed and the count is shown, never dropped. Terminal control characters in records, paths, and notices are escaped before display. Hard-linked generations are rejected, and the 2 MiB generation limit is checked before allocating a read buffer. Flushed on dispose. Appends are queued so a tool call is never blocked by a write, which means an exit that does not drain the queue loses records silently. the session’s dispose() awaits flush(), because quitting ends the process rather than waiting for pending work, and the last credential used is the one an incident concerns.","breadcrumbs":"Architecture overview » Secrets internals » The expansion log","id":"1192","title":"The expansion log"},"1193":{"body":"OperatorNotices ( session/operator-notices.ts) is the one channel for a non-fatal problem the operator must see. It exists because there was none: logger.warn writes to a file with no console transport, and AgentSession.skillWarnings was a getter that production code never read, so skill-loading problems were discarded silently while the field looked like a surface. Both now route here. Notices buffer until a sink attaches, because they are raised while a session is being built and the TUI does not exist yet. Interactive mode passes a sink-less collector to createSession and attaches its own after the first render; every other mode uses the default, which writes to stderr as notices arrive. A caller that attaches nothing gets its notices in the wrong place, never dropped. Identical notices collapse on severity + source + text, keeping the first timestamp. A problem detected once per turn would otherwise train the operator to ignore the channel, which ends in the same silence by another route.","breadcrumbs":"Architecture overview » Secrets internals » Operator notices","id":"1193","title":"Operator notices"},"1194":{"body":"secrets/env-keywords.ts defines the keyword list and the boundary rule; nothing else matches an environment variable name. The list was an inline regex in secrets/index.ts and is Tier B data now, so an operator can extend it without editing source. The boundary rule is (?:)(?:_|$), case-insensitive: a keyword matches only where it ends the name or is followed by an underscore. Candidate Decision Reason PASSPHRASE added The one genuine gap. GPG_PASSPHRASE matched only because of the underscore; a bare PASSPHRASE matched nothing, because PASS is followed by P. No common non-secret variable is named *PASSPHRASE, so there is no false positive traded away. APIKEY no entry needed KEY at the end of a name already matches it. PRIVKEY no entry needed Same. SECRETKEY no entry needed Same. PWD rejected The POSIX current-working-directory variable, present in every shell, with a value that is almost always over the length floor. Detecting it would replace the working directory with a placeholder in every message mentioning a path: text corruption, not protection. OLDPWD is the same. Three of the five filed candidates turned out to be already covered, which is why the list stays short: the trailing-position half of the boundary rule does most of the work. User files ADD ONLY. A project file that could remove TOKEN would let a cloned repository turn off protection for whoever opens it, which is the wrong direction for a detection list to be configurable in. A missing file is empty; an unreadable or malformed one throws, the same asymmetry secrets.yml uses. buildEnvSecretPattern([]) matches NOTHING rather than emitting an empty alternation that would match every name, and every keyword is regex-escaped because a user file is arbitrary text.","breadcrumbs":"Architecture overview » Secrets internals » Env keyword list","id":"1194","title":"Env keyword list"},"1195":{"body":"Define custom secret entries in YAML. Two locations are checked: Level Path Purpose Profile ~/.veyyon/profiles/default/agent/secrets.yml (active agent dir) Profile-wide secrets Project /.veyyon/secrets.yml Project-specific secrets Project entries override profile entries with matching content. The profile level is called profile here and everywhere else the agent directory appears, including the vault’s scope table above. It was labelled “Global” in this table alone, which read as ~/.veyyon and is a different directory.","breadcrumbs":"Architecture overview » Secrets internals » secrets.yml","id":"1195","title":"secrets.yml"},"1196":{"body":"Each entry in the array has these fields: Field Type Required Description type \\"plain\\" or \\"regex\\" Yes Match strategy content string Yes The secret value (plain) or regex pattern (regex) mode \\"obfuscate\\" or \\"replace\\" No Default: \\"obfuscate\\" replacement string No Custom replacement (replace mode only) flags string No Regex flags (regex type only) minLength positive integer No Shortest match this pattern will obfuscate. Regex entries only; default 8","breadcrumbs":"Architecture overview » Secrets internals » Schema","id":"1196","title":"Schema"},"1197":{"body":"Plain secrets # Obfuscate a specific API key (default mode)\\n- type: plain content: sk-proj-abc123def456 # Replace a database password with a fixed string\\n- type: plain content: hunter2 mode: replace replacement: \\"********\\" Generated replace aliases use counter-mode HMAC with the machine placeholder key. A custom replacement that looks like #NAME# or a machine-keyed placeholder is rejected, so one-way output cannot be reinterpreted as a live credential. Emitted placeholders are protected spans: later literal or regex rules cannot scan inside and corrupt them. Regex secrets # Obfuscate any AWS-style key\\n- type: regex content: \\"AKIA[0-9A-Z]{16}\\" # Case-insensitive match with explicit flags\\n- type: regex content: \\"api[_-]?key\\\\\\\\s*=\\\\\\\\s*\\\\\\\\w+\\" flags: \\"i\\" # Regex literal syntax (pattern and flags in one string)\\n- type: regex content: \\"/bearer\\\\\\\\s+[a-zA-Z0-9._~+\\\\\\\\/=-]+/i\\" # A six-digit one-time code is shorter than the default floor, so say so\\n- type: regex content: \\"\\\\\\\\b[0-9]{6}\\\\\\\\b\\" minLength: 6 Regex entries always scan globally (the g flag is enforced automatically). The regex literal syntax /pattern/flags is supported as an alternative to separate content + flags fields. Escaped slashes within the pattern ( \\\\\\\\/) are handled correctly. Alternations whose branches can consume concatenated prefixes are rejected along with nested ambiguous quantifiers. This prevents exponential backtracking even when the ambiguity is spread across alternatives. Only standard, bounded global matching is accepted. The sticky y flag and expressions that can match an empty string are rejected because their scan semantics can skip text or make no progress. Nested ambiguous quantifiers and related catastrophic-backtracking forms are rejected before compilation. Regex replacement rewrites exact match spans rather than every equal substring elsewhere in the message. Replace mode with regex # One-way replace connection strings (not reversible)\\n- type: regex content: \\"postgres://[^\\\\\\\\s]+\\" mode: replace replacement: \\"postgres://***\\"","breadcrumbs":"Architecture overview » Secrets internals » Examples","id":"1197","title":"Examples"},"1198":{"body":"Environment variables are collected first, then file-defined entries are appended. File entries can cover secrets that do not live in environment variables, such as values in local configuration. Equal plain values converge on the same machine-keyed placeholder, so their provider representation is independent of declaration order.","breadcrumbs":"Architecture overview » Secrets internals » Interaction with env var detection","id":"1198","title":"Interaction with env var detection"},"1199":{"body":"packages/coding-agent/src/secrets/audit.ts – the expansion log: record shape, atomic-append cap, reader packages/coding-agent/src/secrets/env-keywords.ts + env-keywords.yml – the Tier B keyword list and the boundary rule, one owner packages/coding-agent/src/secrets/index.ts – loading, merging, env var collection, refusal of unprotectable entries packages/coding-agent/src/secrets/obfuscator.ts – SecretObfuscator, message obfuscation, runtime add/forget packages/coding-agent/src/secrets/placeholder.ts – both placeholder forms and the rule keeping them apart packages/coding-agent/src/secrets/policy.ts – the length rules and the rejection type, defined once packages/coding-agent/src/secrets/regex.ts – regex literal parsing and compilation packages/coding-agent/src/secrets/secret-command.ts – /secret logic, pure and session-free packages/coding-agent/src/secrets/scope-move.ts – planScopeMove: the two refusals that make a scope move safe to perform as add-then-remove packages/coding-agent/src/secrets/vault.ts – entries, lifetimes, scopes, the store packages/coding-agent/src/secrets/vault-crypto.ts – the key, the seal, and the threat model packages/coding-agent/src/slash-commands/helpers/secret.ts – the session-bound adapter shared by the TUI and text/ACP paths packages/coding-agent/src/system-prompt-builder/section-registry.ts – the runtime section row that puts the inventory of spendable names in the base system prompt packages/coding-agent/src/session/operator-notices.ts – the one channel for a warning that must reach a person packages/tui/src/components/input.ts – Input.mask and maskValue, the one place a value becomes visible text packages/coding-agent/src/config/settings-domains/providers.ts – the three settings: secrets.enabled, secrets.defaultTtl, secrets.auditLog","breadcrumbs":"Architecture overview » Secrets internals » Key files","id":"1199","title":"Key files"},"12":{"body":"Model configuration separates model selection from subsystem roles: The interactive model is set with /model or --model and persists as modelRoles.default. Roles pin a model to specific workloads, such as smol for lightweight operations or advisor for review. Custom roles are defined in modelRoles. See Models, roles, and profiles. Overrides are explicit subsystem policies. compaction.model overrides the interactive model for compaction, otherwise compaction inherits it. Subagent models are configured via subagent policies in settings. Cycling rotates through cycleOrder (defaulting to smol then slow), bound to app.model.cycleForward.","breadcrumbs":"Design and mechanisms » Mechanisms » Model slots and roles","id":"12","title":"Model slots and roles"},"120":{"body":"These behaviors stay constant no matter which endpoint you point at: The workflow: read, edit, verify, stop when the work is done. Tool dispatch, argument handling, and edit verification through the hashline edit engine\\n(with apply_patch / patch / replace available via edit.mode). Approval modes ( tools.approvalMode) that gate which tool tiers run without asking. Context compaction, goal cards, session branching, and rollout persistence. Per-model prompt order and tool-form selection once a model (or API kind) is known. Provider is configuration (endpoint, credentials, model id). Keep the same\\ncommands.","breadcrumbs":"Core concepts » Model contract » What the harness owns","id":"120","title":"What the harness owns"},"1200":{"body":"auth-broker-gateway.md – remote credential vault and forward-proxy that keep provider OAuth refresh tokens and access tokens off developer hosts entirely (complementary to in-process obfuscation).","breadcrumbs":"Architecture overview » Secrets internals » See also","id":"1200","title":"See also"},"1201":{"body":"When a memory backend is enabled, the agent automatically extracts durable knowledge from past sessions and injects a compact summary into future sessions for the same project. Over time it builds a project-scoped memory store, technical decisions, recurring workflows, pitfalls, that carries forward without manual effort.","breadcrumbs":"Architecture overview » Memory internals » Autonomous Memory","id":"1201","title":"Autonomous Memory"},"1202":{"body":"memory.backend selects the subsystem (default off): Value What it is off No memory subsystem runs. local Local rollout-summarisation pipeline described on this page ( MEMORY.md / memory_summary.md / generated skills). mnemopi Local SQLite recall/retain backend with optional embeddings; the agent uses the recall, retain, and reflect tools. mnemopi.* settings tune it. hindsight Vectorize Hindsight remote memory service. The rest of this page documents the local pipeline. Enable it via /settings or config.yml: memory: backend: local","breadcrumbs":"Architecture overview » Memory internals » Backends","id":"1202","title":"Backends"},"1203":{"body":"","breadcrumbs":"Architecture overview » Memory internals » Usage","id":"1203","title":"Usage"},"1204":{"body":"At session start, if a memory summary exists for the current project, it is injected into the system prompt as a Memory Guidance block. The agent is instructed to: Treat memory as heuristic context: useful for process and prior decisions, not authoritative on current repo state. Cite the memory artifact path when memory changes the plan, and pair it with current-repo evidence before acting. Prefer repo state and user instruction when they conflict with memory; treat conflicting memory as stale. A backend contributes in two places, and which one it uses matters for what a session costs you: The system prompt contains the guidance that does not change while the session runs. The provider caches the prompt as\\nthe prefix of every request, so this text is paid for once. The context tail carries whatever changes as you work: memories recalled for the current question, and the mental-model\\nblock when it reloads. These arrive as a message alongside your prompt. The split exists because changing the system prompt mid-session invalidates the provider’s cache prefix, and the next request\\nre-reads the whole conversation at the uncached rate. Writing a recalled memory into the prompt made every recall cost a full\\nre-read of everything before it. The model sees the same text in the same order either way. A block is sent once. If a reload finds the same memories, nothing is sent, so the context does not grow by a copy of your\\nmemories every turn. /memory view shows both halves together, so what you read there is what the model gets.","breadcrumbs":"Architecture overview » Memory internals » What gets injected","id":"1204","title":"What gets injected"},"1205":{"body":"The agent can read memory files directly using memory:// URLs with the read tool: URL Content memory://root Compact summary injected at startup memory://root/MEMORY.md Full long-term memory document memory://root/skills//SKILL.md A generated skill playbook","breadcrumbs":"Architecture overview » Memory internals » Reading memory artifacts","id":"1205","title":"Reading memory artifacts"},"1206":{"body":"Subcommand Effect view Show the current backend injection payload stats Show backend-specific memory statistics, when supported diagnose Show backend-specific diagnostics, when supported clear / reset Delete active backend memory data/artifacts enqueue / rebuild Force consolidation/retention work for the active backend mm list List mental models on the active bank mm show Show one mental model mm refresh [id] Refresh auto-refresh models bank-wide, or one model by id mm history Diff the change history of a mental model mm seed Create any built-in mental models that are missing mm delete Delete a mental model from the bank mm reload Re-pull the cached block","breadcrumbs":"Architecture overview » Memory internals » /memory slash command","id":"1206","title":"/memory slash command"},"1207":{"body":"Local summary memories are built by a background pipeline that runs at startup; /memory enqueue marks consolidation work that the next startup picks up. The pipeline is skipped for subagents and for sessions that are not persisted to a session file. Phase 1, per-session extraction: For each past session that has changed since it was last processed, a model reads the session history and extracts durable signal: technical decisions, constraints, resolved failures, recurring workflows. Sessions that are too recent, too old, currently active, or beyond the configured scan/age limits are skipped. Each extraction produces a raw memory block and a short synopsis for that session. Phase 2, consolidation: After extraction, a second model pass reads all per-session extractions and produces three outputs written to disk: MEMORY.md: a curated long-term memory document memory_summary.md: the compact text injected at session start skills/: reusable procedural playbooks, each in its own subdirectory Phase 2 uses a lease and heartbeat to prevent double-running when multiple processes start simultaneously. Stale skill directories from prior runs are pruned automatically. Consolidated output is redacted for common secret/token patterns before MEMORY.md, memory_summary.md, or generated skills are written to disk.","breadcrumbs":"Architecture overview » Memory internals » How it works","id":"1207","title":"How it works"},"1208":{"body":"Memory extraction and consolidation behavior is driven by static prompt files in packages/coding-agent/src/prompts/memories/. File Purpose Variables stage_one_system.md System prompt for per-session extraction n/a stage_one_input.md User-turn template wrapping session content {{thread_id}}, {{response_items_json}} consolidation_system.md System prompt for cross-session consolidation n/a consolidation.md User-turn prompt for cross-session consolidation {{raw_memories}}, {{rollout_summaries}} read-path.md Memory guidance injected into live sessions {{memory_summary}}, {{learned}}","breadcrumbs":"Architecture overview » Memory internals » Extraction behavior","id":"1208","title":"Extraction behavior"},"1209":{"body":"Memory piggybacks on the model role system. Phase Role Purpose Phase 1 (extraction) default Per-session knowledge extraction Phase 2 (consolidation) smol (falls back to default, then current/first registry model) Cross-session synthesis If the requested memory role is not configured, memory model resolution falls back to the default role, then the active session model, then the first model in the registry.","breadcrumbs":"Architecture overview » Memory internals » Model selection","id":"1209","title":"Model selection"},"121":{"body":"The provider defines the wire protocol, auth scheme, model list, rate limits, and the tokens it returns.\\nVeyyon adapts to that surface through the provider’s api kind: Chat-Completions-style endpoints ( api: openai-completions) talk /chat/completions. Responses-style and native provider endpoints use their own request shape. Model ids come from the provider’s discovery endpoint when discovery runs. There is no hardcoded\\nallowlist for BYOK providers, and discovery returns an error; it does not invent an empty catalog on failure. Everything beyond the built-in catalog is data in models.yml, see Providers and docs/handbook/src/reference/providers.md.","breadcrumbs":"Core concepts » Model contract » What the provider owns","id":"121","title":"What the provider owns"},"1210":{"body":"Setting Default Description memory.backend off Select local for this pipeline; legacy memories.enabled: true is migrated to memory.backend: local when no explicit backend is set memories.maxRolloutAgeDays 30 Sessions older than this are not processed memories.minRolloutIdleHours 12 Sessions active more recently than this are skipped memories.maxRolloutsPerStartup 64 Cap on sessions processed in a single startup memories.summaryInjectionTokenLimit 5000 Max tokens of the summary injected into the system prompt Additional tuning knobs (concurrency, lease durations, token budgets) are available in config for advanced use.","breadcrumbs":"Architecture overview » Memory internals » Configuration","id":"1210","title":"Configuration"},"1211":{"body":"packages/coding-agent/src/memories/index.ts: pipeline orchestration, injection, clear/enqueue entry points (the /memory command routes here via packages/coding-agent/src/memory-backend/local-backend.ts) packages/coding-agent/src/memories/storage.ts: SQLite-backed job queue and thread registry packages/coding-agent/src/prompts/memories/: memory prompt templates packages/coding-agent/src/internal-urls/memory-protocol.ts: memory:// URL handler","breadcrumbs":"Architecture overview » Memory internals » Key files","id":"1211","title":"Key files"},"1212":{"body":"Compaction and branch summaries are the two mechanisms that keep long sessions usable without losing prior work context. Compaction rewrites old history into a summary on the current branch. Branch summary captures abandoned branch context during /tree navigation. Both are persisted as session entries and converted into agent-attributed developer context when rebuilding LLM input.","breadcrumbs":"Architecture overview » Compaction internals » Compaction and Branch Summaries","id":"1212","title":"Compaction and Branch Summaries"},"1213":{"body":"packages/agent/src/compaction/compaction.ts (context-full summarization and handoff generation) packages/agent/src/compaction/legacy-snapcompact-archive.ts (reads archives left by the removed image-archive engine so old sessions keep loading) packages/agent/src/compaction/branch-summarization.ts packages/agent/src/compaction/pruning.ts packages/agent/src/compaction/utils.ts packages/coding-agent/src/session/session-manager.ts packages/coding-agent/src/session/agent-session.ts packages/coding-agent/src/session/messages.ts packages/coding-agent/src/extensibility/hooks/types.ts packages/coding-agent/src/config/settings-schema.ts","breadcrumbs":"Architecture overview » Compaction internals » Key implementation files","id":"1213","title":"Key implementation files"},"1214":{"body":"Compaction and branch summaries are first-class session entries, not plain assistant/user messages. CompactionEntry type: \\"compaction\\" summary, optional shortSummary (display only, and no longer produced by compact(): see\\n“Short summary” below) firstKeptEntryId (compaction boundary) tokensBefore optional details, preserveData, fromExtension BranchSummaryEntry type: \\"branch_summary\\" fromId, summary optional details, fromExtension When context is rebuilt ( buildSessionContext): Latest compaction on the active path is converted to one compactionSummary message. Kept entries from firstKeptEntryId to the compaction point are re-included. Later entries on the path are appended. branch_summary entries are converted to branchSummary messages. custom_message entries are converted to custom messages. convertToLlm() transforms these custom roles into LLM-facing messages, through these static\\ntemplates: packages/agent/src/prompts/compaction/compaction-summary-context.md packages/agent/src/prompts/compaction/branch-summary-context.md branchSummary becomes an agent-attributed developer message. compactionSummary becomes an agent-attributed user message. The role is the trust boundary: a\\ncompaction summary is model-generated history, so putting it in the user channel means it cannot\\noutrank a live developer message that contradicts it. Any image attachments follow the summary text\\nin the same message, which is also why the user slot is the safe one: every provider accepts images\\nthere. The compaction template wraps the summary in its own delimiters, so the untrusted region\\nhas an explicit start and end. Exactly one wrapper is ever emitted: a legacy or model-authored wrapper persisted inside the summary text is stripped first\\n( withoutSummaryPresentationTags), and embedded or sibling elements that are not one\\nenclosing wrapper are left alone as content. The branch template uses no delimiters. Other custom messages pass through as developer messages with their raw content and no template.","breadcrumbs":"Architecture overview » Compaction internals » Session entry model","id":"1214","title":"Session entry model"},"1215":{"body":"","breadcrumbs":"Architecture overview » Compaction internals » Compaction pipeline","id":"1215","title":"Compaction pipeline"},"1216":{"body":"Compaction/context maintenance can run in six ways: Manual context compaction: /compact [summary] [focus] calls AgentSession.compact(...). Automatic overflow recovery: after a same-model assistant error that matches context overflow. Automatic incomplete-output recovery: after a same-model assistant message ends with stopReason === \\"length\\" (OpenAI/Codex response.incomplete). Automatic threshold maintenance: after a successful turn when context exceeds the resolved threshold. Mid-turn threshold maintenance: before the next provider request when a tool-loop turn crosses the threshold and compaction.midTurnEnabled !== false. Idle maintenance: runIdleCompaction() can invoke the same auto-maintenance path with reason \\"idle\\".","breadcrumbs":"Architecture overview » Compaction internals » Triggers","id":"1216","title":"Triggers"},"1217":{"body":"Before compaction: entry: 0 1 2 3 4 5 6 7 8 9 ┌─────┬─────┬─────┬──────┬─────┬─────┬──────┬──────┬─────┬──────┐ │ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool │ └─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴──────┘ └────────┬───────┘ └──────────────┬──────────────┘ messagesToSummarize kept messages ↑ firstKeptEntryId (entry 4) After compaction (new entry appended): entry: 0 1 2 3 4 5 6 7 8 9 10 ┌─────┬─────┬─────┬──────┬─────┬─────┬──────┬──────┬─────┬──────┬─────┐ │ hdr │ usr │ ass │ tool │ usr │ ass │ tool │ tool │ ass │ tool │ cmp │ └─────┴─────┴─────┴──────┴─────┴─────┴──────┴──────┴─────┴──────┴─────┘ └──────────┬──────┘ └──────────────────────┬───────────────────┘ not sent to LLM sent to LLM ↑ starts from firstKeptEntryId What the LLM sees: ┌────────┬─────────┬─────┬─────┬──────┬──────┬─────┬──────┐ │ system │ summary │ usr │ ass │ tool │ tool │ ass │ tool │ └────────┴─────────┴─────┴─────┴──────┴──────┴─────┴──────┘ ↑ ↑ └─────────────────┬────────────────┘ prompt from cmp messages from firstKeptEntryId","breadcrumbs":"Architecture overview » Compaction internals » Compaction shape (visual)","id":"1217","title":"Compaction shape (visual)"},"1218":{"body":"The automatic paths are intentionally different: Overflow recovery Trigger: current-model assistant error is detected as context overflow and the error is not older than the latest compaction. The failing assistant error message is removed from active agent state before retry. Context promotion is tried first; if a configured larger model is available, the agent switches model and retries without compacting. If promotion is unavailable and compaction is enabled, in-place compaction runs with reason: \\"overflow\\" and willRetry: true. On success, agent.continue() is scheduled to retry the turn. Incomplete-output recovery Trigger: same-model assistant message ends with stopReason === \\"length\\" and the message is not older than the latest compaction. The incomplete assistant message is removed from active agent state before recovery. Context promotion is tried first. If promotion is unavailable and compaction is enabled, auto maintenance runs with reason: \\"incomplete\\" and willRetry: true. On context-full success, agent.continue() is scheduled to retry the turn. Threshold maintenance Trigger: successful, non-error assistant message whose adjusted context tokens exceed resolveThresholdTokens(...). Mid-turn maintenance also checks safe tool-loop boundaries before the next provider request when compaction.midTurnEnabled !== false. Tool-output pruning can reduce the measured token count before threshold comparison. Context promotion is tried before post-turn compaction. If promotion is unavailable, auto maintenance runs with reason: \\"threshold\\" and willRetry: false. On success, if compaction.autoContinue !== false, post-turn maintenance schedules an agent-authored developer auto-continue prompt from prompts/turn-control/auto-continue.md; mid-turn maintenance never schedules a separate continuation because the core loop already defines the next provider request. Idle maintenance Trigger: runIdleCompaction() when not streaming or already compacting. Uses reason: \\"idle\\" and does not auto-continue afterward.","breadcrumbs":"Architecture overview » Compaction internals » Overflow/incomplete recovery vs threshold/idle maintenance","id":"1218","title":"Overflow/incomplete recovery vs threshold/idle maintenance"},"1219":{"body":"summary is the sole compaction strategy, and it continues the SAME session. The generated summary\\nis prefixed onto a retained raw tail in one message array: buildSessionContext pushes the summary,\\nthen re-emits every entry from firstKeptEntryId onward. findCutPoint walks backwards\\naccumulating until keepRecentTokens (default 10000), and because it can only cut at a turn\\nboundary, prepareCompaction then hard-bounds the tail: a kept turn whose bulk exceeds the budget\\nhas its heavy non-error tool results replaced with an elision marker (largest first, originals\\noffloaded to a recovery artifact:// blob), so the tail stays within budget even when one turn\\nalone is bigger. User messages, assistant text, tool calls, and error results are never elided. Note that the summary prompt does not state any of that. Its opening line requests “a structured\\nhandoff summary for another LLM to resume the task”, which describes a cold restart that compaction\\ndoes not perform. This is inherited from upstream, whose engine keeps the same recent tail, so the\\nmismatch is upstream’s rather than a fork difference. It is recorded here because a summarizer told\\nit is writing for a fresh reader will restate turns that are still in context. Changing the prompt\\nis an operator decision, not a fix to apply locally. /handoff is a separate, explicit operation that starts a new session. Nothing carries over except\\nits generated transfer document. Automatic compaction never selects or schedules a handoff.","breadcrumbs":"Architecture overview » Compaction internals » Compaction and manual handoff","id":"1219","title":"Compaction and manual handoff"},"122":{"body":"Each turn the harness builds a request that includes: Base instructions for the active model or backend (execution order, stop-when-green, format-neutral\\ntool guidance). See Execution-order prompts. User and project instructions from global, active-profile, and project AGENTS.md layers, sticky rules, and session steers. A caller may replace the base for one invocation with --system-prompt. Tool schemas the model is allowed to call on this turn (bash, edit/write, web search, MCP tools,\\nskills, and so on), filtered by feature flags, harness-profile allowlists, and plan-mode narrowing.\\nA per-tool deny policy does not filter this list; it rejects the call at dispatch. Conversation context for the active thread, possibly compacted. The model is expected to call tools using the schemas it was given. When arguments are almost right but\\nmalformed, hashline returns recovery hints so the model can retry inside the same turn budget.","breadcrumbs":"Core concepts » Model contract » System prompts and tool schemas","id":"122","title":"System prompts and tool schemas"},"1220":{"body":"Earlier versions offered snap, handoff, and other strategy values. They now\\nmigrate to summary. Legacy off also sets compaction.enabled: false. Sessions compacted by the old engine still open without loss. The removed engine always stored the full plaintext source alongside its image frames, so a legacy archive degrades gracefully: On each context rebuild, legacyArchiveSourceText (in packages/agent/src/compaction/legacy-snapcompact-archive.ts) reads the archived source from CompactionEntry.preserveData.snapcompact and re-attaches it as a single recovered text block on the compaction summary. The old image frames are never rehydrated, which also removes the oversized-payload hazard they carried. The next compaction over such a session drains that recovered source into the fresh LLM summary and drops the legacy archive from preserveData, so the session converges to a plain summarized history.","breadcrumbs":"Architecture overview » Compaction internals » Legacy compaction strategies","id":"1220","title":"Legacy compaction strategies"},"1221":{"body":"By default the live TUI collapses pre-compaction history: display.collapseCompacted defaults to true, so only the latest compacted tail renders live above the summary divider and the scrollback is cleared at the compaction point. Set display.collapseCompacted to false to keep the full display transcript inline instead ( buildSessionContext({ transcript: true }) / AgentSession.buildTranscriptSessionContext()): every path entry in chronological order, with each compaction shown as a slim divider, ── 📷 compacted · ctrl+o ──, at the point it fired. Expanding (ctrl+o) reveals the summary. In the collapsed default the LLM context and the visible transcript reset together; in the inline mode only the LLM context resets, and the scrollback above the divider stays intact, including across session resume.","breadcrumbs":"Architecture overview » Compaction internals » Display transcript","id":"1221","title":"Display transcript"},"1222":{"body":"Two passes run from AgentSession.#checkCompaction(), after every completed turn, and both persist\\nthrough rewriteEntries() so the session file matches the live context ( /fork, /tan and resume\\nread the file, and a divergent prefix cold-misses the provider prompt cache): Stale-result pass ( #pruneStaleToolResults → pruneSupersededToolResults) runs first, before\\nany threshold gating, so it fires even with compaction.enabled off. It is skipped entirely when\\nboth compaction.supersedeReads and compaction.dropUseless are false. Threshold prune ( #pruneToolOutputs → pruneToolOutputs) runs only on the threshold path,\\nafter the compaction.enabled / strategy check and after error turns are skipped, and only once\\nthe turn has usable usage data. Its savings feed postMaintenanceContextTokens, which is the\\ntrigger figure reported to the compaction it may schedule. Default prune policy: Protect newest 40_000 tool-output tokens. Require at least 20_000 total estimated savings. Never blank a result below 50 tokens ( MIN_PRUNE_TOKENS): the [Output truncated - N tokens] placeholder costs ~8 tokens, so pruning a sub-floor result would grow the context and churn the prompt cache for nothing. (Superseded and useless results keep their own rules: the useless collector already drops no-savings candidates; superseded reads prune for correctness regardless of size.) Never prune skill tool results, read results of skill:// paths, or reads of the active plan reference file (added via AgentSession’s plan protection). Pruned tool results are replaced with: [Output truncated - N tokens]","breadcrumbs":"Architecture overview » Compaction internals » Per-turn and pre-compaction pruning","id":"1222","title":"Per-turn and pre-compaction pruning"},"1223":{"body":"Gated by compaction.supersedeReads (default on). When it is on, the stale-result pass keys every read result by readToolSupersedeKey (path plus selector grammar; a selector-free read supersedes\\nrange reads of the same base path, URL-scheme paths are exempt), and every result but the newest in\\na key group is blanked to the exact placeholder [Superseded by a newer read of this file]\\n( SUPERSEDED_NOTICE). Turning the setting off passes no key function, so no read is ever grouped and\\nevery read result survives at full length. Blanking happens only where it is cheap: when the messages after the candidate total at most ~8k\\nestimated tokens ( PRUNE_CACHE_WARM_SUFFIX_TOKENS, the read→edit→read tail), or when the last\\nmessage is at least 90 minutes old ( PRUNE_IDLE_FLUSH_MS, past the 1h Anthropic “long” prompt-cache\\nretention), in which case every still-sent candidate flushes at once. Entries before the latest\\ncompaction’s firstKeptEntryId are summarized away and are never rewritten.","breadcrumbs":"Architecture overview » Compaction internals » Superseded-read elision","id":"1223","title":"Superseded-read elision"},"1224":{"body":"Tools can flag a finished result as contextually useless, a search with zero matches, a job poll that timed out with everything still running, an empty irc inbox drain. The flag originates on the tool result ( AgentToolResult.useless, set via ToolResultBuilder.useless() or directly on the returned object), is copied by the agent loop onto the persisted ToolResultMessage (never together with isError, errors always win), and is consumed in three places: Per-turn stale-result pass ( pruneSupersededToolResults, gated by compaction.dropUseless, default on): flagged results are blanked to the exact placeholder [Uneventful result elided] ( USELESS_NOTICE) with the same cache-aware timing as superseded reads: only when the suffix after the candidate is small (≤ ~8k tokens) or the session has idled past the provider prompt-cache lifetime. Results smaller than the notice itself are never blanked (no savings), and protected tools are exempt. Threshold prune ( pruneToolOutputs): flagged results bypass the protect-recent window, same as superseded reads, and receive USELESS_NOTICE instead of the token-count placeholder. Summary serialization: serializeConversation drops the whole tool call/result pair from summarizer input: the source region is discarded after summarization anyway, so the exclusion costs no cache. The flag never reaches provider wire formats, and flagged pairs are never removed from history (only blanked in place), so tool-call/result pairing stays intact.","breadcrumbs":"Architecture overview » Compaction internals » Useless-result elision","id":"1224","title":"Useless-result elision"},"1225":{"body":"compaction-summary.md, compaction-update-summary.md, and compaction-summary-context.md are\\noh-my-pi’s text verbatim, by operator order, on the measurement that upstream scores higher on\\nlong-run evals. packages/agent/test/compaction-strategy-contracts.test.ts pins each one by\\nSHA-256 and preflight runs it, so an unapproved edit fails the build instead of quietly changing\\nsummary quality. Approving a change means updating the digest in the same commit. Both prompts request the same ten sections, in the same order: ## Goal, ## Constraints & Preferences, ## Progress ( ### Done, ### In Progress, ### Blocked), ## Key Decisions, ## Next Steps, ## Critical Context, ## Additional Notes. Sections may be\\nomitted when they do not apply. The lists have to match, because iterative compaction feeds its own\\noutput back in: a section the update prompt failed to name would be dropped on every cycle. Both require exact file paths, function names, and error messages preserved rather than paraphrased,\\nrequire repository state changes (branch, uncommitted changes) when mentioned, forbid any text\\noutside the structured summary, and require an unanswered question to the user to survive. The\\ninitial prompt preserves that question verbatim; the update prompt files it into ## Critical Context, replacing a previous pending question once it has been answered. ## Goal is a single undifferentiated field: the prompts do not separate a durable overarching goal\\nfrom the current task, and only handoff-document.md still draws that line. The update prompt also\\ninstructs the model to preserve all information from the previous summary and permits removing only\\nwhat is no longer relevant, so iterative compaction accumulates rather than replacing drift.","breadcrumbs":"Architecture overview » Compaction internals » What the summary prompts request","id":"1225","title":"What the summary prompts request"},"1226":{"body":"Neither a compaction summary nor an explicit handoff document may be empty. A provider can finish with stopReason: \\"stop\\" after spending its output budget on reasoning and emit no text. Both call sites raise instead of persisting an empty artifact. Lower the compaction thinking level if this repeats so the model spends its budget on the document.","breadcrumbs":"Architecture overview » Compaction internals » Empty responses","id":"1226","title":"Empty responses"},"1227":{"body":"prepareCompaction() only considers entries since the last compaction entry (if any). Find previous compaction index. Compute boundaryStart = prevCompactionIndex + 1. Adapt keepRecentTokens using measured usage ratio when available. Run findCutPoint() over the boundary window. Valid cut points include: message entries with roles: user, assistant, bashExecution, hookMessage, branchSummary, compactionSummary custom_message entries branch_summary entries Hard rule: never cut at toolResult. If there are non-message metadata entries immediately before the cut point ( model_change, thinking_level_change, labels, etc.), they are pulled into the kept region by moving cut index backward until a message or compaction boundary is hit.","breadcrumbs":"Architecture overview » Compaction internals » Boundary and cut-point logic","id":"1227","title":"Boundary and cut-point logic"},"1228":{"body":"If cut point is not at a user-turn start, compaction treats it as a split turn. Turn start detection treats these as user-turn boundaries: message.role === \\"user\\" message.role === \\"bashExecution\\" custom_message entry branch_summary entry Split-turn compaction generates two summaries: History summary ( messagesToSummarize) Turn-prefix summary ( turnPrefixMessages) Final stored summary is merged as: --- **Turn Context (split turn):** ","breadcrumbs":"Architecture overview » Compaction internals » Split-turn handling","id":"1228","title":"Split-turn handling"},"1229":{"body":"compact(...) builds summaries from serialized conversation text: Convert messages via convertToLlm(). Serialize with serializeConversation(). Wrap in ... . Optionally include ... . Optionally inject extension hook context and active memory-backend compaction context as entries. Execute summarization prompt with SUMMARIZATION_SYSTEM_PROMPT. Prompt selection: first compaction: compaction-summary.md iterative compaction with prior summary: compaction-update-summary.md split-turn second pass: compaction-turn-prefix.md handoff document: handoff-document.md (used only by explicit generateHandoff(...), not serialized compaction)","breadcrumbs":"Architecture overview » Compaction internals » Summary generation","id":"1229","title":"Summary generation"},"123":{"body":"Veyyon advertises the structured edit tool in one of two shapes. The payload (the patch or edit body) is\\nthe same; only the transport differs. Form How the model calls it Typical API kind Freeform A custom / grammar tool. The raw body is the tool payload (for example a full *** Begin Patch envelope). Responses-style Function A JSON-schema function tool. Arguments are a JSON object (for example {\\"input\\": \\"\\"}). Chat Completions Default edit mode is hashline ( edit.mode: hashline). When edit.mode is apply_patch, the\\nprovider wire form is derived from the API kind by default; an optional catalog override can pin the\\ntool shape to function or freeform. The Function form keeps structured edits available\\non chat-wire endpoints (Ollama, LM Studio, DeepSeek, and similar). See The hashline edit engine\\nfor the default edit wire format.","breadcrumbs":"Core concepts » Model contract » Freeform vs Function tools","id":"123","title":"Freeform vs Function tools"},"1230":{"body":"CompactionEntry.shortSummary is a display-only, pull-request-style line. compact() no longer\\ngenerates one: a second model request per compaction, spent on text the model never reads, is not\\nworth the input cost. Every reader stays, because compaction hooks still set the field and sessions\\nwritten before the change still carry it. Its one display consumer is the session-listing title fallback ( title: header.title ?? shortSummary\\nin packages/coding-agent/src/session/session-listing.ts), which veyyon reaches only when its own\\ntiny-model titler declined: VEYYON_NO_TITLE set, or a first message too low-signal to title from.\\nIn that case the session picker falls back again to the first user message, so nothing renders blank. Remote summarizer endpoint: When compaction.remoteEndpoint is set, summary generation POSTs one of two wire formats: custom veyyon summarizer endpoints receive { systemPrompt, prompt } and must return JSON containing at least { summary }. OpenAI-compatible endpoints whose path ends in /chat/completions receive { model, messages, stream: false }, where messages contains one system prompt and one user prompt. The summary is read from choices[0].message.content, which lets self-hosted servers such as llama.cpp and vLLM act as summarizers without a separate shim. When it is unset, the active model generates the summary locally. That is the default for every provider. Server-side compaction ( compaction.remote, on by default): OpenAI and Azure OpenAI serve POST /responses/compact, which compacts a session’s\\ncontext inside the provider and returns the compacted window. Veyyon uses it when the\\nmodel’s compat.supportsServerCompaction flag is set, which is resolved per host at\\nmodel build time: the official OpenAI API and Azure’s v1 API today, and any gateway\\nthat opts in with an override. The Codex provider stays out, because its transport\\nowns history state server-side and a client-minted window has no replay contract there.\\nA re-pointed openai model also stays out, since another vendor’s host does not serve\\nthat path. Turning compaction.remote off is the only thing that disables it; leaving\\nit unset leaves it on. A server-side compaction stores no summary text, and that is deliberate. The window\\nit returns is an encrypted_content blob minted under the provider’s key. There is\\nnothing in it to read, and nothing to decrypt: it is the compacted context itself, meant\\nto be handed straight back to the same provider. The path used to run a full local\\nsummarization of the same span alongside the remote call and store both, which cost the\\nremote call plus the exact summary the remote call was supposed to replace, and only one\\nof the two was ever read. Writing readable text here is not a missing feature that could\\nbe added later. The only way to produce it is to pay a second model to describe a span,\\nwhich is the local strategy with an extra network round trip in front of it, and any text\\nderived from the blob rather than the span would be invented. An empty summary is the\\nhonest record of what happened. Because the entry cannot explain itself, the rebuild will not trust it outside the\\nprovider that minted it. buildSessionContext treats a compaction as usable only when the\\nstored window replays on the active provider, or when there is real summary text. When\\nneither holds, which is a fork or resume onto a different provider, it re-expands every\\nmessage the compaction hid. Nothing was lost to recover: compaction only advances firstKeptEntryId, so the discarded span is still in the session file. Sessions compacted by the earlier, removed path ( preserveData.openaiRemoteCompaction,\\nwhose summary field held a fixed placeholder) load through the same rule and re-expand.","breadcrumbs":"Architecture overview » Compaction internals » Short summary","id":"1230","title":"Short summary"},"1231":{"body":"packages/agent/src/compaction/compaction.ts also exports generateHandoff(...). Handoff generation uses the same completeSimple(...) oneshot style as summarization, but it preserves the live agent cache prefix by sending the active system prompt, tool array, and real LLM message history, then appending one agent-attributed user message containing the handoff prompt. It forces toolChoice: \\"none\\" and returns joined text blocks directly. Handoff does not write a CompactionEntry. AgentSession.handoff() performs the session transition: it starts a new session, injects the generated document as a visible custom_message with customType: \\"handoff\\", and rebuilds agent messages from that new session.","breadcrumbs":"Architecture overview » Compaction internals » Handoff generation","id":"1231","title":"Handoff generation"},"1232":{"body":"Compaction tracks cumulative file activity using assistant tool calls: read(path) → read set write(path) → modified set edit(path) → modified set Cumulative behavior: Includes prior compaction details only when prior entry is pi-generated ( fromExtension !== true). In split turns, includes turn-prefix file ops too. details.readFiles excludes files also modified; details.modifiedFiles contains the rest (persisted shape is unchanged). The file list is a grouped, prefix-folded directory tree (find-tool shape) with a per-file access marker, (Read) for read-only files, (Write) for modified files never read, (RW) for modified files also present in the cumulative read set. Capped at 20 files with an […N files elided…] line. Compaction and explicit handoff append it as a tag (via upsertFileOperations). \\n# packages/agent/src/compaction/\\ncompaction.ts (Read)\\nutils.ts (RW)\\n## prompts/\\nfile-operations.md (Write)\\n Legacy / tags from summaries written by earlier versions are stripped (alongside ) before re-appending, so old summaries self-heal on the next compaction.","breadcrumbs":"Architecture overview » Compaction internals » File-operation context in summaries","id":"1232","title":"File-operation context in summaries"},"1233":{"body":"After summary generation (or a hook-provided summary), agent session: Appends a CompactionEntry with appendCompaction(...). Rebuilds display context from the active leaf via buildDisplaySessionContext(). Replaces live agent messages with rebuilt context. Synchronizes active todo phases from the rebuilt branch and closes provider sessions whose history was rewritten. Emits session_compact hook event.","breadcrumbs":"Architecture overview » Compaction internals » Persist and reload","id":"1233","title":"Persist and reload"},"1234":{"body":"Branch summarization is tied to tree navigation, not token overflow.","breadcrumbs":"Architecture overview » Compaction internals » Branch summarization pipeline","id":"1234","title":"Branch summarization pipeline"},"1235":{"body":"During navigateTree(...): Compute abandoned entries from old leaf to common ancestor using collectEntriesForBranchSummary(...). If caller requested summary ( options.summarize), generate summary before switching leaf. If summary exists, attach it at the navigation target using branchWithSummary(...). Operationally this is commonly driven by /tree flow when branchSummary.enabled is enabled.","breadcrumbs":"Architecture overview » Compaction internals » Trigger","id":"1235","title":"Trigger"},"1236":{"body":"Tree before navigation: ┌─ B ─ C ─ D (old leaf, being abandoned) A ───┤ └─ E ─ F (target) Common ancestor: A\\nEntries to summarize: B, C, D After navigation with summary: ┌─ B ─ C ─ D ─ [summary of B,C,D] A ───┤ └─ E ─ F (new leaf)","breadcrumbs":"Architecture overview » Compaction internals » Branch switch shape (visual)","id":"1236","title":"Branch switch shape (visual)"},"1237":{"body":"generateBranchSummary(...) computes budget as: tokenBudget = model.contextWindow - branchSummary.reserveTokens prepareBranchEntries(...) then: First pass: collect cumulative file ops from all summarized entries, including prior pi-generated branch_summary details. Second pass: walk newest → oldest, adding messages until token budget is reached. Prefer preserving recent context. May still include large summary entries near budget edge for continuity. Compaction entries are included as messages ( compactionSummary) during branch summarization input.","breadcrumbs":"Architecture overview » Compaction internals » Preparation and token budget","id":"1237","title":"Preparation and token budget"},"1238":{"body":"Branch summarization: Converts and serializes selected messages. Wraps in . Uses custom instructions if supplied, otherwise branch-summary.md. Calls summarization model with SUMMARIZATION_SYSTEM_PROMPT. Prepends branch-summary-preamble.md. Appends file-operation tags. Result is stored as BranchSummaryEntry with optional details ( readFiles, modifiedFiles).","breadcrumbs":"Architecture overview » Compaction internals » Summary generation and persistence","id":"1238","title":"Summary generation and persistence"},"1239":{"body":"","breadcrumbs":"Architecture overview » Compaction internals » Extension and hook touchpoints","id":"1239","title":"Extension and hook touchpoints"},"124":{"body":"┌──────────────────────── harness (veyyon) ─────────────────────┐\\n│ session / turn loop │\\n│ prompts, tool schemas, edit, approvals, compaction │\\n└────────────────────────────┬──────────────────────────────────┘ │ HTTPS / local HTTP ▼\\n┌──────────────────────── provider ─────────────────────────────┐\\n│ endpoint auth + model discovery + completions/responses │\\n│ model weights, rate limits, provider-side refusals │\\n└───────────────────────────────────────────────────────────────┘ If something fails, ask which side is responsible: Config rejected at load, malformed models.yml, missing key → harness / your config. HTTP 401 / 429 / empty model list → provider or key. Patch applied but tests red → harness did its job; the change still needs work. Approval or critical-pattern denial → permission model, not the model provider.","breadcrumbs":"Core concepts » Model contract » Harness vs provider: a clear split","id":"124","title":"Harness vs provider: a clear split"},"1240":{"body":"Pre-compaction hook. Can: cancel compaction ( { cancel: true }) provide full custom compaction payload ( { compaction: CompactionResult })","breadcrumbs":"Architecture overview » Compaction internals » session_before_compact","id":"1240","title":"session_before_compact"},"1241":{"body":"Prompt/context customization hook for default compaction. Can return: prompt (override base summary prompt) context (extra context lines injected into ) preserveData (stored on compaction entry)","breadcrumbs":"Architecture overview » Compaction internals » session_compacting","id":"1241","title":"session_compacting"},"1242":{"body":"Post-compaction notification with saved compactionEntry and fromExtension flag.","breadcrumbs":"Architecture overview » Compaction internals » session_compact","id":"1242","title":"session_compact"},"1243":{"body":"Runs on tree navigation before default branch summary generation. Can: cancel navigation provide custom { summary: { summary, details } } used when user requested summarization","breadcrumbs":"Architecture overview » Compaction internals » session_before_tree","id":"1243","title":"session_before_tree"},"1244":{"body":"Post-navigation event exposing new/old leaf and optional summary entry.","breadcrumbs":"Architecture overview » Compaction internals » session_tree","id":"1244","title":"session_tree"},"1245":{"body":"compaction.model (settings/ config.yml, --compaction-model CLI, or the Compaction Model picker in /settings) selects the model used for LLM compaction and handoff generation. Default: unset, compaction inherits the main session model live, so switching the session model also switches the compactor. When set, resolveCompactionModelPatterns expands the value through the normal pattern/role resolution (role aliases like \\"@smol\\" and :thinking suffixes work), and auto compaction tries the resulting candidates in order. The value is a chain and can be written either way, as a comma-separated string ( opus,sonnet) or as a YAML list; both normalize to the same ordered candidates. Legacy config keys compaction.compactionModel / top-level compactionModel are migrated to compaction.model on load.","breadcrumbs":"Architecture overview » Compaction internals » Which model compacts","id":"1245","title":"Which model compacts"},"1246":{"body":"Manual compaction aborts current agent operation first. abortCompaction() cancels manual compaction, auto-compaction, and handoff generation controllers. Auto compaction emits start/end session events for UI/state updates. Auto compaction can try multiple model candidates and retry transient failures; long retry delays prefer the next candidate when one is available. Overflow errors are excluded from generic retry path because they are handled by context promotion/compaction. If auto-compaction fails: overflow path emits Context overflow recovery failed: ... incomplete-output path emits Incomplete response recovery failed: ... threshold/idle paths emit Auto-compaction failed: ... Branch summarization can be cancelled via abort signal (e.g., Escape), returning canceled/aborted navigation result.","breadcrumbs":"Architecture overview » Compaction internals » Runtime behavior and failure semantics","id":"1246","title":"Runtime behavior and failure semantics"},"1247":{"body":"From settings-schema.ts: compaction.enabled = true compaction.strategy = \\"summary\\", the sole strategy. Every stored legacy strategy token migrates to summary; legacy off also sets compaction.enabled: false. Use /handoff for an explicit transfer to a new session. compaction.reserveTokens = unset (absent key). When unset the compaction layer falls back to DEFAULT_RESERVE_TOKENS = 16384, and small-window recovery may substitute a proportional 15%-of-window reserve when the default does not fit the window ( resolveBudgetReserveTokens). compaction.keepRecentTokens = 10000 compaction.supersedeReads = true (drop earlier file reads that a later read of the same file makes redundant) compaction.dropUseless = true compaction.handoffSaveToDisk = false (also write the handoff packet to disk) compaction.modelContextWindow = unset (absent key); overrides the window size the compaction budget resolves against compaction.autoContinue = true compaction.midTurnEnabled = true compaction.remoteEndpoint = undefined compaction.threshold = auto; the one trigger setting, with its unit in the value. auto is contextWindow - max(15% of contextWindow, reserveTokens). 85% is a percent of the current model’s window. 170000 is an absolute token amount, model-independent: compaction runs once context exceeds that many tokens whatever the current model’s window is, and when the amount is larger than that window it is honored up to contextWindow - 1 with a one-time warning (never silently reinterpreted). Resolution and the migration off the two retired keys live in packages/agent/src/compaction/threshold.ts. compaction.thresholdTokens = -1 and compaction.thresholdPercent = -1; retired. The global config is rewritten on load ( #migrateRawSettings): a positive amount becomes threshold: , a positive percent becomes threshold: % (the amount wins when both are set), and both keys are dropped, so the ambiguity leaves the file without moving the trigger. Config sources that are never rewritten — project files, --config overlays — are folded in at read time by withLegacyCompactionThreshold with the same precedence, and the session reports which retired key supplied the value. compaction.idleEnabled = false compaction.idleThresholdTokens = 200000 compaction.idleTimeoutSeconds = 300 branchSummary.enabled = false branchSummary.reserveTokens = 16384 These values are consumed at runtime by AgentSession and compaction/branch summarization modules.","breadcrumbs":"Architecture overview » Compaction internals » Settings and defaults","id":"1247","title":"Settings and defaults"},"1248":{"body":"The current TUI contract used by packages/coding-agent and packages/tui for extension UI, custom tool UI, and custom renderers.","breadcrumbs":"Architecture overview » The terminal UI » TUI integration for extensions and custom tools","id":"1248","title":"TUI integration for extensions and custom tools"},"1249":{"body":"The runtime has two layers: Rendering engine ( packages/tui): differential terminal renderer, input dispatch, focus, overlays, cursor placement. Integration layer ( packages/coding-agent): mounts extension/custom-tool components, wires keybindings/theme, and restores editor state.","breadcrumbs":"Architecture overview » The terminal UI » What this subsystem is","id":"1249","title":"What this subsystem is"},"125":{"body":"For BYOK providers, model and provider entries are data in models.yml: A YAML or schema error makes the registry skip the custom file with an error message; it does not silently drop\\nmodels. Custom providers are merged alongside the built-in catalog. A custom entry with the same id as an\\nimplicit local engine ( ollama, lm-studio, llama.cpp) replaces that engine’s discovery. Provider availability requires the id not be in disabledProviders and the provider be keyless or\\nhave resolvable credentials. Malformed provider data fails at load. Silent fallback to a weaker provider is treated as a bug.","breadcrumbs":"Core concepts » Model contract » Provider data at load time","id":"125","title":"Provider data at load time"},"1250":{"body":"Mode ctx.ui.custom(...) availability Notes Interactive TUI Supported Component is mounted in the editor area or overlay, focused, and must call done(result) to resolve. Background/headless Not interactive UI context is no-op ( hasUI === false). RPC mode Not mounted custom() is implemented as unsupported UI and returns undefined as never; do not depend on interactive UI in RPC handlers. If your extension/tool can run in non-interactive mode, guard with ctx.hasUI / pi.hasUI.","breadcrumbs":"Architecture overview » The terminal UI » Runtime behavior by mode","id":"1250","title":"Runtime behavior by mode"},"1251":{"body":"packages/tui/src/tui.ts defines: export interface Component { render(width: number): readonly string[]; handleInput?(data: string): void; wantsKeyRelease?: boolean; invalidate?(): void; dispose?(): void;\\n} Render results are component-owned and immutable to callers; a component that did not change should return the same array reference it returned last time (reference equality is what enables the renderer’s memoization and row virtualization), and must return a new array whenever its content changed. Focusable is separate: export interface Focusable { focused: boolean; setUseTerminalCursor?(useTerminalCursor: boolean): void;\\n} Cursor behavior uses CURSOR_MARKER (not getCursorPosition). Focused components emit the marker in rendered text; TUI extracts it and positions the hardware cursor.","breadcrumbs":"Architecture overview » The terminal UI » Core component contract ( @veyyon/tui)","id":"1251","title":"Core component contract ( @veyyon/tui)"},"1252":{"body":"Your render(width) output must be terminal-safe: Do not intentionally exceed width on any line. The renderer truncates overwide non-image lines as a last-resort guard, but components should still return width-safe output. Measure visual width, not string length: use visibleWidth(). Truncate/wrap ANSI-aware text with truncateToWidth() / wrapTextWithAnsi(). Sanitize tabs/content from external sources using replaceTabs() (and higher-level sanitizers in coding-agent render paths). Minimal pattern: import { replaceTabs, truncateToWidth } from \\"@veyyon/tui\\"; render(width: number): readonly string[] { return this.lines.map(line => truncateToWidth(replaceTabs(line), width));\\n}","breadcrumbs":"Architecture overview » The terminal UI » Rendering constraints (terminal safety)","id":"1252","title":"Rendering constraints (terminal safety)"},"1253":{"body":"","breadcrumbs":"Architecture overview » The terminal UI » Input handling and keybindings","id":"1253","title":"Input handling and keybindings"},"1254":{"body":"Use matchesKey(data, \\"...\\") for navigation keys and combos.","breadcrumbs":"Architecture overview » The terminal UI » Raw key matching","id":"1254","title":"Raw key matching"},"1255":{"body":"Extension UI factories receive a KeybindingsManager (interactive mode; an in-memory instance containing the default bindings, not the user’s keybindings.yml) so you can match action ids instead of hardcoding keys: if (keybindings.matches(data, \\"app.interrupt\\")) { done(undefined); return;\\n}","breadcrumbs":"Architecture overview » The terminal UI » Match app keybinding actions","id":"1255","title":"Match app keybinding actions"},"1256":{"body":"Key release events are filtered unless your component sets: wantsKeyRelease = true; Then use isKeyRelease() / isKeyRepeat() if needed.","breadcrumbs":"Architecture overview » The terminal UI » Key release/repeat events","id":"1256","title":"Key release/repeat events"},"1257":{"body":"TUI.setFocus(component) routes input to that component. Overlay APIs exist in TUI ( showOverlay, OverlayHandle). In interactive extension/custom UI, custom(..., { overlay: true }) mounts your component through TUI.showOverlay(...); without overlay, it replaces the editor component area directly. Overlay custom UI is anchored at bottom-center with full terminal width/max height and is removed through the returned overlay handle when done(...) closes the flow.","breadcrumbs":"Architecture overview » The terminal UI » Focus, overlays, and cursor","id":"1257","title":"Focus, overlays, and cursor"},"1258":{"body":"","breadcrumbs":"Architecture overview » The terminal UI » Mount points and return contracts","id":"1258","title":"Mount points and return contracts"},"1259":{"body":"Current signature ( extensibility/extensions/types.ts): custom( factory: ( tui: TUI, theme: Theme, keybindings: KeybindingsManager, done: (result: T) => void, ) => (Component & { dispose?(): void }) | Promise, options?: { overlay?: boolean },\\n): Promise Behavior in interactive mode ( extension-ui-controller.ts): Saves editor text. Without options.overlay, replaces the editor component with your component. With options.overlay, mounts your component as a bottom-centered overlay instead of replacing the editor. Focuses your component. On done(result): calls component.dispose?.(), hides the overlay if present, restores editor + text for non-overlay flows, focuses editor, resolves promise.\\nSo done(...) is mandatory for completion.","breadcrumbs":"Architecture overview » The terminal UI » 1) Extension UI ( ExtensionUIContext)","id":"1259","title":"1) Extension UI ( ExtensionUIContext)"},"126":{"body":"The conversation model ( /model or --model) is separate from background roles. Roles are configured\\nunder modelRoles: modelRoles.tiny (or smol): lightweight background work (titles, memory, auto-thinking). Subagent models are not roles. They live in the Subagents settings area, where four layers can\\nname one, highest first: that agent’s row in subagent.agents, the blanket subagent.model, the\\nagent definition’s own model:, otherwise the conversation model. There is no silent blend, and a\\nconfigured value that matches no available model rejects the spawn instead of quietly handing the\\ndecision to the next layer. /agents shows the resolved model and which of the four decided.\\nSee Settings: Subagents and Models, roles, and profiles.","breadcrumbs":"Core concepts » Model contract » Per-role models","id":"126","title":"Per-role models"},"1260":{"body":"HookUIContext.custom is typed as (tui, theme, done) in hook/custom-tool types.\\nUnderlying interactive implementation calls factories with (tui, theme, keybindings, done). JS consumers can use the extra arg; type-level compatibility still reflects the 3-arg legacy signature. Custom tools typically use the same UI entrypoint via the factory-scoped pi.ui object, then return the selected value in normal tool content: async execute(toolCallId, params, onUpdate, ctx, signal) { if (!pi.hasUI) { return { content: [{ type: \\"text\\", text: \\"UI unavailable\\" }] }; } const picked = await pi.ui.custom((tui, theme, done) => { const component = new MyPickerComponent(done, signal); return component; }); return { content: [{ type: \\"text\\", text: picked ? `Picked: ${picked}` : \\"Cancelled\\" }] };\\n}","breadcrumbs":"Architecture overview » The terminal UI » 2) Hook/custom-tool UI context (legacy typing)","id":"1260","title":"2) Hook/custom-tool UI context (legacy typing)"},"1261":{"body":"Custom tools and extension tools can return components from: renderCall(args, options, theme) renderResult(result, options, theme, args?) options currently includes: expanded: boolean isPartial: boolean spinnerFrame?: number These renderers are mounted by ToolExecutionComponent.","breadcrumbs":"Architecture overview » The terminal UI » 3) Custom tool call/result renderers","id":"1261","title":"3) Custom tool call/result renderers"},"1262":{"body":"dispose() is optional at type level but should be implemented when you own timers, subprocesses, watchers, sockets, or overlays. done(...) should be called exactly once from your component flow. For cancellable long-running UI, pair CancellableLoader with AbortSignal and call done(...) from onAbort. Example cancellation pattern: const loader = new CancellableLoader( tui, theme.fg(\\"accent\\"), theme.fg(\\"muted\\"), \\"Working...\\",\\n);\\nloader.onAbort = () => done(undefined);\\nvoid doWork(loader.signal).then((result) => done(result));\\nreturn loader;","breadcrumbs":"Architecture overview » The terminal UI » Lifecycle and cancellation","id":"1262","title":"Lifecycle and cancellation"},"1263":{"body":"import type { Component } from \\"@veyyon/tui\\";\\nimport { SelectList, matchesKey, replaceTabs, truncateToWidth,\\n} from \\"@veyyon/tui\\";\\nimport { getSelectListTheme, type ExtensionAPI,\\n} from \\"@veyyon/coding-agent\\"; class Picker implements Component { list: SelectList; keybindings: any; done: (value: string | undefined) => void; constructor( items: Array<{ value: string; label: string }>, keybindings: any, done: (value: string | undefined) => void, ) { this.list = new SelectList(items, 8, getSelectListTheme()); this.keybindings = keybindings; this.done = done; this.list.onSelect = (item) => this.done(item.value); this.list.onCancel = () => this.done(undefined); } handleInput(data: string): void { if (this.keybindings.matches(data, \\"app.interrupt\\")) { this.done(undefined); return; } this.list.handleInput(data); } render(width: number): readonly string[] { return this.list .render(width) .map((line) => truncateToWidth(replaceTabs(line), width)); } invalidate(): void { this.list.invalidate(); }\\n} export default function extension(pi: ExtensionAPI): void { pi.registerCommand(\\"pick-model\\", { description: \\"Pick a model profile\\", handler: async (_args, ctx) => { if (!ctx.hasUI) return; const selected = await ctx.ui.custom( (tui, theme, keybindings, done) => { const items = [ { value: \\"fast\\", label: theme.fg(\\"accent\\", \\"Fast\\") }, { value: \\"balanced\\", label: \\"Balanced\\" }, { value: \\"quality\\", label: \\"Quality\\" }, ]; return new Picker(items, keybindings, done); }, ); if (selected) ctx.ui.notify(`Selected profile: ${selected}`, \\"info\\"); }, });\\n}","breadcrumbs":"Architecture overview » The terminal UI » Realistic custom component example (extension command)","id":"1263","title":"Realistic custom component example (extension command)"},"1264":{"body":"packages/tui/src/tui.ts: Component, Focusable, cursor marker, focus, overlay, input dispatch. packages/tui/src/utils.ts: width/truncation/sanitization primitives. packages/tui/src/keys.ts / keybindings.ts: key parsing and configurable action mapping. packages/coding-agent/src/modes/controllers/extension-ui-controller.ts: interactive mounting/unmounting for extension/hook/custom-tool UI. packages/coding-agent/src/extensibility/extensions/types.ts: extension UI and renderer contracts. packages/coding-agent/src/extensibility/hooks/types.ts: hook UI contract (legacy custom signature). packages/coding-agent/src/extensibility/custom-tools/types.ts: custom tool execute/render contracts. packages/coding-agent/src/modes/components/tool-execution.ts: mounting renderCall/ renderResult components and partial-state options. packages/coding-agent/src/tools/context.ts: tool UI context propagation ( hasUI, ui).","breadcrumbs":"Architecture overview » The terminal UI » Key implementation files","id":"1264","title":"Key implementation files"},"1265":{"body":"Product behavior is covered by tests that assert concrete outcomes, not only non-empty results.","breadcrumbs":"Testing and verification » Testing and verification","id":"1265","title":"Testing and verification"},"1266":{"body":"Hashline edit path: round-trip: generated patches apply to the intended content; mismatches fail with the expected error surface. Tool-call repair: unit and conformance cases in packages/coding-agent/test/repair/schema-repair.test.ts (clean / repaired / unrepairable, alias ambiguity, strict additionalProperties). Tool-output bounds: truncation limits behave as configured and remain visible to the model. Architecture gates: layering, import cycles, and module-reach checks in packages/coding-agent/test/architecture/.","breadcrumbs":"Testing and verification » Examples of what tests check","id":"1266","title":"Examples of what tests check"},"1267":{"body":"The capture configuration below is the only source of visual proof.\\nRecord interactive proofs on the repository’s private display. Do not record a\\nlogged-in desktop, and do not use a terminal multiplexer capture as visual\\nevidence. There are no other capture paths and no fallbacks.","breadcrumbs":"Testing and verification » Recording terminal proofs","id":"1267","title":"Recording terminal proofs"},"1268":{"body":"The artifact class follows the change class, and a mismatch is a failed proof. static surface changed two PNG frames, before and after\\nanimation or timing changed two animated clips, before and after\\nsetting added or changed two PNG frames, off and on A still never proves an animation: a frame cannot show a cadence, a transition, or a\\nspinner. An animated clip never substitutes for a frame pair either, because a reader\\ncomparing two clips cannot hold both states side by side. The recorder publishes\\nanimation as WebP at 33 ms per frame; a GIF is the same clip in an older container and\\nproves the same thing. Both arms of a pair are the same class, produced by one driver\\nrun, and attached to the pull request body. The recorder refuses to publish a clip whose cadence is not the one it captured. Three\\ncriteria come from --expect-ms: typical frame capture interval +/-1 ms (33/34 ms at 30 fps)\\nmoving average at least 80% of the capture rate, held stills set aside\\ncadence share at least 85% of moving frames at the capture interval The typical frame catches a resample, where every frame was rewritten. The moving\\naverage catches a clip whose wall clock is mostly slower than its most common frame.\\nThe cadence share catches frequent short holds that an acceptable average can hide.\\nByte-identical frames from normal terminal input and model output are coalesced by the\\nWebP encoder, so the average allows 20% while the share limits how often that occurs.\\nA hold at or past ten intervals is a still screen, is reported, and does not count.\\nMeasure a published file with: python3 proof/webp-cadence.py assets/demo-hd.webp --expect-ms 33","breadcrumbs":"Testing and verification » Which artifact proves which change","id":"1268","title":"Which artifact proves which change"},"1269":{"body":"The HD recorder starts Xvfb and kitty inside the recorder container. It drives the shipped CLI with real keyboard and pointer events and records the private display at 30 frames per second. 30 is the rate the pipeline delivers whole. Measured at 2560x1440 with the hero’s chrome and a payload repainting every cell as fast as the terminal accepts it, a 30 fps capture returns 240 unique frames of 240 grabbed. Capturing at 60 adds no motion the session had: it doubles the encoder’s cores and the file, and writes a 60 fps header over slower content, which is how a stuttering take once read as smooth to ffprobe. A take is judged on whether the picture moved, never on the rate the container declares. proof/motion-gate.sh counts unique frames with mpdecimate and fails a take below SCENE_MOTION_FLOOR; both session scripts run it before the take is published. The landing-page terminal uses: terminal kitty\\nfont JetBrains Mono 15\\ncanvas 2560x1440 at 30 fps\\nwindow inset 128 px\\nbackground #171b22\\nforeground #d3dae6\\npublish Lanczos downsample to 1920x1080","breadcrumbs":"Testing and verification » Real interactive sessions","id":"1269","title":"Real interactive sessions"},"127":{"body":"For non-interactive runs, pass the prompt and pick an approval mode that matches your trust\\nboundary. A headless run has no terminal to answer a prompt on, so a rung that prompts turns the\\ngated tool call into an error rather than a pause: the default auto runs every tier while the\\nworking-directory, credential and critical-command guards still stop the calls they cover. $ veyyon --print \\"run the unit tests and fix failures\\" Use --yolo (auto-approve everything) only in trusted automation, ideally in an externally isolated environment (Docker, a VM, a CI jail), since Veyyon does not sandbox the commands it runs.","breadcrumbs":"Core concepts » Model contract » Automation note","id":"127","title":"Automation note"},"1270":{"body":"proof/docker/scene-config.sh is the single definition of every SCENE_* knob. The two session scripts and the two host recorders source it; none of them restates a default. Override a knob by exporting it, never by editing one of those four files, because a default written down twice is two defaults and the one a run gets depends on which file it entered through. The chrome — rounded corners, the shadow, the translucent window over the backdrop — is drawn after the take by proof/compose-chrome.sh, not by a compositor during it. The backdrop does not move, so blending it under the window every frame recomputes one static picture thousands of times, and it cost the capture: with picom’s blur on, ffmpeg could grab only 69 of 360 frames, and opacity alone still cost a third. xwallpaper puts the backdrop in the capture for free as a root pixmap; the pass replaces the square-cornered inset with the same pixels rounded, blended and shadowed. SCENE_CHROME=live runs a compositor during the capture instead, for comparison. It is not the default and a take recorded that way is slower. The pass is cosmetic. It cannot recover a frame the capture never drew, so a take that stuttered while it was recorded still stutters after it, and the motion gate runs on the composited file that ships. Preview a scene without replacing tracked proof assets: PUBLISH=0 DEMO_SERVER=x11 \\\\ PROOF_LLM_BASE_URL=http://:11434/v1 \\\\ bash scripts/demos/record-hd-demo.sh demo-hd The recorder keeps rehearsal output in the temporary directory it prints. Inspect the video and named frames there. Set PUBLISH=1 only for a complete take whose frame guards all passed. The scene’s task prompt is static at proof/prompts/demo-hd.md. The scene stores the secret, submits that prompt once, and sends no phase-by-phase operator prompts; every later turn is the model’s own. A take is published only when every named frame guard passed, so a scene whose model does not reach a guarded surface produces a rehearsal and nothing else. Record on the machine that serves the weights. The endpoint must be a loopback address, or the\\nrecorder will not start; ALLOW_REMOTE_MODEL=1 records against another host and reports it. A\\nsession driven across a network pauses for reasons the recording cannot separate from the product. Before anything is recorded the driver checks three things and exits on any of them: bun scripts/verify-scene.ts demo-hd # the same check, run on its own\\nbun scripts/verify-scene.ts --all Every string the scene waits for must be produced by the submitted prompt, the product’s own\\nsource, the sandbox seed, or a line the scene types. A guard nothing produces does not fail fast:\\nit waits out its timeout, marks the shot missed, and the publish step leaves the previous take’s\\nframe under that name. A needle that comes from somewhere else is declared in the scene: # needle-source: WARP CORE -- printed by the compiled binary\'s banner The driver also requires the model row to exist on that server, and writes -model.txt\\nbeside the frames recording the row, the endpoint, the host and the display server the take was\\nrecorded on. Every binary the run will use is resolved before the first frame: docker, bun for the scene\\ncheck, and ffmpeg and python3 for the publish chain. ImageMagick answers to magick on 7 and convert on 6, and either is accepted. Bun is looked for at ~/.bun/bin/bun when it is not on PATH, because a recording is driven over ssh and a non-login shell there does not carry the\\ninstaller’s entry. A publish tool first called after the recording is a take lost to a PATH\\ndifference, which is why a rehearsal needs only docker. The container is built by one script and tagged from one declaration: bash proof/docker/build-recorder.sh The tag contains the bun version in the root package.json packageManager field,\\nbecause the image contains a bun and the product will not start on a runtime older\\nthan the one it is built for. A bump therefore makes a stale image a missing image,\\nwhich docker reports before a display server starts. Recording with an image built\\non an older bun ends the take from inside the container after the whole rig is up. The recorder reaches a model served on the host through the docker host gateway; the\\nloopback address the driver requires is rewritten for the container by proof/docker/host-endpoint.sh, so no scene needs to name a network address. The archived take remains at capture speed. The landing-page cut keeps the plan, the worker setup, verification and signing at 1×. Visible implementation between the worker launch and the verified build plays at 1.25×. Named marks in the take select those boundaries; untouched screens are shortened to four seconds rather than accelerated.","breadcrumbs":"Testing and verification » Where the settings live, and where the chrome is drawn","id":"1270","title":"Where the settings live, and where the chrome is drawn"},"1271":{"body":"A settings change proves with two frames of the settings screen recorded from the\\nsame scene, one with the setting at its default and one with the operator’s value. SCENE_SETTINGS appends config-file lines to the seeded home before the session\\nstarts, so each arm is seeded rather than toggled by a keybinding that may not land: OUT_DIR=proof/captures/x11/off \\\\ proof/docker/record-x11.sh proof/scenes/settings-pointer.sh OUT_DIR=proof/captures/x11/on SCENE_SETTINGS=\'argot.enabled: true\' \\\\ proof/docker/record-x11.sh proof/scenes/settings-pointer.sh Both arms run the same scene at the same window size, so the only difference between\\nthem is the setting. A pair whose two frames are byte-identical, or whose “on” arm\\ndoes not show the value in effect, is a failed proof.","breadcrumbs":"Testing and verification » Settings differentials","id":"1271","title":"Settings differentials"},"1272":{"body":"A change to a visible surface proves with two frames of the same scene, one on the\\ntree without the change and one on the tree with it. proof/docker/record-x11.sh proof/scenes/.sh # the after arm\\nproof/docker/record-x11-before.sh proof/scenes/.sh # the before arm The after arm writes to proof/captures/x11/. The before arm writes to proof/captures/x11/before/, holding every source file the change touched at the\\ncontent of the base commit for the length of the run, restoring from an in-memory\\ncopy and proving the restore by sha256. No git mutation command runs and the working\\ntree ends byte-identical. Once the change is on main, reproducing the before arm\\nmeans pointing that hold at the commit before it. Both arms record the same scene at the same width and are sampled at the same second\\nof the same script, so the only difference between them is the change. Attach the\\nlabeled Before and After pair to the pull request body. It is never committed: not to assets/, not to a README, not to a handbook page, not to the website. A pair whose two arms differ for an unrelated reason is a failed proof. An arm that\\ndoes not show the surface at all is a failed proof: a lane block is not evidence\\nabout lanes in a frame where no agent is running.","breadcrumbs":"Testing and verification » Before-and-after pairs for a UI change","id":"1272","title":"Before-and-after pairs for a UI change"},"1273":{"body":"A 2560-wide capture published at 1920 loses a small detail to the downsample. A row\\nwhose subject is one block of text names the mark to hold on, and the stage eases into\\nthe region and back out: python3 proof/zoom.py take.mp4 zoomed.mp4 --marks take-marks.tsv --mark todo-board\\npython3 proof/zoom.py --self-check The region is measured, not typed in: the stage diffs the frames around the moment and\\nholds the bounding box of what changed there, padded and clamped inside the frame at\\nthe source aspect ratio. A moment with nothing moving in it produces no file. The hero take drives the same stage from a cue file the scene writes next to its\\nmarks ( zoom-in FRAME [x,y,w,h], pan FRAME x,y,w,h, zoom-out FRAME). The hold between those cues\\nis at least two seconds of real time, with no time compression on that span, so\\nthe stored secret is readable. Magnification is relative to the published wide\\nshot and is at least 2x: proof/glyph-height.py measures capture_width / crop_width on the hold. A missing rect on zoom-in means “measure it”. The zoom ceiling still defaults to the capture width over the published width so a\\n1.33x hold is a crop. The hero’s 2x secret hold is a tighter crop scaled back to\\n1920x1080 — a camera move of the one capture path, not a second recorder. The\\nstage runs on the take, before the cut, and keeps every frame and the recorded\\nrate, so the cadence gate still measures the capture’s own cadence. A scene asks\\nfor one by writing the cue file, or by setting ZOOM_ARGS in scripts/demos/record-hd-demo.sh. --self-check records a synthetic clip whose moving region is known and asserts the\\nmeasured rect, the frame count, the rate and the held magnification. Run it on a\\nrecorder host before a take depends on the stage.","breadcrumbs":"Testing and verification » Zooming into a detail","id":"1273","title":"Zooming into a detail"},"1274":{"body":"Rasterizing a component’s ANSI answers one narrow question quickly and without a\\ndisplay: whether a fill, colour or spacing reads correctly on a grey and a black\\nground. env -u NO_COLOR FORCE_COLOR=3 bun scripts/demos/render-.ts [args] | bun scripts/demos/render-proof.ts --out /tmp/ --width 100 --scale 2 render-proof.ts writes -grey.png with background #1e2127 and -black.png with background #000000. Inspect both. A background fill\\ncan disappear on black while remaining visible as a slab on grey. The output draws a fixture written by hand, at a chosen width, through a constructed\\ncall, so it cannot show that the surface is reachable, that the state is real, or\\nthat the block is positioned, sized and clipped the way a session draws it. It does\\nnot satisfy an evidence requirement. Write the file to a temporary path. scripts/an-off-screen-raster-never-enters-assets.test.ts pins the raster set under assets/ by exact equality, so the list only shrinks, and fails on a demo driver\\nthat writes a render into assets/. Drivers write outside the tracked tree.","breadcrumbs":"Testing and verification » Off-screen component renders are a debugging aid, not a proof","id":"1274","title":"Off-screen component renders are a debugging aid, not a proof"},"1275":{"body":"The repair cascade The hashline edit engine","breadcrumbs":"Testing and verification » Related","id":"1275","title":"Related"},"1276":{"body":"Models often emit tool arguments that are almost valid JSON or almost match the tool schema: stringified objects, trailing commas, truncated payloads, or misnamed fields. Without a repair step those calls fail validation and cost a full turn. Repair runs in the agent loop before argument validation. Clear malformations are coerced into a schema-valid object; ambiguous cases are rejected and returned to the model as an error tool result (no dispatch).","breadcrumbs":"Repair » Tool-call repair","id":"1276","title":"Tool-call repair"},"1277":{"body":"Step What it does Seam Runs at tool dispatch, before schema validation Fix-if-clear Trailing commas, parse sentinels ( __parseError / __rawJson), stringified JSON objects Refuse-if-ambiguous Missing required strings with multiple plausible sources → unrepairable Alias / typo rename Unknown keys that clearly map to a declared property are renamed; ambiguous renames are rejected Strict unknown keys Schemas with additionalProperties: false reject leftover keys after alias resolution Size bound Inputs over 1 MiB are not repaired Disable VEYYON_REPAIR_DISABLE=1, or per-model harness.profiles with repair: false Implementation: packages/coding-agent/src/repair/schema-repair.ts Tests: packages/coding-agent/test/repair/schema-repair.test.ts","breadcrumbs":"Repair » Behavior","id":"1277","title":"Behavior"},"1278":{"body":"The repair cascade: ordered rules Per-model posture: harness profiles Soundness and telemetry The hashline edit engine: edit path (separate from argument repair)","breadcrumbs":"Repair » Related","id":"1278","title":"Related"},"1279":{"body":"Before argument validation, the agent loop runs packages/coding-agent/src/repair/schema-repair.ts in this order: Parse leniency: trailing commas / relaxed JSON; stringified argument blobs. Alias / typo key rename: unknown keys that match a common alias ( filepath → path, contents → content) or a casing/separator typo of a declared property are renamed to the declared name. Refuse when the rename would be ambiguous (two unknown keys map to the same property, one unknown key matches more than one declared property, or the alias target already has a value). Strict unknown-key rejection: when the tool schema declares additionalProperties: false, any key left after alias resolution is rejected rather than dropped or passed through. Ambiguity guard: reject when required string fields have multiple plausible donors. Outcome: clean, repaired (canonical args + hints), or unrepairable (error tool result, no dispatch). Conformance suite: packages/coding-agent/test/repair/schema-repair.test.ts (alias renames, ambiguity refusals, strict-mode refusals, and a guard that strict rejection does not fire on ArkType/Zod wire schemas that synthesize additionalProperties: false for closed-object emission rather than authorial strictness). Per-model enable/disable and tool allowlist hints: Per-model posture.","breadcrumbs":"Repair » The repair cascade » The repair cascade","id":"1279","title":"The repair cascade"},"128":{"body":"Workflow shape: read, edit, verify, stop when done. Edit verification, approvals, and context handling are harness behavior. Provider is configuration: endpoint, credentials, and model id.","breadcrumbs":"Core concepts » Model contract » What stays constant","id":"128","title":"What stays constant"},"1280":{"body":"Why repair exists Repair on edits","breadcrumbs":"Repair » The repair cascade » Related","id":"1280","title":"Related"},"1281":{"body":"The repair hook receives the active model id so behavior can vary by model. Configure overrides with harness profiles: harness.profiles in config.yml, or harness-profiles.yml in the agent dir. Keys are provider/model-id or provider/* wildcards. harness: profiles: \\"anthropic/claude-sonnet-4-20250514\\": repair: true tools: [\\"read\\", \\"edit\\", \\"grep\\", \\"bash\\"] promptSectionOrder: [\\"tool-policy\\", \\"delivery-contract\\"] \\"google/*\\": repair: false Field Effect repair: false Skip schema repair for that model tools: [...] Filter the initial tool allowlist promptSectionOrder: [...] Reorder default system-prompt banner sections Addressable banner sections are role, runtime, tool-policy, execution-workflow, delivery-contract, project, shorthand, and shorthand-handles. Listed sections move first in the order you provide, after the fixed system-conventions preamble. Unlisted sections keep section-registry order. One provider-cache boundary remains: runtime sections ( project, shorthand, and shorthand-handles) cannot move ahead of the static statement-assembled prefix. If you list a runtime section before a static section, Veyyon keeps the cache boundary and logs a warning. An unknown or non-string section rejects the whole list with a warning, so a hand-edited file cannot apply an order you did not write. tools follows the same rule: one invalid entry drops the whole allowlist rather than silently denying the model a tool. A harness-profiles.yml file that cannot be read or parsed is reported with its path and reason, and no profiles take effect. Custom system prompts have no banner sections, so Veyyon ignores this setting with a warning. Disable all repair process-wide: VEYYON_REPAIR_DISABLE=1. See Why repair exists and Models.","breadcrumbs":"Repair » Per-model posture » Per-model repair posture","id":"1281","title":"Per-model repair posture"},"1282":{"body":"","breadcrumbs":"Repair » Soundness and telemetry » Observability for repair and sessions","id":"1282","title":"Observability for repair and sessions"},"1283":{"body":"/usage in the TUI and veyyon stats on the CLI: token and usage views Status line token accounting ( token_*, context_pct, cost) Coding-agent structured logger","breadcrumbs":"Repair » Soundness and telemetry » Usage and sessions","id":"1283","title":"Usage and sessions"},"1284":{"body":"When OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set, the process registers an OTLP/protobuf trace exporter and exports agent-loop spans ( invoke_agent, chat, execute_tool, …). Standard OTEL_* env vars apply ( OTEL_SERVICE_NAME, headers, OTEL_SDK_DISABLED, OTEL_TRACES_EXPORTER=none). Transport is http/protobuf only. See packages/coding-agent/src/telemetry-export.ts.","breadcrumbs":"Repair » Soundness and telemetry » OpenTelemetry","id":"1284","title":"OpenTelemetry"},"1285":{"body":"Observability Repair cascade","breadcrumbs":"Repair » Soundness and telemetry » Related","id":"1285","title":"Related"},"1286":{"body":"Default edit mode is hashline ( edit.mode: hashline in config.yml), implemented in @veyyon/hashline. Veyyon applies file changes through the edit tool (hashline patch language by default). The\\nmodel copies [PATH#TAG] anchors from read / grep / write output, then emits SWAP, DEL,\\nand INS operations against numbered lines. Snapshot tags detect stale anchors and drive recovery. Alternate modes ( apply_patch, patch, replace) exist for compatibility; hashline is the\\ndefault and the path Veyyon optimizes for.","breadcrumbs":"The edit engine » The hashline edit engine","id":"1286","title":"The hashline edit engine"},"1287":{"body":"read or grep records a whole-file snapshot and prints [relative/path#TAG] plus LINE:content rows ( TAG is a four-hex snapshot id). The model sends edit with an input string: one or more [PATH#TAG] sections and hashline\\nops ( SWAP N.=M:, DEL N.=M, INS.PRE N: / INS.POST N: / INS.HEAD / INS.TAIL, block ops SWAP.BLK / DEL.BLK / INS.BLK.POST, plus whole-file REM and MV DEST). @veyyon/hashline parses, verifies the tag against the snapshot store, applies ops, and\\nreturns a fresh [path#TAG] header plus a compact diff preview. write can create or overwrite whole files; in hashline display mode it also mints snapshot\\nheaders for the next edit. edit.mode and VEYYON_EDIT_VARIANT select among hashline, apply_patch, patch, and replace.","breadcrumbs":"The edit engine » How a hashline edit works","id":"1287","title":"How a hashline edit works"},"1288":{"body":"Property Behavior Stale anchor Mismatch errors name the tag; snapshot recovery can suggest the current file hash Line numbers 1-indexed; body rows use +TEXT prefix Order Non-overlapping hunks; overlapping regions fail with an error Encoding Applies to normalized content; BOM and dominant line ending preserved on write","breadcrumbs":"The edit engine » Invariants","id":"1288","title":"Invariants"},"1289":{"body":"User guide: Editing and repair Tool contract: docs/tools/edit.md Read/grep anchors: docs/tools/read.md, docs/tools/grep.md Settings: edit.mode in docs/handbook/src/reference/settings.md There is no veyyon-edit Rust crate, no V4A-only write path, and no make_update_patch envelope\\nrouting. General schema-based tool-call repair is shipped, see Repair overview.","breadcrumbs":"The edit engine » Further reading","id":"1289","title":"Further reading"},"129":{"body":"Configuring providers: Ollama, LM Studio, Anthropic, and custom\\nOpenAI-compatible endpoints. Models and providers: choosing and switching models in a session. Safety: boundaries around tool use and model output. Permission model: the approval modes. Signing in: interactive and env-var auth paths.","breadcrumbs":"Core concepts » Model contract » Next","id":"129","title":"Next"},"1290":{"body":"Schema-based tool-call repair runs on all tools, including edit, before argument validation. Hashline parsing and verification inside @veyyon/hashline is a separate step. When a model emits a malformed edit (or compatibility apply_patch) call: Schema repair attempts JSON recovery and ambiguity refusal at the agent-loop seam. If repair succeeds, arguments proceed to hashline / apply_patch validation and dispatch. If repair cannot disambiguate, the loop returns an error tool result with hints (no dispatch). Hashline still applies envelope stripping and bare-body handling inside @veyyon/hashline. See Repair overview and The hashline edit engine.","breadcrumbs":"The edit engine » Repair on edits » Repair on edits","id":"1290","title":"Repair on edits"},"1291":{"body":"Correctness properties of the edit path. The default engine is hashline ( @veyyon/hashline); apply_patch, patch, and replace remain as compatibility modes. See The hashline edit engine.","breadcrumbs":"The edit engine » Edit-path properties » Edit-path properties","id":"1291","title":"Edit-path properties"},"1292":{"body":"Edits must not silently rewrite encoding. The path strips a leading UTF-8 BOM before matching and restores it afterward, and restores the file’s dominant line ending (CRLF or LF) on write. Matching runs against a normalized LF body. Without this, one edit can rewrite CRLF to LF or drop a BOM.","breadcrumbs":"The edit engine » Edit-path properties » BOM and line endings","id":"1292","title":"BOM and line endings"},"1293":{"body":"The edit tool can apply several disjoint changes in one call. Anchors match against the original file (not incrementally); replacements are ordered so earlier growth does not invalidate later matches. Ambiguous anchors, overlapping regions, and no-ops fail with an actionable error.","breadcrumbs":"The edit engine » Edit-path properties » Multi-edit in one call","id":"1293","title":"Multi-edit in one call"},"1294":{"body":"Hashline avoids re-echoing surrounding text: the model references spans by [PATH#TAG] snapshot anchors from read / grep / write, then sends operations ( SWAP, DEL, INS) and new text. Stale tags fail verification instead of applying a wrong edit. Default: edit.mode: hashline in config.yml.","breadcrumbs":"The edit engine » Edit-path properties » Hashline","id":"1294","title":"Hashline"},"1295":{"body":"Non-parallel tools take an exclusive lock so file mutations from one turn are serialized. Same-file concurrent edits from subagents still need care; independent files may proceed under the tool locking rules.","breadcrumbs":"The edit engine » Edit-path properties » Concurrency","id":"1295","title":"Concurrency"},"1296":{"body":"The write path normalizes by ensuring a trailing newline on the final line when applying some write forms. Tests cover this behavior; see edit/write tool tests in packages/coding-agent.","breadcrumbs":"The edit engine » Edit-path properties » Trailing newlines","id":"1296","title":"Trailing newlines"},"1297":{"body":"The harness provides the model registry and provider auth. A provider is the API namespace ( anthropic, openai, google, custom gateways, local ollama, …). A model is provider/model-id. Veyyon assembles the selectable catalog from: Bundled pi-catalog models ~/.veyyon/profiles/default/agent/models.yml custom providers and models Runtime discovery (Ollama, LM Studio, discovery-enabled gateways) Extension-registered providers A model is available when its provider is not disabled and credentials resolve (or the provider\\nis keyless/local).","breadcrumbs":"Provider stack » The provider stack and bring-your-own-key","id":"1297","title":"The provider stack and bring-your-own-key"},"1298":{"body":"Resolution order (first match wins): CLI --api-key (ephemeral) models.yml apiKey on a custom provider Stored API key / OAuth in the agent auth store ( ~/.veyyon/profiles/default/agent/agent.db) Provider environment variables (see docs/handbook/src/reference/providers.md) Custom fallback resolvers in models.yml Use /login, /logout, or veyyon OAuth flows in setup. Provider-scoped logins do not cross\\nproviders.","breadcrumbs":"Provider stack » Credentials","id":"1298","title":"Credentials"},"1299":{"body":"Add OpenAI- or Anthropic-compatible endpoints as data: # ~/.veyyon/profiles/default/agent/models.yml\\nproviders: my-gateway: baseUrl: https://api.example.com/v1 api: openai-completions apiKey: MY_GATEWAY_API_KEY models: - id: claude-sonnet name: Claude Sonnet via Gateway contextWindow: 200000 maxTokens: 8192 Validate with veyyon models list and /model.","breadcrumbs":"Provider stack » Custom providers","id":"1299","title":"Custom providers"},"13":{"body":"The agent loop, TUI, session format, MCP, skills, hooks, and extensions operate independently of specific model providers. Providers are configured in the active profile agent directory through config.yml or /setup, with account management via /providers.","breadcrumbs":"Design and mechanisms » Mechanisms » Provider-neutral loop","id":"13","title":"Provider-neutral loop"},"130":{"body":"Features, in two groups: the surfaces of an everyday session, then what extends and customizes the agent. Worked examples are at the end.","breadcrumbs":"Features » Features","id":"130","title":"Features"},"1300":{"body":"ollama, llama.cpp, and lm-studio are treated as keyless when the engine responds. Each has its\\nown discovery variable, not a shared VEYYON_OSS_* pair: OLLAMA_BASE_URL (or OLLAMA_HOST), LLAMA_CPP_BASE_URL, LM_STUDIO_BASE_URL, see Environment variables. User guides: Models, Configuring providers. There is no separate backends.toml catalog subsystem; Veyyon uses models.yml plus the bundled\\ncatalog.","breadcrumbs":"Provider stack » Local engines","id":"1300","title":"Local engines"},"1301":{"body":"The harness assembles system and developer prompts and adapts them per provider. Base instructions\\nencode control-flow discipline: explore → plan → edit → verify → STOP. Plan mode ( /plan) and\\ngoal mode ( /goal) add gating on top of the default prompt stack.","breadcrumbs":"Provider stack » Execution-order prompts » Execution-order prompts","id":"1301","title":"Execution-order prompts"},"1302":{"body":"A default system prompt plus per-tool prompts Per-provider streaming and tool wire format Skills and rules inject additional context via discovery Edit tool prompts switch with edit.mode (the hashline prompt when hashline is active). There is no backends.toml-driven catalog or per-backend prompt tuning, and apply_patch is not the\\ndefault edit surface, Veyyon uses hashline by default.","breadcrumbs":"Provider stack » Execution-order prompts » Delivery","id":"1302","title":"Delivery"},"1303":{"body":"You do not have to read the source to find out what Veyyon sends a model. Run: veyyon prompt --prompts That lists every prompt by id, grouped by the directory it lives in, with one line on what\\neach is for. An id is the file’s path under that directory without the .md, so turn-control/auto-continue and dialect/gemma name their own files. Then look at one: veyyon prompt --prompt subagent/system-prompt The lookup spans every registry, so an id from any of them works without specifying its package.\\nA mistyped id is rejected with the nearest real id quoted back. For the system prompt itself, veyyon prompt prints the assembled text and veyyon prompt --sections breaks it down by section with the byte and token cost of each. veyyon prompt --statements goes one level finer. The prompt is assembled from named statements,\\none per rule, so this prints what each individual rule costs you: statement bytes tokens share condition\\nexecution-workflow/verify 1099 275 10.6% always\\ndelivery-contract/personality 1098 274 10.5% personality\\ntool-policy/lsp 412 103 4.0% tools has lsp Two things to read from it. The cost is MARGINAL: it is what the prompt would be shorter by without\\nthat rule, not the length of the rule’s text, so the numbers add up to their section rather than\\nexceeding it. And the condition states what turns the rule on, which is what you need to know\\nbefore deciding a rule is not earning its tokens. Under the table is every rule this configuration leaves out, with the condition that would include\\nit, so a rule being off is visible as a fact rather than as an absence you have to notice: not in this prompt (33 of 68): tool-policy/delegation-gates needs tools has task runtime/obsidian-vault-url needs hasObsidian To read one of those rules, name it: veyyon prompt --statement delivery-contract/personality You get the rule’s rendered text, which is what the model sees rather than the template behind it. If\\nthe rule is not in this prompt you get the condition that would include it and why the rule exists,\\nand the command still exits 0, because a rule being off is a configuration and not a failure. An id\\nthat does not exist exits non-zero and quotes the ids of the section you named. Both read your real configuration. The settings the prompt is gated on – your personality, whether\\nsubagent delegation is preferred or required, whether Mermaid diagrams are rendered, which tool\\ndialect applies – are resolved from your profile config.yml before the prompt is\\nassembled, so what you see is what a session would send. Change a setting, run it\\nagain, and the difference is visible. The prompt is only half of what a turn pays before your first message. Every active tool ships a\\ndescription and a parameter schema on every request, and veyyon prompt --tools prices that half: tool bytes desc schema tokens share\\nedit 8158 2013 27 2040 15.4%\\neval 5720 1258 173 1431 10.8%\\nlaunch 5563 684 707 1391 10.5%\\nTOTAL 53060 10011 3266 13277 17 tools cost 13277 tokens; the system prompt costs 23403. Every request pays both. The row set is the tool set your configuration loads, so disabling a tool removes its row and its\\ncost. The two halves are separated because they are cut differently: a description is prose you can\\nshorten, a schema is the parameter list and shrinks only by dropping parameters. Nothing is written while you look. The command opens no database, migrates nothing, and leaves no\\nmarker files, so inspecting the prompt cannot change what the next session does.","breadcrumbs":"Provider stack » Execution-order prompts » Seeing every prompt","id":"1303","title":"Seeing every prompt"},"1304":{"body":"The system prompt is not one string. It is an ordered list of parts, and the boundary between the\\nfirst part and the rest is a provider-caching contract rather than a stylistic choice. To change\\nwhat a part contains, read System prompt customization. To\\nunderstand why the parts are split where they are, and where a new part would belong, read docs/internal/system-prompt-architecture.md.","breadcrumbs":"Provider stack » Execution-order prompts » Going deeper","id":"1304","title":"Going deeper"},"1305":{"body":"How the coding-agent assembles the system prompt sent to the model, and what you can control. The system prompt is ASSEMBLED. It is composed from the section registry and from statements gated on your settings; there is no file on disk holding its text for you to edit. PROMPT_SECTIONS/ is how you change what a section contains, per statement and validated. --system-prompt remains for a caller that supplies its own prompt for one invocation (the SDK, an eval harness). For the implementation side of the same subsystem, the block/tier model, the ordering rules, and how to decide where a new section belongs, see System prompt architecture, and for how the cached prefix is marked on the wire see Prompt caching. Veyyon no longer reads a SYSTEM.md or APPEND_SYSTEM.md file from disk. SYSTEM.md replaced the whole assembled prompt with hand-written text. APPEND_SYSTEM.md added text to the end of it, which is what AGENTS.md already does, at more scopes and with a directory walk-up that APPEND_SYSTEM.md never had. Both were discovered out of any repository you entered, and a new profile copied them along under a checkbox labelled AGENTS.md. To add instructions, write them in AGENTS.md. To change the text of a prompt section, use PROMPT_SECTIONS/. If either removed file is still on disk, veyyon reports it at launch and points at the replacement rather than ignoring it in silence. Primary implementation: packages/coding-agent/src/system-prompt.ts ( buildSystemPrompt) packages/coding-agent/src/main.ts (resolves the two prompt flags; there is no file discovery for either) packages/coding-agent/src/prompts//rows.ts (the prompts one directory owns, each with its id and purpose) and packages/coding-agent/src/prompts/registry.ts (which aggregates all of them) packages/utils/src/prompt-registry.ts (what a registry IS: the row shape, definePromptRegistry, and requirePromptFrom, the one lookup that rejects an unknown id) packages/coding-agent/src/system-prompt-builder/banner-grammar.ts (what a banner IS for every prompt: how one is written, how one is recognised, and splitBanneredDocument, the one parser that cuts a prompt at its banners) packages/coding-agent/src/system-prompt-builder/section-registry.ts (the section registry: which sections exist, their banners, and their order) packages/coding-agent/src/system-prompt-builder/prompt-sections.ts (the system prompt’s own section names and the reordering a harness profile specifies) packages/coding-agent/src/prompts/session/system-prompt.md (zero-prose outer scaffold containing only {{templateSections}}) packages/coding-agent/src/prompts/session/custom-system-prompt.md (the base template a caller-supplied prompt switches to) packages/coding-agent/src/prompts/session/project-prompt.md (project/environment footer) packages/coding-agent/src/utils/host-environment.ts (the workstation rows that footer renders: OS, kernel, arch, CPU, GPU, terminal)","breadcrumbs":"Provider stack » Customizing the system prompt » System Prompt Customization","id":"1305","title":"System Prompt Customization"},"1306":{"body":"A package defines its own prompts. Each package that ships any keeps them under its own src/prompts/ directory with a registry.ts beside them, and that registry is the only module allowed to import one: Package Prompts directory What is in it @veyyon/coding-agent packages/coding-agent/src/prompts/ the system prompt, the subagent prompt, tool descriptions, and every turn the agent takes on its own behalf @veyyon/agent-core packages/agent/src/prompts/ compaction: summarizing a session, branch summaries, handoff documents @veyyon/ai packages/ai/src/prompts/ one format guide per tool-call dialect, plus the tool-catalog template that contains them @veyyon/hashline packages/hashline/src/ the hashline patch language, which is the edit tool’s description @veyyon/metaharness packages/metaharness/adapters/edit/prompts/ the edit benchmark’s task, system, and retry prompts An id is the file’s path under its registry’s directory without the .md, so turn-control/auto-continue is packages/coding-agent/src/prompts/turn-control/auto-continue.md and dialect/gemma is packages/ai/src/prompts/dialect/gemma.md. Ids are unique across the registries, so you never have to name the package to ask about a prompt. @veyyon/hashline is the one package whose prompt is not under a prompts/ directory: its single file is published at @veyyon/hashline/prompt.md for anyone embedding hashline in their own agent, so moving it would break a public subpath. To find a prompt, read the registry or run veyyon prompt --prompts, which lists every id in the four product registries under its directory, with a line saying what it is for. The benchmark harness’s prompts are not listed there: they are used by a measurement tool rather than by the agent. The registries are the whole set by construction, not by anyone remembering to add a row. The import is the registration, so a prompt file with no row is unreachable code, and prompt-registry-coverage.test.ts fails if the set on disk and the set in a registry disagree in either direction, or if any module outside a registry imports a .md as text. A registry is one definePromptRegistry(dir, rows) call, and the descriptor it returns is what other code takes. That matters for the same reason the rest of this section does: the directory is stated once, in that call, and veyyon prompt, the coverage suite and the generated inventory read it off the descriptor instead of each writing the path again. They used to write it again, and the inventory’s copy had gone stale, listing three directories while claiming one per package. The same test fails if a directory is written down twice. A descriptor gives you dir, prompts, ids, text(id), require(id), has(id) and fileFor(id). Use prompts[\\"some/id\\"].text where the id is a literal, since that is checked at compile time; use require(id) where the id comes from a variable, because it throws on an unknown one rather than handing back a prompt with no text.","breadcrumbs":"Provider stack » Customizing the system prompt » Where prompts live","id":"1306","title":"Where prompts live"},"1307":{"body":"Two user-controllable inputs feed prompt assembly. Each resolves as either a literal string or, if the argument is a path to a readable file, the contents of that file ( resolvePromptInput). A value that fails to read is an error when it has no spaces and either contains a path separator or ends in a prompt-file extension ( .md, .markdown, .txt, .text, .prompt). --system-prompt ./promtps/main.md states the path and the reason rather than quietly using the string ./promtps/main.md as your whole system prompt. Prompt text is unaffected: a one-line prompt containing a slash, or ending in a dotted word, is used as written. To pass text that would otherwise read as a path, put it on more than one line, since no path contains a newline. Input Source Effect --system-prompt CLI flag Replaces block 0: the default stable instructions. Highest precedence. --append-system-prompt CLI flag Adds a prompt block. Without a custom system prompt it goes after all default blocks; with one it goes after the custom block and before the preserved project/environment footer. Both are per-invocation flags, for a caller that supplies its own prompt for one run: the SDK, an eval harness, a benchmark adapter. Neither has a file on disk that veyyon discovers on your behalf. Neither flag is discovered from a file, so there is no precedence list to learn and no path where a repository you enter supplies prompt text. Instruction files still work the way they always have: AGENTS.md is discovered from the global location, the active profile, and every directory walked up from the working directory to the repository root, and it is inlined into the prompt. See docs/handbook/src/architecture/config.md for the discovery contract.","breadcrumbs":"Provider stack » Customizing the system prompt » 1) Inputs","id":"1307","title":"1) Inputs"},"1308":{"body":"Normal CLI startup resolves the two flag values, then hands them to prompt assembly in packages/coding-agent/src/main.ts: export function applyResolvedSystemPromptInputs( options: CreateAgentSessionOptions, resolvedSystemPrompt: string | undefined, resolvedAppendPrompt: string | undefined,\\n): void { if (resolvedSystemPrompt) { options.customSystemPrompt = resolvedSystemPrompt; } if (resolvedAppendPrompt) { options.appendSystemPrompt = resolvedAppendPrompt; }\\n} buildSystemPrompt in packages/coding-agent/src/system-prompt.ts then selects the base template: No custom prompt: statement modules assemble every stable instruction section. The section registry supplies section identity, order, and banners. The zero-prose system-prompt.md scaffold contributes only the {{templateSections}} slot. Custom prompt present: the base switches to session/custom-system-prompt.md, which renders your custom text plus the append prompt, context files, discovered skills, always-apply rules, and rules. The stable statement modules, tool inventory, and default workflow guidance are not rendered. In both cases the dynamic project/environment footer from project-prompt.md still renders after the base template. It includes workstation information, the active profile name, the agent and skills directories, the global and profile AGENTS.md paths, the directory-context list, workspace tree, date, and cwd. With a custom prompt the footer omits context files and the append prompt because the custom template already rendered them. Consequences for normal CLI use: Passing --system-prompt replaces the stable default instructions and tool inventory. Context files, skills, always-apply rules, and rules are kept (the custom template renders them), and the dynamic project/environment footer remains. Passing --append-system-prompt without a custom system prompt appends your text after the default instructions. Passing both produces: custom system prompt text, append prompt text, then the kept skills/rules/context files and the dynamic project/environment footer. For everyday use you want neither flag. To add instructions, write AGENTS.md. To change the text of one section, use PROMPT_SECTIONS/.","breadcrumbs":"Provider stack » Customizing the system prompt » 2) Replace vs. append","id":"1308","title":"2) Replace vs. append"},"1309":{"body":"Contents of --system-prompt and --append-system-prompt are treated as plain text. They are resolved before prompt-block replacement and are not rendered as Handlebars templates. An explicitly supplied empty or whitespace-only custom prompt is still a replacement. It does not\\nfall back to the shipped statements. If it renders no base content, Veyyon omits the empty provider\\nblock and keeps the dynamic project footer. The built-in prompt templates are Handlebars ( packages/utils/src/prompt.ts), but user-provided strings are not compiled with that renderer. The assembler inserts each resolved flag value into a Handlebars parent template as a string. Handlebars does not recursively render substituted text. Concretely: {{! parent template, handled by Handlebars }}\\n{{customPrompt}} If the value passed to --system-prompt contains: Working in {{cwd}} on {{date}}.\\n{{#if hasMemoryRoot}}Memory enabled.{{/if}} the rendered output contains those characters verbatim, {{cwd}}, {{#if hasMemoryRoot}}, etc. are NOT substituted. They will be shown to the model as literal Handlebars syntax. This is by design. The internal template variables ( cwd, date, environment, workspaceTree, skills, rules, toolRefs, hasMemoryRoot, hasObsidian, mcpDiscoveryServerSummaries, …) are not a supported public surface, they change between releases as the prompt is rewritten, and they would couple user configs to internals. Treat them as private. There is no supported public templating surface for a caller-supplied prompt. Write plain text (or markdown) only.","breadcrumbs":"Provider stack » Customizing the system prompt » 3) Templating contract","id":"1309","title":"3) Templating contract"},"131":{"body":"These are the parts of the TUI you touch every session: Status line and multi-agent UI covers the status segments, the Agent Control Center ( /agents), jobs, and the swarm view. Keybindings covers the chords. The composer gives you prompt history, @ and / completion, and Esc to interrupt. See Quickstart and Keybindings. Web search covers searching from inside a session.","breadcrumbs":"Features » Interactive surfaces","id":"131","title":"Interactive surfaces"},"1310":{"body":"","breadcrumbs":"Provider stack » Customizing the system prompt » 4) Recommended patterns","id":"1310","title":"4) Recommended patterns"},"1311":{"body":"Write an AGENTS.md. The default instructions and the project footer stay intact, and your text is inlined into the prompt with the other context files. This is the everyday answer, and it needs no flag. # ~/.veyyon/profiles/default/agent/AGENTS.md\\nPrefer Bun APIs over Node APIs in this project.\\nWhen you change a public function, run `bun check` before yielding. An AGENTS.md beside the code applies to that project; the one in your profile applies to every session in that profile; the global one applies everywhere. All of them are discovered for you.","breadcrumbs":"Provider stack » Customizing the system prompt » “Tweak the default”: keep default, add a few rules","id":"1311","title":"“Tweak the default”: keep default, add a few rules"},"1312":{"body":"Pass --system-prompt. You replace the stable default instructions in block 0, but startup still preserves the dynamic project/environment footer block ( project-prompt.md): workstation info, context files, dir-context list, workspace tree, current date, cwd, and related project context. $ veyyon --system-prompt ./reviewer-prompt.md There is no file veyyon picks up on its own for this. A prompt that replaces the whole assembly is a per-invocation decision by a caller who wants exactly that, not a setting that follows you into every session. Reach for this only when you want a genuinely different base prompt. If you are keeping most of the default and changing one part, use PROMPT_SECTIONS/ instead (section 8): it edits a single section and leaves the rest as shipped, so you do not have to maintain a copy of the default tool guidance, exploration rules, or workflow rules.","breadcrumbs":"Provider stack » Customizing the system prompt » “Replace the stable default instructions”: bring your own base prompt","id":"1312","title":"“Replace the stable default instructions”: bring your own base prompt"},"1313":{"body":"Use AGENTS.md, not --system-prompt. The tool inventory, role guidance, and default exploration and workflow rules come from statement modules in src/system-prompt-builder/statements/. A custom system prompt switches to session/custom-system-prompt.md, so those default statements are not available to the model. A custom system prompt still keeps the generated project content: session/custom-system-prompt.md renders context files, discovered skills, always-apply rules, and rules alongside your text, and the project-prompt.md footer still carries workstation info, the workspace tree, the current date, and cwd. If you wanted a full replacement only to change one part, use PROMPT_SECTIONS/ (section 8). It replaces or appends to one registry section and keeps every other statement module.","breadcrumbs":"Provider stack » Customizing the system prompt » “Customize while keeping the tool inventory and default workflow guidance”","id":"1313","title":"“Customize while keeping the tool inventory and default workflow guidance”"},"1314":{"body":"The system prompt does not affect the model call that titles a new session. Create the title-specific prompt file instead: # ~/.veyyon/profiles/default/agent/TITLE_SYSTEM.md\\nGenerate a session name using lowercase `:`.\\nIf the message contains no concrete task, output exactly `none`. TITLE_SYSTEM.md is discovered project-first, then user, across the config bases. It is a prompt for a side call that titles a session, not the agent’s own system prompt, which is why it is still a file. When absent, Veyyon uses the bundled title-system.md / tiny-title-system.md prompts. When present, both the online title path and the local tiny-model path keep the ... wrapper while using this file as the system turn.","breadcrumbs":"Provider stack » Customizing the system prompt » “Customize automatic session titles”","id":"1314","title":"“Customize automatic session titles”"},"1315":{"body":"The CLI flag path intentionally preserves defaultPrompt.slice(1). Code using CreateAgentSessionOptions.systemPrompt directly can return a full replacement array and omit the project footer, but that is not what --system-prompt does.","breadcrumbs":"Provider stack » Customizing the system prompt » “Replace everything, including project context”: SDK-only","id":"1315","title":"“Replace everything, including project context”: SDK-only"},"1316":{"body":"Use PROMPT_SECTIONS/, described in section 8. Put your text in PROMPT_SECTIONS/.append.md to add to a section, or PROMPT_SECTIONS/.md to replace it. Every other section stays exactly as shipped, including the generated skills, rules, and tool guidance, so this is the option to reach for whenever you want to change one thing rather than own the whole prompt. Run veyyon prompt --sections to see the section names for your configuration.","breadcrumbs":"Provider stack » Customizing the system prompt » “Change one section of the default instructions, keep the rest”","id":"1316","title":"“Change one section of the default instructions, keep the rest”"},"1317":{"body":"A custom base and an append value are each rendered once. dedupeAlwaysApplyRules also omits an always-apply rule when its body already appears verbatim in the custom base, append value, or a loaded context file.","breadcrumbs":"Provider stack » Customizing the system prompt » 5) Deduplication","id":"1317","title":"5) Deduplication"},"1318":{"body":"Veyyon does not discover a whole-prompt replacement or append file. Both whole-prompt inputs are flags. Persistent PROMPT_SECTIONS/ files remain discoverable because each file targets one validated assembled section instead of bypassing assembly. The instruction files that ARE discovered are a different mechanism, and they still walk the tree: AGENTS.md is read from the global location, from the active profile’s agent directory, and from every directory between the working directory and the repository root. See docs/handbook/src/architecture/config.md.","breadcrumbs":"Provider stack » Customizing the system prompt » 6) Discovery paths","id":"1318","title":"6) Discovery paths"},"1319":{"body":"Goal Use Add an instruction on top of the full default prompt AGENTS.md (profile, global, or beside the code) Change one section and keep the rest PROMPT_SECTIONS/.append.md (see section 8) Replace the stable default instructions for one run --system-prompt Append text for one run --append-system-prompt Customize automatic session titles TITLE_SYSTEM.md; the agent’s own prompt does not affect title generation Use {{cwd}} / {{date}} / other internals in my file Not supported. Caller-supplied prompts are inserted verbatim. See the prompt a configuration actually produces veyyon prompt (see section 9) Change instructions per repository An AGENTS.md in that repository Change instructions everywhere The global AGENTS.md, or the one in your profile’s agent directory","breadcrumbs":"Provider stack » Customizing the system prompt » 7) Quick reference","id":"1319","title":"7) Quick reference"},"132":{"body":"These add capabilities or change how the agent runs: Feature What it adds Plan mode, goals, and vibe Engine modes that plan, pursue an objective, or direct workers Skills Reusable, on-demand instructions Plugins Packaged extensions Hooks TypeScript modules that run on events with pi.on(...) MCP Model Context Protocol servers and tools Branching Forking a session into parallel lines of work Subagents Delegating work to background agents Memory Project-scoped recall across sessions Profiles Isolated config, sessions, and state per name Personalities Named voice and behavior presets Speech Text-to-speech output Export and import Moving sessions in and out Connectors Third-party app integrations Approvals The approval-mode boundary in depth Secrets Credentials the agent uses by placeholder and never sees Code review Reviewing branches, commits, and uncommitted work Non-interactive mode Running Veyyon from a script","breadcrumbs":"Features » Extend and customize","id":"132","title":"Extend and customize"},"1320":{"body":"--system-prompt replaces the stable default template. The generated project footer, context files, discovered skills, and rules remain, but the tool inventory, default workflow guidance, and settings-gated default sections do not render. If you only want to add a rule or reword one part, use the narrower mechanism. PROMPT_SECTIONS/ changes one section and leaves the others exactly as shipped. The default template is a sequence of named sections. To see the names for your configuration, run: veyyon prompt --sections Put a file named after a section in a PROMPT_SECTIONS/ directory under the active profile’s agent dir: ~/.veyyon/profiles/default/agent/PROMPT_SECTIONS/ # default profile\\n~/.veyyon/profiles//agent/PROMPT_SECTIONS/ # named profile The active profile is the only location. A repository’s .veyyon/PROMPT_SECTIONS/ used to be read and could replace a shipped section outright; a working tree no longer contributes prompt sections. Two filename forms decide what happens: File Effect .append.md Your text is added at the end of that section. The shipped text stays, including anything added to it in a later release. .md Your body text replaces that section. The section registry adds the canonical banner. Prefer append. It survives upgrades, because the shipped section is reused rather than copied. To add a rule to the delivery contract: # ~/.veyyon/profiles/default/agent/PROMPT_SECTIONS/delivery-contract.append.md\\nAlways include the exact command you ran when you report a test result. Everything else in the prompt is untouched. Overriding one section never changes another, and never disables a setting-gated block in a different section. A few rules worth knowing: A file that specifies a section that does not exist is an error, not a no-op. The message lists the valid names. A typo that silently did nothing would leave you believing a change was live when it was not. Section names are the ids veyyon prompt --sections prints: conventions, role, runtime, tool-policy, execution-workflow, delivery-contract. The systemPrompt.sectionOverrides config key accepts the same ids, and also accepts the camelCase spelling ( toolPolicy) that the SDK uses for its property names. Both reach the same section, so you can use the id everywhere and never think about the difference. Replacement and append files contain section body text only. Do not copy any registered NAME and ============== banner into the file. The section registry adds the target section’s canonical banner, and rejects any banner-shaped text that could manufacture a second section. An empty or whitespace-only append file is a no-op. PROMPT_SECTIONS/ cannot be combined with --system-prompt. A custom prompt has no sections to override, so asking for both is an error rather than a silent choice between them. A directory that is not there means you have no overrides, and that is the ordinary case. A directory that IS there and cannot be read is an error stating the path and the reason, as is a file inside it that cannot be opened. Both would otherwise run the shipped prompt while your files sat on disk looking applied.","breadcrumbs":"Provider stack » Customizing the system prompt » 8) Changing one section: PROMPT_SECTIONS/","id":"1320","title":"8) Changing one section: PROMPT_SECTIONS/"},"1321":{"body":"system-prompt.md is a zero-prose scaffold containing only {{templateSections}}. It is not a useful way to inspect instructions. The text comes from statement modules, and conditions depend on the active tools, settings, workspace, and model. veyyon prompt prints the assembled prompt for your current configuration, without starting a session. veyyon prompt # the full assembled prompt\\nveyyon prompt --sections # a size breakdown, largest section first\\nveyyon prompt --section role # one section\'s text\\nveyyon prompt --json # the same breakdown, machine readable\\nveyyon prompt --no-tools # assemble with no tools\\nveyyon prompt --tools # what each active tool description and schema costs --sections answers “what is taking up my prompt”: section source block bytes tokens share\\nproject runtime 1 23191 5798 62.2%\\ntool-policy template 0 4406 1102 11.8%\\ndelivery-contract template 0 4053 1014 10.9% The source column describes the provider-cache source class. template means a static statement-assembled section in block 0. It does not mean prose comes from system-prompt.md. runtime means a separately emitted section computed from workspace or session state. Under the table you get the sections that are NOT in this prompt: not in this prompt: shorthand optional the shorthand notation block, taught when the encode gate is open shorthand-handles optional the handle table for loaded projects This is the difference between a prompt that is small and one that is broken. An optional section is absent because its feature is off, which is ordinary. A REQUIRED section is absent because assembly failed, and the command reports it and exits 1: 1 REQUIRED section did not render (role). This prompt is incomplete, not minimal. Every other exit is 0, so veyyon prompt --sections works as a check in a script. --json contains the same information in a missing array, present even when empty. The block column is the index of the part in the ordered array buildSystemPrompt returns. Block 0 is the static prefix that providers cache; later blocks hold text that changes often. Each provider serializes those parts its own way (Anthropic sends them as separate system text blocks, most OpenAI-wire paths as separate system or developer messages, Gemini as separate systemInstruction parts), so a block is a separate part, not necessarily a separate message. Moving content from a later block into block 0 would break the cache, which is why the breakdown reports the boundary rather than hiding it. See System prompt architecture for the per-provider mapping. --no-tools is useful for finding tool-gated text: run it, diff against the normal output, and every line that disappeared was behind a tool being available. Use --json to compare two configurations mechanically, for example to check that a settings change altered only the section you expected.","breadcrumbs":"Provider stack » Customizing the system prompt » 9) Seeing the prompt: veyyon prompt","id":"1321","title":"9) Seeing the prompt: veyyon prompt"},"1322":{"body":"The system prompt is not the only prompt a model receives. Delegated tasks run under a subagent prompt, and there are separate prompts for summarizing a session, titling it, writing a commit message, classifying a turn, teaching a model how to write a tool call, and more. List them with: veyyon prompt --prompts Then look at one: veyyon prompt --prompt subagent/system-prompt That reports the prompt’s sections and which of them are optional, so you can tell a subagent prompt that rendered three of its five sections because the task had no plan and no worktree from one that lost two sections to a bug. Most of these prompts are a single region with no internal structure, and they report one body section. The subagent prompt has five: role, context, plan, coop, and completion. The list is grouped by the directory each prompt lives in, and the lookup spans all four groups, so veyyon prompt --prompt compaction/summarization-system and veyyon prompt --prompt dialect/gemma work the same way as one from the coding agent’s own tree. A mistyped id is rejected with the nearest registered id quoted back, rather than printing an empty description that would read as a prompt with nothing in it.","breadcrumbs":"Provider stack » Customizing the system prompt » The other prompts","id":"1322","title":"The other prompts"},"1323":{"body":"The sections above are data, not code. section-registry.ts holds two registries, and everything else about a section is derived from its row there. TEMPLATE_SECTIONS describes the static cached-prefix sections assembled from statements. RUNTIME_SECTIONS describes separately emitted sections, and each runtime row states where its text comes from: { id: \\"project\\", source: \\"runtime\\", name: \\"PROJECT\\", input: { kind: \\"computed\\" }, purpose: \\"...\\" } { id: \\"shorthand\\", source: \\"runtime\\", name: \\"SHORTHAND\\", input: { kind: \\"option\\", key: \\"argotPreamble\\" }, purpose: \\"...\\" } computed means buildSystemPrompt produces the text. option means a caller passes it in under the named key, which is the shape a settings-gated preamble takes: the setting is read in sdk.ts, and the option contains the rendered text. Adding one is two edits. First, add the id to RUNTIME_SECTION_IDS and a row to RUNTIME_SECTIONS: { id: \\"house-style\\", source: \\"runtime\\", name: \\"HOUSE STYLE\\", input: { kind: \\"option\\", key: \\"houseStylePreamble\\" }, purpose: \\"the project\'s writing conventions, when the setting is on\\", optional: true,\\n} A row declares the banner’s NAME, never the rendered banner. banner-grammar.ts\\ndefines the = underline for every prompt in the product, so a section cannot ship a\\nwidth of its own: renderBanner writes one, leadingBannerName reads one back, and bannerTable turns a set of rows into the table a splitter is driven by. Anything\\nthat needs to know what a banner looks like reads that module rather than spelling\\nthe rule out again. The row’s own fields ( id, name, purpose, optional) come from PromptSection\\nin packages/utils/src/prompt-registry.ts. TemplateSection and RuntimeSection\\nextend it with the two things only the system prompt needs, source and input, and\\nevery other registry uses it as it is. The grammar and the row shape are separate\\nbecause they answer separate questions: the grammar sets what the bytes look like,\\nthe row states what a section claims about itself. optional states whether the section may be absent, and it is checked rather than\\nbelieved. A settings-gated section is optional: true, because it disappears when\\nits setting is off. Mark one false and it must render from the barest options the\\nbuilder accepts; mark one true and it must be absent until its input is supplied. system-prompt-section-presence.test.ts holds both directions, so the flag cannot\\nbecome a comment that stopped being true. Second, declare that key on BuildSystemPromptOptions in system-prompt.ts: /** The house-style preamble, present when the `houseStyle` setting is on. */\\nhouseStylePreamble?: string; That is the whole change. The assembler reads the registry, so it needs no edit: the section is emitted in registry order, under its own banner, and omitted entirely when the option is absent rather than rendered as a bare heading. Four mistakes are caught rather than shipped. Three are compile errors: Naming an option that is not a field of BuildSystemPromptOptions. The error states the offending key. Declaring the field as something other than a string. Marking the section computed without giving computedText an entry for it. Two more are test failures rather than compile errors, because no type can see them. Declaring an option and never setting it in sdk.ts leaves the section permanently empty; system-prompt-wiring.test.ts fails if a declared option has no production caller. And getting optional wrong in either direction fails system-prompt-section-presence.test.ts. Two things are worth knowing before you edit section-registry.ts. RUNTIME_SECTIONS ends in as const satisfies readonly RuntimeSection[] rather than containing a : readonly RuntimeSection[] annotation. The annotation typechecks and reads better, and it silently disables every check above: it widens input.key to string, so “is this a real option field” starts accepting anything. system-prompt-section-derivation.test.ts fails if the annotation comes back. Position is the row’s position in the array. There is no separate order list to keep in step, and promptSectionOrder permutes template and runtime sections together from the same list, so a new runtime section is reorderable by a harness profile with no extra wiring. A runtime section lands in its own entry of the returned string[], outside block 0. Block 0 is the byte-stable prefix a provider caches, and system-prompt-cached-prefix-stability.test.ts records its digest: adding a runtime section leaves that digest alone, and a change that moves text into the prefix fails there with the section named.","breadcrumbs":"Provider stack » Customizing the system prompt » 10) Adding a section (contributors)","id":"1323","title":"10) Adding a section (contributors)"},"1324":{"body":"Drop the .md under the directory that matches WHEN it fires, add its import and its row to that directory’s rows.ts, and use it through that module. That is the whole procedure, and each step is checked: // packages/coding-agent/src/prompts/turn-control/rows.ts\\nimport turnControlAutoContinue from \\"./auto-continue.md\\" with { type: \\"text\\" }; export const turnControlPrompts = { \\"turn-control/auto-continue\\": { text: turnControlAutoContinue, purpose: \\"continues a turn the model ended without finishing\\", }, // ...\\n} satisfies Record; Read it back from the same module: import { turnControlPrompts } from \\"../prompts/turn-control/rows\\"; const text = turnControlPrompts[\\"turn-control/auto-continue\\"].text; The import is the registration, so there is nothing else to remember. A file with no row is unreachable code rather than a prompt that quietly ships unlisted, and prompt-registry-coverage.test.ts fails if the directory and the rows disagree in either direction. prompts/registry.ts aggregates all twenty-one row modules into PROMPTS, which is still the aggregate every cross-directory consumer takes, and PromptId is still the union of every id. Prefer the row module: it is the reason the rows are split at all. The registry held all 163 .md imports itself, so importing it for one string reached all 163 prompt modules, which cost the file-reading tool 167 modules for its own description. Reach for the aggregate when a module genuinely spans directories, or when the id is not known statically and you need requirePrompt. The satisfies clause is not decoration. An annotation ( : Record) typechecks and widens every key to string, and PromptId then accepts any string: a typo compiles and renders as the empty prompt. Three things that suite will refuse, each because it has happened: Importing a .md outside a registry. Registration would go back to being optional, and the registry back to being an incomplete list that looks authoritative. A relative path into another package’s prompts tree is rejected even for a file that is otherwise fine to read, because it records that package’s layout a second time. Writing a prompts directory down twice. Consumers read dir off the descriptor. Four of them used to type the path themselves and one had gone stale. A row whose purpose states nothing. The purpose is what makes the registry a list a person can read instead of a directory listing with extra steps. If you are adding the first prompt to a package that has none, give it a src/prompts/registry.ts of its own rather than reaching into another package’s. Rows per directory are worth it once a registry is large enough that a consumer of one prompt paying for all of them matters; the other three packages hold their rows in the registry itself. A package defines its prompts; sharing the row SHAPE is what @veyyon/utils is for.","breadcrumbs":"Provider stack » Customizing the system prompt » 11) Adding a prompt (contributors)","id":"1324","title":"11) Adding a prompt (contributors)"},"1325":{"body":"","breadcrumbs":"Provider stack » Customizing the system prompt » 12) Settings that change the prompt","id":"1325","title":"12) Settings that change the prompt"},"1326":{"body":"The outer system-prompt.md scaffold holds no policy, prose, conditions, or banners. Anything that decides what the model should do belongs to a setting and a statement row. Put whole-statement presence conditions in statement-registry.ts. Put wording-level Handlebars variables inside that statement’s Markdown module. The failure this rule exists to prevent is concrete. The delegation section used to carry a\\nliteral category list: “…multi-file changes, refactors, new features, tests, investigations — MUST be\\ndecomposed and delegated.” An audit is an investigation, so the prompt instructed the model to delegate audits, in every\\nsession, whether or not an agent suited to that work existed. That policy was invisible in /settings, unaffected by the Agents table, and only findable by reading the template. It was\\nnot a wording problem: a hardcoded list cannot follow a setting, so it was wrong in every\\nsession that did not happen to match it. The check to apply when writing template text: Kind of text Belongs in the template? Structure: headings, ordering, the shape of a list Yes A fact about this session ( {{cwd}}, the tool names, the concurrency cap) Yes, as a variable A behavior a setting decides No — a {{#if}} on that setting’s gate A behavior nothing decides, stated as a rule the operator cannot see or change No — make it a setting first For delegation this means the template never lists what is delegable. The enabled agents are\\nthe instruction: subagentNames and hasSubagentSpecialists carry the operator’s answer, and\\nthe template reads them. Enabling reviewer is how an operator says reviews are delegable\\nhere, so nothing needs to say it in prose.","breadcrumbs":"Provider stack » Customizing the system prompt » The rule: policy is a setting, not a sentence","id":"1326","title":"The rule: policy is a setting, not a sentence"},"1327":{"body":"Some of the prompt’s text is decided by a setting. The IRC coordination clause appears only\\nwhen the session can still spawn subagents, the delegation section changes wording with subagent.delegation, and the personality block disappears when personality is none. Those settings are listed in one place, packages/coding-agent/src/system-prompt-builder/gate-registry.ts. Each row records the\\nsetting path, the template variables it decides, one line on what the model sees change, and\\nwhether flipping it reaches a running session.","breadcrumbs":"Provider stack » Customizing the system prompt » The gates","id":"1327","title":"The gates"},"1328":{"body":"A live gate takes effect when you change it. The settings UI rebuilds the system prompt\\nfrom the registry, so the model sees the new text on its next request. These are live today: Setting What changes in the prompt personality the personality block, or nothing when set to none tui.renderMermaid whether the model is told Mermaid fences render as terminal diagrams subagent.enabled the whole Delegation section, which is absent when subagents are off subagent.delegation whether the section requests delegation, and whether it uses MUST/ONLY wording subagent.batch which call shape the delegation guidance teaches subagent.maxConcurrency the concurrency limit quoted in that guidance subagent.maxNestedSpawnDepth the IRC coordination clause, present only when this session can spawn subagent.agents which specialists delegation prose names includeModelInPrompt whether the active model is surfaced in the workstation block tools.format whether tools are described inline or left to the provider’s tool list inlineToolDescriptors whether descriptors live in the prompt or provider schemas for the active model tools.intentTracing whether the prompt explains the intent field, and whether tool schemas carry it tools.intentTracing and inlineToolDescriptors also decide provider schema shape. When intent\\ntracing is on, every tool schema sent to the model contains an extra intent field and the prompt\\nexplains it. Descriptor placement sends full descriptions in exactly one place. In auto mode,\\nGemini receives them inline while other native tool-calling models receive them in their schemas.\\nThe agent resolves both settings on every request, so a model switch rebuilds the prompt and updates\\nthe schemas together. A frozen gate is read once at session start, so changing it mid-session saves the new value and\\nleaves the prompt as it was. The settings screen reports when a change applies on the next session.\\nOne gate remains frozen: Setting Why includeWorkspaceTree read into a session constant before the prompt builder is defined","breadcrumbs":"Provider stack » Customizing the system prompt » Live and frozen gates","id":"1328","title":"Live and frozen gates"},"1329":{"body":"buildSystemPrompt takes every gate as an optional argument, so a caller can omit all of them.\\nThat is what the SDK does when it builds a prompt outside a session, and what tests do. The\\nfallbacks live in one table, OMITTED_GATE_DEFAULTS in packages/coding-agent/src/system-prompt-builder/gate-inputs.ts, and the builder reads them from\\nthere rather than repeating a value next to each argument. An omitted gate means the caller has no configuration to offer, so the gate renders off or empty.\\nThat is not the same as a default session, and on four gates it is deliberately different: Gate Omitted A default session eagerTasks false, no delegation ask true, because subagent.delegation ships as preferred taskIrcEnabled false, no coordination clause true, because the recursion limit allows spawning subagentNames [], prose lists no specialist the agents this session can spawn taskMaxConcurrency 0, quote no cap 32, the shipped limit You want the resolved values, not the fallbacks, whenever you are showing or benchmarking a real\\nconfiguration. Call resolveGateInputs(settings, { tools, model }) and spread the result, which is\\nwhat both sdk.ts and veyyon prompt do. Building by omission is how veyyon prompt once printed\\na prompt with no delegation guidance for a session that had it. prompt-gate-inputs.test.ts renders the prompt both ways and asserts the four differences above by\\nvalue, so a fallback that starts disagreeing with its setting for no stated reason fails there.","breadcrumbs":"Provider stack » Customizing the system prompt » What a gate is worth when nobody says","id":"1329","title":"What a gate is worth when nobody says"},"133":{"body":"For worked examples, see the Task guides. For the full command and setting reference, see Reference.","breadcrumbs":"Features » Recipes","id":"133","title":"Recipes"},"1330":{"body":"When a setting controls whether a whole statement is present, add a row to PROMPT_GATES and use that variable in the statement row’s condition. When the setting changes wording inside one statement, keep the Handlebars conditional in that statement’s Markdown file. Never add a gate to the outer system-prompt.md scaffold. You declare the gate’s builder input in one place. GateInputs in system-prompt-builder/gate-inputs.ts holds the field and its doc comment, and BuildSystemPromptOptions extends Partial, so a field you add there is a builder\\noption immediately. Do not restate the default in the doc comment. OMITTED_GATE_DEFAULTS defines what\\nan omitted option means. Then pass the variable in the statement context in system-prompt.ts. This step has no type that\\ncan prove the runtime value was supplied, because statement templates read context by name. A gate\\nyou omit renders as off. prompt-gate-registry.test.ts and the statement gate matrix check that\\ndeclared variables reach observable statement output. prompt-gate-registry.test.ts rejects five things, each because it has happened: An unclassified gate. Every {{#if}} variable in the template must be either a\\nregistered settings gate or listed as fed by something else. A new one fails until you\\ndecide which it is. A setting path the schema does not define. Rows carry paths as strings, so a typo would\\nproduce a gate that never fires and reads like a working row. A per-setting rebuild call in the controller. That hand-written list carried two of the\\nnine gates, which is how seven settings came to change the configuration and leave the\\nprompt describing the previous one. A registered gate the context never passes. The suite builds a real prompt and reads the statementContext it rendered with, so a variable that is missing, undefined, or shadowed by a\\nlater spread fails there rather than rendering as off. A context value pinned to a constant. Present but fixed is the same bug with a key in place, so\\nthe suite also asserts the value follows what the caller requested. A row marked frozen-by-placement also has its claim checked against sdk.ts: the setting\\nreally is read above the prompt builder. Move that read inside and the test fails, which is\\nthe reminder to reclassify the gate rather than leave a stale label on one that now works. That is how tools.intentTracing stopped being frozen. Its row said what would have to change,\\nin the row itself: not only moving the read, but making the tool-schema injection follow the\\nsetting as well. Both happened, so the row now reads live, and a separate suite in packages/agent proves the schema half by flipping the resolver between two requests to the same\\nagent. The prompt suite alone could not prove it, because it passes just as well on a build where\\nthe schemas never change.","breadcrumbs":"Provider stack » Customizing the system prompt » Adding a gate","id":"1330","title":"Adding a gate"},"1331":{"body":"The system prompt is a list of statements. A statement is a fragment of prompt text with an id,\\na condition, and a purpose. Its text lives in src/system-prompt-builder/statements//.md, and the row that registers it lives in statement-registry.ts.","breadcrumbs":"Provider stack » Customizing the system prompt » 13) Statements: the prompt is a list, not a document","id":"1331","title":"13) Statements: the prompt is a list, not a document"},"1332":{"body":"Sections were already rows, so a section is addressable, orderable and overridable. The\\nconditions inside a section were {{#if}} blocks buried in prose, so they were none of those\\nthings. Two consequences you can see in the tests: the gate suite had to run a regular expression\\nover system-prompt.md to find out what the prompt gates on, and the end-to-end suite could only\\nassert that two 76KB strings differed, because a single gated line had no name to assert on. A statement has a name. That is what lets you refine one point of the prompt, assert that it\\nappears under the right conditions, measure what it costs in tokens, and ablate it in an eval.","breadcrumbs":"Provider stack » Customizing the system prompt » Why","id":"1332","title":"Why"},"1333":{"body":"One rule sets it: A statement is the smallest unit that can independently be present, absent, or different across\\nsessions. If you cannot name a condition or configuration under which it would change, it is not\\na statement, it is part of one. So the ROLE section’s fourteen lines are two statements, not fourteen. The role sentence and the\\nfive engineering principles are always present together, so they are one statement. The Mermaid\\nbullet is a second, because renderMermaid removes it. The rule has one addition, and the last two sections are why. A unit the prompt itself delimits\\nmay be its own statement even when nothing varies it. DELIVERY CONTRACT is five unconditional XML\\nblocks ( , , , , ) and\\nEXECUTION WORKFLOW is six numbered steps under markdown headings. The rule as stated would merge each\\nset into a single row. It should not, because those boundaries are declared by the document rather\\nthan invented by the registry, and an eval that ablates one contract block or one workflow step needs\\neach to have a name. So the check is: two adjacent always rows are a merge to make unless the second one opens a unit the\\ndocument declares, meaning its text starts with a markdown heading or an XML tag. Two adjacent always rows of plain prose are still reported, which is the case the rule was written for.","breadcrumbs":"Provider stack » Customizing the system prompt » How fine is a statement","id":"1333","title":"How fine is a statement"},"1334":{"body":"A row contains one of six conditions: Condition Meaning Template shape it replaces always in every prompt plain text when a variable is truthy {{#if x}} whenContains a collection holds a member, such as a tool being active {{#has tools \\"task\\"}} whenAll every nested condition holds nested {{#if}} blocks whenAny any nested condition holds {{#ifAny a b}} not the nested condition does not hold a block-level {{else}} arm whenAll and whenAny hold conditions rather than variable names, so they nest. The condition\\nalgebra can therefore say “A and not B”. For example, the descriptor statement uses allOf(when(\\"hasTools\\"), not(when(\\"toolListMode\\"))). Write conditions with the builders\\n( when, contains, allOf, anyOf, not) rather than object literals; they construct exactly the\\nsame values and the rows stay readable. The variable a condition names has to be either a registered settings gate ( gate-registry.ts) or a\\nrow in SESSION_FACT_VARIABLES. A typo, or a variable the builder renamed, would otherwise produce a\\nstatement that never appears and reports nothing, so statement-registry.test.ts rejects it.","breadcrumbs":"Provider stack » Customizing the system prompt » Conditions","id":"1334","title":"Conditions"},"1335":{"body":"Only block-level conditions become separate statements. Wording-level conditionality stays\\ninside the relevant statement module: - {{#if label}}{{label}}: `{{name}}`{{else}}`{{name}}`{{/if}} That is one bullet inside an {{#each}}, not two statements. Splitting it would shatter a sentence\\ninto fragments and make the registry finer than behavior requires. The division is: the statement registry sets whether a statement is present, Handlebars inside the statement module sets the statement text. This is why statement Markdown can still contain {{#each skills}}, {{toolRefs.task}}, and {{#list globs join=\\", \\"}}. The outer system-prompt.md scaffold contains none of them.","breadcrumbs":"Provider stack » Customizing the system prompt » What stays in Handlebars","id":"1335","title":"What stays in Handlebars"},"1336":{"body":"A statement file never contains a section banner. assembleSection renders the banner from the\\nsection registry at the width banner-grammar.ts owns. A PROMPT_SECTIONS/.md replacement also\\ncontains body text only. The same assembler adds its registry banner, so shipped statements and\\noperator replacements cannot disagree about a section boundary.","breadcrumbs":"Provider stack » Customizing the system prompt » The registry owns section structure","id":"1336","title":"The registry owns section structure"},"1337":{"body":"assembleSection returns Handlebars template text, not rendered text. assembleStatementSections creates the complete static section map. Operator overrides apply to\\nthat map, and assembleDefaultTemplate fills the outer scaffold: const statementSections = assembleStatementSections(data, statementOverrides);\\nconst sectionOverrides = applySectionOverrides(files, statementSections);\\nassembleDefaultTemplate({ ...statementSections, ...sectionOverrides }); The scaffold is: {{templateSections}} The complete document is rendered once, so formatting and variable expansion are global rather\\nthan changing with statement boundaries. assembleDefaultTemplate defines the one newline between\\nadjacent static sections. Statement modules own only their own final line. Operator section overrides win because they are spread after the shipped statement map. Append mode\\nstarts from the complete statement-assembled section, then adds your body inside that region. There\\nis no prose-bearing template fallback.","breadcrumbs":"Provider stack » Customizing the system prompt » How statements reach the model","id":"1337","title":"How statements reach the model"},"1338":{"body":"The test suites enforce these contracts directly: system-prompt.md contains exactly the {{templateSections}} variable and no literal prose,\\ncondition, or banner. Every static section declared by section-registry.ts owns at least one statement. A missing\\nsection fails module loading. The registry supplies section order and banner bytes. Replacement files are body-only. A legacy file containing its own banner fails loudly. The gate matrix renders every statement condition through the modular assembly. Production buildSystemPrompt output proves the statement modules, operator precedence, and\\nsection ordering reach the model. There is no frozen prose copy and no migration byte-parity fixture. Prompt behavior is tested from\\nthe one modular source.","breadcrumbs":"Provider stack » Customizing the system prompt » Structural invariants","id":"1338","title":"Structural invariants"},"1339":{"body":"Two things follow from a rule having a name, and both are the reason the migration was worth doing. veyyon prompt --statements prints what each rule costs. The number is MARGINAL: what the prompt\\nwould be shorter by without that rule, not the length of the rule’s text. The distinction matters\\nbecause render ends in a format pass that normalizes whitespace across statement boundaries, so\\nthe lengths of the statement texts do not add up to the length of the section they form. Measured the\\nother way, the parts reconcile with the whole exactly: section bytes = banner + sum of statement bytes + separator The banner belongs to the section registry, and assembleDefaultTemplate defines the one newline\\nbetween adjacent static sections. prompt-inspect.test.ts asserts that the reported parts reconcile,\\nso a change to either convention cannot silently corrupt the cost breakdown. veyyon prompt --statement prints one rule’s rendered text. The text it prints weighs exactly\\nwhat the table charges the rule, which is asserted, so the two surfaces cannot disagree about the same\\nrule. A rule that is not in this prompt reports the condition that would include it and exits 0,\\nbecause a rule being off is a configuration rather than a failure. VEYYON_EVAL_SYSTEM_PROMPT_STATEMENTS changes one rule. It is a JSON object of statement id to\\nreplacement text, or to null to remove the rule entirely: VEYYON_EVAL_SYSTEM_PROMPT_STATEMENTS=\'{\\"tool-policy/delegation-gates\\": null}\' Same instrument as VEYYON_EVAL_SYSTEM_PROMPT_SECTIONS, one level finer, and deliberately the same\\nshape: environment variable only, no config key, no CLI flag. A config-reachable prompt override could\\nsilently contaminate a production run, and a contaminated eval reports a number that looks valid. null and \\"\\" are different operations, so pick deliberately. null ablates: the row and the\\nseparation it contains both leave the prompt, because a statement’s text includes its own separation. \\"\\" keeps the row present and empty, so the separation stays and only the words go. Use the first to\\nask whether a rule is worth having, the second to ask whether it needs saying at all. Every way an override could do nothing is an error rather than a no-op: an unknown statement id, a\\nvalue that is neither a string nor null, malformed JSON. An arm that quietly did nothing would\\nreport the shipped prompt’s score as the arm’s score, which is a false result with no signal that\\nanything went wrong. An override targeting a rule whose condition is false is rejected. A statement override also cannot\\ntarget a section replaced wholesale by a section override, because the section replacement would\\nsilently discard the statement arm. These conflicts fail before the prompt is assembled. All six static sections come from statements. STATEMENT_SECTIONS is derived from the sections the\\nregistry declares, and the module will not load if any one has no statements. The zero-prose system-prompt.md scaffold cannot supply fallback instructions, so losing a section is a loud\\nassembly failure rather than a silent reversion.","breadcrumbs":"Provider stack » Customizing the system prompt » What each rule costs, and testing one of them","id":"1339","title":"What each rule costs, and testing one of them"},"134":{"body":"Editing reliably is the core of a coding agent, so it is worth understanding how Veyyon does it. The default edit surface is hashline. In practice that means three things work together: numbered lines that come back from read and grep, snapshot tags that identify a known state of a file, and the edit tool with its SWAP, DEL, and INS operations. For the design behind the edit and repair path, see The hashline edit engine.","breadcrumbs":"Editing and repair » Editing and repair","id":"134","title":"Editing and repair"},"1340":{"body":"Four tools give the agent controlled access to your files: read, glob, grep, and write. They are always available; there is no experimental_tools or backends.toml gate\\nto turn them on. The point of these tools is bounds. An unbounded cat, find, or grep -r in the shell\\ncan dump enough text to fill the whole context window. These tools apply line, byte, and\\nresult caps instead, and they surface truncation rather than dropping output silently. This\\npage documents each tool’s parameters and the limits it enforces. The implementations live\\nunder packages/coding-agent/src/tools/{read,glob,grep,write}.ts.","breadcrumbs":"Context » Bounded reads and search","id":"1340","title":"Bounded reads and search"},"1341":{"body":"read takes a single path string (no separate offset/ limit arguments) and bounds every read\\nto a budget: One parameter, inline selectors. read {path}, where path can carry a line-range selector\\nappended after a colon: src/foo.ts:50-200 (inclusive range), src/foo.ts:50 / :50- (from line 50\\non), src/foo.ts:50+150 (150 lines from line 50), or src/foo.ts:5-16,960-973 (multiple ranges in\\none call). :raw reads verbatim with no anchors or line prefixes. Dual budget, whichever is hit first: a line cap ( DEFAULT_MAX_LINES = 3000) and a byte cap\\n( DEFAULT_MAX_BYTES, 50 KB). A file that is short in lines but huge in bytes (minified JS, a data\\nblob) is bounded by bytes; a file with many short lines is bounded by lines. Both are compiled, not\\nconfigured, and read is deliberately the one tool exempt from artifact spilling: it is bounded by\\nLINES, so spilling on bytes would hand back fewer lines than you asked for and break the contract\\nthe tool has. Every other tool’s output is bounded by tools.artifactSpillThreshold, described\\nunder The grep tool below. Structural summaries for parseable code. A read with no selector on a parseable source file\\nreturns declarations with bodies elided ( …), and the footer states the recovery selector so the model\\nre-issues only the ranges it actually needs instead of re-reading the whole file. Truncation is explicit. A summary footer or a [Showing lines …]-style notice states the\\ncontinuation selector. Beyond plain text files: the same tool also reads directories (depth-limited listing), archives\\n( .tar, .tar.gz, .zip, via archive.zip:path/inside), SQLite databases ( file.db:table, with\\npagination and where/ order filters), PDF/Word/PowerPoint/Excel/EPUB (extracted text), Jupyter\\nnotebooks (editable cell text), images, URLs (reader-mode by default), and internal URI schemes\\n( memory://, skill://, artifact://, mcp://, ssh://, and others). Text reading is intentionally separate from image inspection. By default, read decodes image\\nfiles (PNG, JPEG, GIF, WEBP) inline for direct visual analysis. When inspect_image.enabled is set, read returns image metadata instead and the model inspects the image by calling inspect_image\\nwith a question.","breadcrumbs":"Context » The read tool ( tools/read.ts)","id":"1341","title":"The read tool ( tools/read.ts)"},"1342":{"body":"There is no separate find or ls tool, pattern matching and directory listing are both the glob\\ntool. A model that runs find . -name \'*.rs\' or ls -R in the shell gets back an unbounded dump that\\nincludes target/, node_modules/, and .git/; glob is bounded and gitignore-aware instead: Glob matching, or a bare directory/file path. glob {path?, hidden?, gitignore?, limit?}. path\\naccepts a glob, a single file, a directory (recursed), or a semicolon-delimited list of any of those\\n( src/**/*.ts; test/**/*.ts); omitted, it searches the workspace root. gitignore (default true) hides .gitignore matches; set false to find .env*, build\\noutput, or anything the repo ignores. hidden (default true) includes dotfiles. Bounded by result count, default and max 200 ( DEFAULT_LIMIT / MAX_LIMIT in glob.ts): not\\na byte cap. Every truncation is surfaced as an actionable notice. Sorted by mtime, newest first (not lexicographic), grouped under # / headers with\\nbasenames below; directories get a trailing /. .git and node_modules are never descended. Traversal uses the same filesystem abstraction\\nas read/ grep (including remote/task workspaces when those backends are active).","breadcrumbs":"Context » The glob tool ( tools/glob.ts)","id":"1342","title":"The glob tool ( tools/glob.ts)"},"1343":{"body":"A model that runs grep -r / rg in the shell can get back tens of thousands of matching lines. The grep tool is always regex (Rust regex / PCRE2 syntax; no literal-match flag) and paginates by file\\ncount on top of the same gitignore-aware traversal glob uses: grep {pattern, path?, case?, gitignore?, skip?}. path scopes the search (single path,\\nsemicolon-delimited list, or a file:line-range selector on one target); case toggles\\ncase-sensitivity and defaults to sensitive ( caseSensitive ?? true in grep.ts); skip pages\\npast files already returned once a call hits the file limit. Bounded by file count, not match count. Results are paginated at DEFAULT_FILE_LIMIT = 20 files\\nper call, with an internal total cap of 2000 matches ( grep.ts); skip continues from where the\\nprevious call left off. Output is per-file, line-number-prefixed, with context rows around each match when the harness\\nruns in line-number mode. Cross-line patterns are detected from a literal \\\\n/ \\\\\\\\n in pattern. The tool description explicitly forbids shelling out to grep/ rg/ ripgrep/ ag/ ack/ git grep\\nvia Bash, the built-in tool is the only sanctioned path.","breadcrumbs":"Context » The grep tool ( tools/grep.ts)","id":"1343","title":"The grep tool ( tools/grep.ts)"},"1344":{"body":"read/ glob/ grep are the read side; write {path, content} creates or replaces a whole file. It\\nshares infrastructure with the edit engine rather than touching the filesystem directly: Shared verified pipeline. write.ts imports the same file-snapshot store and LF-normalization\\nhelpers as the edit path ( ../edit/file-snapshot-store, ../edit/normalize) and formats hashline\\nheaders via @veyyon/hashline, so writes inherit LSP diagnostics writethrough and diff/verification\\nbehavior rather than bypassing it. Exclusive concurrency. The tool declares concurrency: \\"exclusive\\", so nothing else can create or\\nchange the target file mid-call. Steers to edit for surgery. The tool description tells the model to prefer edit for a\\nsurgical change to an existing file, keeping write from becoming a “re-emit the whole file” habit\\nthat burns tokens.","breadcrumbs":"Context » The write tool ( tools/write.ts)","id":"1344","title":"The write tool ( tools/write.ts)"},"1345":{"body":"Bash/exec tool output is sanitized before it reaches the model, via sanitizeText()\\n( packages/utils/src/sanitize-text.ts), used from session/streaming-output.ts and the interactive PTY\\ncapture path ( tools/bash-interactive.ts): ANSI stripping is Bun-native, not a hand-rolled parser. sanitizeText() calls Bun’s built-in Bun.stripANSI() when an ESC byte is present, then strips C0/C1 control bytes and DEL with a single\\nregex pass. The function is a TypeScript replacement for a former Rust native\\n( crates/veyyon-natives/src/text.rs::sanitize_text, noted in the current source comment), there is no\\nlive Rust ECMA-48 grammar walker in this path today. Keep \\\\n and \\\\t, drop the rest. The control regex covers C0 (excluding tab/newline), \\\\r,\\nDEL, and the C1 range; \\\\n and \\\\t are the two explicit exclusions. Model-facing only. Sanitizing happens on the text that becomes tool output for the model. The TUI\\nrenders exec output from its own delta stream and keeps its colors, so the operator’s view is\\nuntouched. Zero-cost when clean. Well-formed input with no control/ANSI bytes returns the original string\\nreference after one regex probe; only output that actually carries escapes pays for Bun.stripANSI().","breadcrumbs":"Context » Sanitizing exec output for the model","id":"1345","title":"Sanitizing exec output for the model"},"1346":{"body":"A read that bounds and a search that bounds its output are both about keeping the working context small and relevant. Long trajectories degrade when context fills with raw file dumps; these tools plus compaction & project memory are how a long task stays coherent.","breadcrumbs":"Context » Why these are grouped with context","id":"1346","title":"Why these are grouped with context"},"1347":{"body":"Context files are Markdown instruction files that veyyon discovers automatically before a session starts and injects into the agent’s project context. Use them for repository conventions, architecture notes, test and review expectations, and instructions that should travel with a user account or a project. Matching files ( AGENTS.md, CLAUDE.md, GEMINI.md, and related) are discovered and injected into the opening session context when discovery is enabled.","breadcrumbs":"Context » Context files » Context files","id":"1347","title":"Context files"},"1348":{"body":"Four similarly named things behave differently. Keep them straight: Context files are read as plain Markdown and shown to the agent inside a block. They are advisory background that stays in the session’s opening context. Sticky rules come from a top-level RULES.md. They are converted into an always-apply rule that is re-attached near the current turn, so they keep their hold even after the visible conversation grows. See “Sticky rules vs normal context” below. Discovery providers are the config-source adapters ( native, claude, codex, gemini, opencode, github, agents, agents-md) that record where each tool keeps its files. The same provider that contributes context files may also contribute MCP servers, slash commands, skills, hooks, tools, prompts, and settings. Model providers are inference backends such as anthropic, openai, google, groq, ollama, and openrouter. They have nothing to do with context files except that both kinds of id share the one disabledProviders list: see “Disabling discovery providers” below and Providers. Authoring skills and rule files (as opposed to the sticky RULES.md) is covered in Skills. Use AGENTS.md for additive instructions, PROMPT_SECTIONS/ for persistent section changes, and the two CLI flags for one-run prompt replacement or appending. See System prompt customization.","breadcrumbs":"Context » Context files » How context files relate to other concepts","id":"1348","title":"How context files relate to other concepts"},"1349":{"body":"The native provider is the recommended format for new projects. It reads from your user agent directory and from .veyyon/ directories inside a project, and it has the highest discovery priority, so its files win over every other convention at the same scope. File Scope Behavior ~/.veyyon/AGENTS.md Global User Global cross-profile context for every session across all profiles. ~/.veyyon/profiles//... Profile User Active profile context. Scanned in descending priority order (first match wins; exactly 1 file loaded per profile): 1. ~/.veyyon/profiles//agent/AGENTS.md (Highest) 2. ~/.veyyon/profiles//AGENTS.md 3. ~/.veyyon/profiles//agent/agent.md 4. ~/.veyyon/profiles//agent.md (Lowest) /.veyyon/AGENTS.md Project Project context. veyyon walks upward from the current directory to the repository root and every ancestor contributes at most one file. The nearest non-empty .veyyon/ directory supplies that ancestor’s file from its AGENTS.md; other ancestors fall back to a bare AGENTS.md, then a bare CLAUDE.md. See Load order and shadowing for the full per-directory order. ~/.veyyon/profiles//agent/RULES.md User User-level sticky rule content. Loaded as an always-apply rule, not as a context file. Two details matter: Walk-up to the repository root. Discovery starts in the current working directory and climbs through each ancestor up to the repository root. The nearest non-empty .veyyon/ directory claims its own level with its AGENTS.md; every other level contributes a bare AGENTS.md, falling back to a bare CLAUDE.md when no AGENTS.md has content there. The .veyyon/ directory must be non-empty. An empty .veyyon/ directory is skipped during the walk-up, so the search continues to the next ancestor. An empty AGENTS.md file contributes nothing and shadows nothing. ~/.veyyon/profiles/default/agent is the user base, and it is profile-aware: under a named profile ( --profile / VEYYON_PROFILE) the base becomes ~/.veyyon/profiles//agent, so each profile contains its own AGENTS.md and RULES.md. Non-native user files ( ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, …) are profile-independent and still discovered under every profile. If VEYYON_CODING_AGENT_DIR is set under the default profile, it relocates the base outright, so the user files become $VEYYON_CODING_AGENT_DIR/AGENTS.md and $VEYYON_CODING_AGENT_DIR/RULES.md; under a named profile the override is ignored.","breadcrumbs":"Context » Context files » Native .veyyon files","id":"1349","title":"Native .veyyon files"},"135":{"body":"Models often emit slightly wrong tool JSON, or line anchors that have gone stale. Hashline catches a stale [path#TAG] tag and returns recovery hints instead of writing the wrong bytes. On top of that, general schema repair runs on every tool call before validation. See Repair overview.","breadcrumbs":"Editing and repair » Failure modes","id":"135","title":"Failure modes"},"1350":{"body":"repo/ .veyyon/ AGENTS.md packages/api/ .veyyon/ AGENTS.md Starting a session in repo/packages/api: The .veyyon/ context file is repo/packages/api/.veyyon/AGENTS.md (the nearest non-empty .veyyon/ directory). repo/.veyyon/AGENTS.md is not also included, though a bare repo/AGENTS.md beside it would be, at its own depth. Put broad, durable project background in AGENTS.md. Reserve RULES.md for short, hard requirements that must stay visible across long conversations; it is a user-level file, so a repository cannot ship one.","breadcrumbs":"Context » Context files » Monorepo example","id":"1350","title":"Monorepo example"},"1351":{"body":"veyyon also discovers the context and rule files of other agent tools so existing projects keep working without migration. Provider id Convention path Scope Notes native .veyyon/AGENTS.md User + project Recommended veyyon format. User file at ~/.veyyon/profiles//agent/AGENTS.md; project files are one per ancestor directory from the repo root down to the cwd, resolved per directory as described in Load order and shadowing. claude .claude/CLAUDE.md User + project User file ~/.claude/CLAUDE.md; project file /.claude/CLAUDE.md only (no ancestor walk-up). codex .codex/AGENTS.md User User file ~/.codex/AGENTS.md only. Project-level standalone AGENTS.md files load through the native provider’s ancestor walk-up, not from /.codex/AGENTS.md. gemini .gemini/GEMINI.md User User file ~/.gemini/GEMINI.md only. opencode .config/opencode/AGENTS.md User User file ~/.config/opencode/AGENTS.md only. github .github/copilot-instructions.md User User-global ~/.copilot/copilot-instructions.md (relocate with COPILOT_HOME) and an AGENTS.md from each COPILOT_CUSTOM_INSTRUCTIONS_DIRS entry. A repository’s own .github/copilot-instructions.md is not read. agents .agent/AGENTS.md, .agents/AGENTS.md User User files from ~/.agent/ and ~/.agents/ only; there is no project scope. agents-md AGENTS.md Project Standalone (non-config-directory) AGENTS.md files, discovered by walking up from the current directory to the repository root (or home when no repo root is known). Files whose parent directory name starts with . are ignored, those belong to a config-directory provider instead. github /.github/instructions/**/*.instructions.md User rules GitHub Copilot / VS Code instruction files under each COPILOT_CUSTOM_INSTRUCTIONS_DIRS entry become rules. applyTo: \'*\' or applyTo: \'**\' is injected as always-apply context; other applyTo globs are listed in the rulebook with description and are readable as rule://. A repository’s own .github/instructions/ is not read. Providers marked “(no ancestor walk-up)” only look in the current working directory’s config directory. If you need ancestor walk-up behavior, prefer the native .veyyon/AGENTS.md format or a standalone AGENTS.md (the agents-md provider), or launch veyyon from the directory that holds the config directory.","breadcrumbs":"Context » Context files » Other supported context conventions","id":"1351","title":"Other supported context conventions"},"1352":{"body":"When two providers describe the same scope, the higher-priority provider wins. Provider priorities: Priority Provider id 100 native 80 claude 70 agents, codex 60 gemini 55 opencode 30 github 10 agents-md Discovered files are then deduplicated by scope: One user context file is kept across all providers. Because native has the highest priority, ~/.veyyon/profiles//agent/AGENTS.md shadows every other user-level context file. One project context file per directory depth. Depth is measured from the current directory: the cwd is depth 0, its parent depth 1, and so on. Config subdirectories of an ancestor ( .claude/, .github/, .gemini/, …) count as the same depth as that ancestor. Within one directory, native picks a single file before any shadowing happens. The order is .veyyon/AGENTS.md (only from the nearest non-empty .veyyon/ directory), then a bare AGENTS.md, then a bare CLAUDE.md. The first one that has content wins and the rest of that directory’s candidates are never read, so a CLAUDE.md beside an AGENTS.md is not loaded, not appended, and not deduplicated later. CLAUDE.md is last because AGENTS.md is the tool-neutral convention: a project containing both is nearly always stating the same rules twice, and a stale CLAUDE.md must not contradict a maintained AGENTS.md. A candidate that is empty or unreadable contributes nothing and therefore shadows nothing, so the next one down gets its turn. The pick is per directory, not per project. A repo root with only AGENTS.md and a package directory with only CLAUDE.md both load, each at its own depth. At the same depth, the higher-priority provider shadows the rest. Across depths, multiple files survive. In a monorepo, an ancestor AGENTS.md and a package-level one are different depths and both load. Contained files are collapsed. If one surviving file’s whole content already appears inside another’s, only one copy is kept, and the copy that survives is the one from the more authoritative scope (see below). Two files from the same scope fall back to position, so between a repo-root file and a package file with identical text the package one is kept. After deduplication, project files are sorted so farther ancestors appear first and files closer to the cwd appear last. Both are project scope, so this is one project directory refining another, not a project file outranking a broader scope.","breadcrumbs":"Context » Context files » Load order and shadowing","id":"1352","title":"Load order and shadowing"},"1353":{"body":"Provider priority and depth decide which files survive. A separate axis sets where each survivor is rendered, and therefore which one wins an outright conflict. These are two different orders and it is easy to read one as the other: Resolution order is the order the three scopes are read: global, then profile, then project. Authority order is the order they are rendered, least authoritative first: the project group (farther ancestors first, closest to the cwd last), then the profile file, then the cross-profile global ~/.veyyon/AGENTS.md last of all. Your live instruction in the conversation beats all of them. Below that, the ladder runs broadest to narrowest: your own ~/.veyyon/AGENTS.md, then the active profile’s file, then the project’s files lowest. A narrower file may add detail the broader ones do not cover, and the agent follows it there, but it may not contradict, loosen, or forbid what a broader file allows. That direction is a safety boundary, not a style choice. A project file is content checked into a repository you may not have written, so letting one outrank your own configuration would let any repository you clone rewrite the rules you set for yourself. Within the project group the file closest to your working directory is still the most specific one, because both files are project scope and neither outranks the other on the ladder.","breadcrumbs":"Context » Context files » Scope authority: your own configuration is last and wins","id":"1353","title":"Scope authority: your own configuration is last and wins"},"1354":{"body":"repo/ AGENTS.md packages/api/ AGENTS.md .claude/CLAUDE.md Starting in repo/packages/api: Both bare AGENTS.md files load through native (priority 100): repo/AGENTS.md at depth 2 and repo/packages/api/AGENTS.md at depth 0. repo/packages/api/.claude/CLAUDE.md ( claude, priority 80) also resolves to depth 0 and is shadowed there by the higher-priority native file. The kept files are ordered root-first, package-last, so packages/api’s file is the more specific one within the project group. If you add repo/packages/api/.veyyon/AGENTS.md, it is the nearest non-empty .veyyon/AGENTS.md and loads as the project context file at its depth; repo/.veyyon/AGENTS.md is not also included.","breadcrumbs":"Context » Context files » Worked shadowing example","id":"1354","title":"Worked shadowing example"},"1355":{"body":"Discovered context files are injected into the opening project prompt as a single block, one element per surviving file, least authoritative first, so the project files come before the profile file and the global file comes last: The user\'s instructions in this conversation have ABSOLUTE authority. ... \\nThe user-authored context files below rank from BROADEST to NARROWEST, and a narrower file NEVER overrides a broader one: 1. The user\'s OWN configuration, from their home config directory. ...\\n2. The active profile\'s configuration.\\n3. The PROJECT\'s files, from the repository you are working in. LOWEST authority of the three.\\n...\\n\\n...root content...\\n \\n\\n...package content...\\n \\n\\n...your own standing rules...\\n Precedence again, because you have just read these files in ascending order of authority and the\\none you read FIRST is the narrowest, not the strongest: ...\\n The agent sees each file’s absolute path and its fully expanded Markdown content (with @ imports already resolved, see below). When discovery is enabled, matching context files are injected at session start. A sentence stating that your live instruction in the conversation has absolute authority renders in every session, whether or not any context file loaded, because a rule or a memory can tell the agent to reject just as a file can. The scope ladder above renders only when at least one context file loaded, since there is nothing to rank otherwise. Below your live instruction, the surviving context files win over conflicting generic Veyyon workflow defaults, retrieved material, and historical summaries; among themselves they rank by the scope ladder, and a project file never overrides your own configuration. Deeper-directory AGENTS.md files that were not auto-loaded (for example, ones below the current directory) are surfaced separately in a block that lists their paths and tells the agent to read them before editing those directories. Those files are pointers, not full injected content.","breadcrumbs":"Context » Context files » Injection behavior","id":"1355","title":"Injection behavior"},"1356":{"body":"Inside any context file, an @path token expands inline to the referenced file’s content before injection: # Project notes Read @docs/architecture.md before changing storage code.\\nShared release steps live in @../RELEASE.md and personal aliases in @~/.notes/aliases.md. The exact rules: Relative paths resolve from the importing file’s own directory, not the session’s working directory. ~/ and ~ resolve from the user’s home directory; absolute paths are used as-is. Tokens inside fenced code blocks and inline code spans are left untouched: useful when you want to write about an @token without expanding it. git@github.com:org/repo.git and user@example.com-style tokens are not treated as imports. A token only counts when the @ sits at the start of a line or after a space or tab. Trailing sentence punctuation is trimmed off the path ( . , ; : ! ? ) ] } \\" \'), so @notes/setup.md. imports notes/setup.md. Imports recurse up to five hops. An imported file may itself contain @ imports, up to a total depth of five. Cycles are skipped. A file already pulled into the current expansion tree is not re-expanded, so mutual imports terminate cleanly. A missing or unreadable target leaves the original @token text in place rather than erroring.","breadcrumbs":"Context » Context files » @ imports","id":"1356","title":"@ imports"},"1357":{"body":"Use a normal context file ( AGENTS.md, CLAUDE.md, .claude/CLAUDE.md, …) for the bulk of your guidance: repository overview, code style, build and test commands, review expectations, and local conventions. These load into the opening block. Use a top-level RULES.md for the handful of hard requirements that must stay active even after a long conversation has pushed the opening context far up the transcript: # ~/.veyyon/profiles//agent/RULES.md Never commit or push unless the user explicitly asks.\\nDo not edit generated files. RULES.md is special: It is read only at the user location ~/.veyyon/profiles//agent/RULES.md. A RULES.md anywhere else, including inside a repository, is not a context-file convention and is ignored. It is loaded as an always-apply rule, not as a context file, so it is re-attached near the current turn and keeps its hold across long sessions. It is always sticky: frontmatter cannot make it non-sticky. If you want conditional or opt-in behavior, write a normal rule file instead (see Skills). Keep RULES.md short. Long background belongs in AGENTS.md, where it costs context budget only once.","breadcrumbs":"Context » Context files » Sticky rules vs normal context","id":"1357","title":"Sticky rules vs normal context"},"1358":{"body":"Turn a provider off with the disabledProviders setting in ~/.veyyon/profiles//agent/config.yml or a --config overlay: # ~/.veyyon/profiles/default/agent/config.yml\\ndisabledProviders: - claude - github disabledProviders is a whole-provider switch with one shared id namespace, used by two unrelated subsystems: Id kind Examples Effect when listed Discovery provider ids native, claude, codex, gemini, opencode, github, agents, agents-md The entire config source is removed, not just its context files, but also any MCP servers, slash commands, skills, hooks, tools, prompts, and settings it would have contributed. Model provider ids anthropic, openai, google, groq, ollama, openrouter The model backend is removed from selection even when its credentials are present. See Providers. Ids are exact and the two namespaces do not collide by accident: google disables the Google model backend, while gemini disables the Gemini CLI discovery files. Disabling a discovery provider is heavier than it looks, disabling claude, for instance, also drops Claude-discovered MCP servers, commands, skills, hooks, tools, and settings, not only CLAUDE.md. Only enabledModels and disabledProviders support path-scoped entries, so you can vary provider availability per subtree: disabledProviders: - github # disabled everywhere - path: ~/work/legacy-claude providers: - claude # disabled only under this directory A scoped entry applies when the cwd equals the configured path or sits beneath it; ~ expands to home. Bare string entries apply everywhere. Remember that higher-precedence settings layers replace array settings rather than appending to them. If your profile config disables claude but a --config overlay sets disabledProviders: [github], then in that process Claude discovery is re-enabled and only GitHub is disabled. See Settings for the full layer precedence, merge rules, and path-scoped array details.","breadcrumbs":"Context » Context files » Disabling discovery providers","id":"1358","title":"Disabling discovery providers"},"1359":{"body":"","breadcrumbs":"Context » Context files » Troubleshooting","id":"1359","title":"Troubleshooting"},"136":{"body":"Veyyon keeps surgical edits and whole-file writes separate on purpose. Path Applier Role edit @veyyon/hashline (default) Surgical edits, anchored on snapshot tags and hashline ops write Whole-file writer Create or overwrite a file, minting new snapshot tags in hashline mode apply_patch, patch, replace Mode-specific parsers Compatibility modes selected by edit.mode There is one hashline edit applier for anchored edits, and write stays separate for whole-file creation. Both honor the same approval policy.","breadcrumbs":"Editing and repair » Write path versus edit path","id":"136","title":"Write path versus edit path"},"1360":{"body":"Native project context must live at .veyyon/AGENTS.md, and the .veyyon/ directory must be non-empty; an empty .veyyon/ is skipped and the walk-up continues to the next ancestor. A standalone AGENTS.md or CLAUDE.md at any ancestor is loaded by native itself; agents-md contributes only when native is disabled. A CLAUDE.md is skipped when the same directory has a usable AGENTS.md or .veyyon/AGENTS.md; that is deliberate, see Load order and shadowing. .claude/CLAUDE.md is read only from the current working directory, not from every ancestor. .gemini/GEMINI.md and .github/copilot-instructions.md are user-level only; a repository’s copies are not read. ~/.codex/AGENTS.md and ~/.config/opencode/AGENTS.md are user-level only and have no project equivalent. Empty files contribute nothing for the native and standalone providers. A disabled discovery provider contributes nothing: check disabledProviders across your profile and --config layers.","breadcrumbs":"Context » Context files » A file is not loaded","id":"1360","title":"A file is not loaded"},"1361":{"body":"At one user scope or project depth, the higher-priority provider shadows the others (native > claude > agents/codex > gemini > opencode > github > agents-md). To force deterministic behavior, move your guidance into .veyyon/AGENTS.md (native always wins) or disable the competing discovery provider.","breadcrumbs":"Context » Context files » The wrong file wins","id":"1361","title":"The wrong file wins"},"1362":{"body":"Only one user-level context file survives, and ~/.veyyon/profiles//agent/AGENTS.md has the highest priority. If it exists, it shadows user-level ~/.claude/CLAUDE.md, ~/.codex/AGENTS.md, ~/.gemini/GEMINI.md, ~/.config/opencode/AGENTS.md, ~/.copilot/copilot-instructions.md, and ~/.agent/ ~/.agents files. Consolidate user guidance into the native file or remove the native one if you prefer another tool’s file. A profile without one falls through to the next-priority user file (typically ~/.claude/CLAUDE.md).","breadcrumbs":"Context » Context files » User context disappeared","id":"1362","title":"User context disappeared"},"1363":{"body":"Only one native RULES.md location is sticky: ~/.veyyon/profiles//agent/RULES.md. A RULES.md in any other directory, including a repository’s .veyyon/, is not a recognized convention and will not be loaded.","breadcrumbs":"Context » Context files » A RULES.md file is ignored","id":"1363","title":"A RULES.md file is ignored"},"1364":{"body":"Confirm the target exists relative to the importing file (not the cwd). Imports inside fenced code blocks or inline code spans are intentionally left literal, git@ and email-looking tokens are never imported, cycles are skipped, expansion stops after five hops, and a missing target leaves the original @path text unchanged.","breadcrumbs":"Context » Context files » An @ import did not expand","id":"1364","title":"An @ import did not expand"},"1365":{"body":"On a long task, the model can drift. The objective was stated an hour ago, and now it is buried under a\\nthousand messages the model reads only the tail of. Goal mode fixes this. It pins a structured\\nobjective to the session and injects it separately from the raw conversation tail, so the goal stays in\\nview no matter how long the transcript grows. It pairs with compaction, which handles the history\\nbehind it.","breadcrumbs":"Context » Goal state and long sessions » Goal state and long sessions","id":"1365","title":"Goal state and long sessions"},"1366":{"body":"id:\\nobjective:\\nstatus: # active | paused | budget-limited | complete | dropped\\ntoken_budget: # optional\\ntokens_used:\\ntime_used_seconds:\\nturns_completed: # agent turns accounted to this goal\\ncreated_at / updated_at: The harness persists this on the session. Updates come from /goal commands and the goal tool ( create, get, complete, resume, drop). User objective text is escaped before prompt injection. Token accounting includes input, output, and cache-write deltas used for provider billing. turns_completed counts each agent turn that ran under the goal, so it advances only while the goal is active. Goal budgets are disabled by default. Only the interactive Settings UI can toggle goal.modelBudgetsEnabled; the goal tool and slash-command surfaces cannot change it.","breadcrumbs":"Context » Goal state and long sessions » Goal card (session-backed)","id":"1366","title":"Goal card (session-backed)"},"1367":{"body":"While a goal is set, the mode segment in the status line reads Goal with a live token count. When goal.modelBudgetsEnabled is on and you set a token budget, it shows used/budget and a percent, for example 20K/50K 40%. Once the goal has burned 90% or more of its budget, the segment turns to the warning color so you see the ceiling approaching before the goal hits budget-limited. With the setting off, persisted budgets are inert and the segment shows only tokens used. The goal icon animates through the theme spinner frames while the agent is streaming under the goal, and holds steady when the goal is paused or idle. The animation is driven by active processing time, so it moves only while work is happening. The goal.statusInFooter setting no longer controls whether the token count appears (it always does). It now controls verbosity: turn it on to also render a compact ▰▱ progress bar next to the numbers. To see the full goal card, press the down arrow while the composer is empty. This opens the goal detail menu (the same menu /goal opens): objective, status, tokens used, completed turns, time spent, and the pause, resume, and drop actions. When goal.modelBudgetsEnabled is on, the card also shows budget progress and the adjust-budget action. The down arrow only opens this while a goal is active or paused, so it never interferes with normal editing.","breadcrumbs":"Context » Goal state and long sessions » Status indicator","id":"1367","title":"Status indicator"},"1368":{"body":"Each turn combines system rules, goal injection (when active), active instructions, recent transcript, compaction prefix, and other session context. Compaction settings: Compaction and project memory. Operator commands: Plan mode and goals.","breadcrumbs":"Context » Goal state and long sessions » Context assembly","id":"1368","title":"Context assembly"},"1369":{"body":"Objective visible across turns without rereading the full transcript Idle continuation toward the objective when goal.continuationModes allows Optional token budgets when goal.modelBudgetsEnabled is on, plus pause/complete/drop lifecycle","breadcrumbs":"Context » Goal state and long sessions » What goal mode provides","id":"1369","title":"What goal mode provides"},"137":{"body":"Tool What the model sends Use it for edit A hashline input (default), or a mode-specific payload Surgical edits write A path plus the full content New files or full rewrites apply_patch A V4A envelope When edit.mode is apply_patch You set edit.mode to hashline, apply_patch, patch, or replace in config.yml, or use VEYYON_EDIT_VARIANT for a one-shot override.","breadcrumbs":"Editing and repair » Tools","id":"137","title":"Tools"},"1370":{"body":"A long session eventually fills the context window. The simple fix, dropping the oldest messages, loses\\nthe decisions and constraints the model still needs. Compaction is the better fix: instead of\\ntruncating old history, it compresses it into a summary and keeps working. At any moment a long session\\nholds three records: the goal (when enabled), the recent transcript verbatim, and the compacted history\\nbehind it.","breadcrumbs":"Context » Compaction and project memory » Compaction and project memory","id":"1370","title":"Compaction and project memory"},"1371":{"body":"Primary compaction knobs (settings → Models → Compaction, or config.yml): Threshold ( compaction.threshold): when auto-compaction runs. The unit is\\npart of the value, so one setting covers all three ways you might want to say\\nit: auto (the default) triggers at the model’s context window minus the\\nreserve, so it adapts to whatever model you are on. 85% is a percent of the current model’s window, so the trigger moves with\\nthe model. 170000 is an absolute token amount and triggers at the same point on every\\nmodel. When the amount is larger than the current model’s window it is\\nhonored up to one token below the window and you get a warning (once per\\nmodel context window, so switching to a smaller-window model warns again). You can also compact on demand with /compact. Type ( compaction.strategy): summary, the sole strategy. It rewrites old\\nhistory into an in-place LLM summary on the current branch. Model ( compaction.model): the models that perform LLM compaction, tried\\nin order. Unset uses your interactive model. See Fallback models\\nbelow and Models, roles, and profiles. /compact steers a run with an “Additional focus:” directive. The most\\nrecent user, assistant, and tool messages stay verbatim up to compaction.keepRecentTokens (default 10,000 tokens). Use /handoff when you explicitly want a new session. Handoff is not a\\ncompaction strategy, and automatic maintenance never selects it. Compaction and handoff both write a machine-owned continuity record separate\\nfrom generated prose. It preserves the active objective, the original user\\ncontract, goal and todo state, pending blockers, changed paths, verification\\nevidence, and checkpoint state. Handoff writes that record into the replacement\\nsession before the next turn. Reopening either session restores exact state\\ninstead of relying on generated prose to repeat every field. Stored legacy strategy names such as handoff, snap, soft, and remote\\nmigrate to summary. A legacy off value also disables compaction.","breadcrumbs":"Context » Compaction and project memory » Context compaction","id":"1371","title":"Context compaction"},"1372":{"body":"compaction.model is an ordered list, not one model: compaction: model: anthropic/claude-opus-4-1,anthropic/claude-sonnet-4-5,anthropic/claude-haiku-4-5 Compaction tries the first entry. If you are not signed in to it, or its context window cannot\\nhold the history being summarized, veyyon moves on to the second, then the third. A single model\\nis still written the way you would expect ( model: anthropic/claude-sonnet-4-5). In /settings,\\nthe compaction model row is the same list: add a fallback, and press Enter on any entry to move it\\nup. Falling back is never quiet. When compaction runs on anything other than your first choice, you get\\na warning in the session stating both models and the reason: Compacted with anthropic/haiku-4-5. anthropic/opus-4-1 was skipped: it is not authenticated. You see that once per distinct reason, not once per compaction. compaction.modelFallbackStrategy sets what happens after your list runs out: auto (the default) stays on models you named: your main model, the same-provider compaction\\nsibling its catalog row recommends, then each of your model roles. any-model keeps going past those to the largest context window you have credentials for,\\nwhichever provider that is. Compaction almost never fails, at the cost of summarizing on a\\nprovider you did not choose for this session and being billed for it there. configured-only stops at the models you listed. Compaction fails with the reason instead, which\\nis what you want when the summary quality matters more than the session continuing. With compaction.model unset, configured-only means your interactive model and nothing else. Compaction fires unattended, so any-model is the one setting here that can spend money on an\\naccount you were not using: a session on one provider can summarize on another provider’s key and\\nreport that provider’s billing error as a compaction failure. That is why it is not the default.","breadcrumbs":"Context » Compaction and project memory » Fallback models","id":"1372","title":"Fallback models"},"1373":{"body":"Shake is a lighter reducer than compaction. Instead of summarizing history, it drops heavy\\ncontent out of the live context and leaves a short placeholder in its place. Whole tool\\nresults and large fenced or XML blocks are replaced with a marker such as [shaken ~1200 tokens; recover: artifact://42 (region 3)]. The full text is saved as a\\nsession artifact first, so you can always read it back with read artifact://42. Nothing is\\nlost, it just stops being resent on every turn. Run it on demand with /shake. Shake also removes redundancy. When you read the same unchanged file twice, or run the same\\ncommand twice and get the same output, every copy but the newest contains no new information.\\nShake finds each earlier tool result whose tool, arguments, and output exactly match a later\\none, and elides the earlier copies through the same artifact path. The newest copy stays in\\nplace. This runs even for recent results that the size-based pass would otherwise keep, because\\na duplicate is redundant however recent it is. Results from a protected tool (such as skill),\\nerror results, and results already elided are never deduplicated. The match is exact. If a command’s output changes between runs, both runs are kept, because the\\nlater one is genuinely new information rather than a repeat. Duplicate elision runs on its own before in-place compaction. Whenever automatic\\nmaintenance runs because context crossed the threshold or overflowed, it first\\nruns this lossless Tier-0 pass. If dropping duplicates brings a threshold trigger\\nback under the bar, compaction is skipped and history stays intact apart from the\\nelided copies. Overflow recovery still finishes compaction because the prompt must\\nbe rebuilt to fit the window, but it starts from the smaller deduplicated history.","breadcrumbs":"Context » Compaction and project memory » Shake and duplicate elision","id":"1373","title":"Shake and duplicate elision"},"1374":{"body":"When memory.backend is mnemopi or hindsight, compaction can request pre-compaction context\\nfrom the active memory backend so summaries retain project facts. See Memory.","breadcrumbs":"Context » Compaction and project memory » Memory backends","id":"1374","title":"Memory backends"},"1375":{"body":"Goal cards and budgets: /goal, /guided-goal, and the goal tool. Structure: Goal state and long sessions. Operator surface: Plan mode and goals.","breadcrumbs":"Context » Compaction and project memory » Goals","id":"1375","title":"Goals"},"1376":{"body":"Role and subagent machinery is configuration and spawn parameters, not a fixed pipeline. Intra-harness\\nrole policy chooses which model, prompt, and tool surface fits a subagent or specialized pass.\\nVeyyon is provider-agnostic: roles are not hard-coded provider assumptions.","breadcrumbs":"Role policy » Role policy","id":"1376","title":"Role policy"},"1377":{"body":"Subagents via the task tool ( packages/coding-agent/src/task/executor.ts). /agents opens the\\nlive hub for active and persisted agent threads. Explicit model policies, not a role-to-model matrix: the interactive model ( /model), profile-wide\\nsubagent defaults and per-agent overrides under subagent, plus compaction.model. default is not\\na model or a role. Named roles ( modelRoles, scoped per profile) let you pin specific work types.\\nEdit roles under Settings → Model → Roles and subagent policy under Settings → Subagents. See Compaction & project memory and Models, roles, and profiles. Plan / goal modes alter prompts and tool gating ( /plan, /goal). There is no /advisor slash\\ncommand, the advisor watchdog ( advisor.enabled and related settings, in packages/coding-agent/src/advisor/) is a background continuous-review mechanism, not a mode you\\ninvoke. See docs/handbook/src/features/advisor.md. Addressed inter-agent messaging via the irc tool ( packages/coding-agent/src/tools/irc.ts, packages/coding-agent/src/irc/bus.ts): send/ wait/ inbox/ list ops over a process-global bus. send is fire-and-forget with delivery receipts; the bus wakes an idle recipient with a real turn,\\nrevives a parked one, or injects a non-interrupting aside into a busy one, the shipped analogue of\\nwake-now-vs-defer message routing. wait (or send await:true) observes the recipient’s reply as a\\nreal turn. Gated by isIrcEnabled: available to every subagent and to a top-level session that can\\nstill spawn subagents.","breadcrumbs":"Role policy » What exists today","id":"1377","title":"What exists today"},"1378":{"body":"Veyyon does not enforce a staged plan → implement → verify → repair handoff. It uses lighter-weight\\nspawn, subagent-policy, and irc messaging patterns instead; you compose the stages yourself. Pair role choice with execution-order prompts: explore → plan → edit → verify.","breadcrumbs":"Role policy » No fixed role pipeline","id":"1378","title":"No fixed role pipeline"},"1379":{"body":"","breadcrumbs":"Observability » Observability","id":"1379","title":"Observability"},"138":{"body":"The loop is short: read (or grep) returns [relative/path#TAG] and LINE:text rows. The model calls edit, anchoring each section on the same TAG. On success, the output includes a fresh [path#NEW_TAG] and a compact diff. write strips pasted hashline prefixes when appropriate, and can mint new tags after a whole-file write.","breadcrumbs":"Editing and repair » Hashline workflow","id":"138","title":"Hashline workflow"},"1380":{"body":"Status line token and cost segments during interactive sessions veyyon stats (CLI) and /usage (TUI) via @veyyon/stats when enabled Structured logging in the coding-agent logger","breadcrumbs":"Observability » Interactive and CLI usage","id":"1380","title":"Interactive and CLI usage"},"1381":{"body":"When OTEL_EXPORTER_OTLP_ENDPOINT or OTEL_EXPORTER_OTLP_TRACES_ENDPOINT is set, the process exports agent-loop traces over OTLP/protobuf. See Soundness and telemetry and packages/coding-agent/src/telemetry-export.ts.","breadcrumbs":"Observability » OpenTelemetry","id":"1381","title":"OpenTelemetry"},"1382":{"body":"/dump, /context, /debug, and standalone tool CLIs such as veyyon grep for inspecting what the agent would see.","breadcrumbs":"Observability » Session debugging","id":"1382","title":"Session debugging"},"1383":{"body":"When a model behaves in a way you cannot explain from the transcript, record the\\nexact HTTP exchange. Set VEYYON_REQ_DEBUG=1 before starting veyyon: VEYYON_REQ_DEBUG=1 veyyon Every request writes two files into the directory you started veyyon from: rr-session-N.json, the request: method, URL, headers, and body. A JSON body\\nis stored parsed under body; anything else is stored as bodyText, or as bodyBase64 when it is not valid UTF-8. rr-session-N.res.log, the response: the status line and headers, then the\\nraw body bytes exactly as they arrived, including every streaming chunk. N counts up from 1 each time veyyon starts, and existing files are never\\noverwritten, so a second run in the same directory continues past the numbers\\nalready there. Two things are worth knowing before you turn it on. The files land in your\\nworking directory rather than a cache directory, because you usually want to\\nread them next to the project you were working in; they are created readable by\\nyou alone, and the repo .gitignore already covers rr-session-* so a stray git add cannot pick one up. And a dump is the request as it went on the wire,\\nincluding file contents and whatever a provider echoed back, so treat it as\\nsensitive and delete it when you are done. Credential headers do not go in. Authorization, Cookie, any header whose\\nname carries api-key, auth-token, access-token or secret, and their\\nresponse-side counterparts are written as : you can still\\nsee that the header was sent and how long the value was, which is what a\\ndebugging session needs, without the key itself sitting in a file you might\\nattach to a bug report. Bodies are recorded verbatim, so an OAuth token\\nexchange still puts a refresh token in the file. Recording never interferes with the session it records. If a log cannot be\\nwritten, because the disk is full or the directory is read-only, veyyon logs an\\nerror stating the file and the cause, stops recording that response, and lets the\\nresponse through untouched. You get your answer and a truncated log, not a\\nfailed request. Each file stops at 32 MiB. Past that the response log writes a line stating the\\nceiling, then a final line stating how many bytes it recorded and how many it\\nomitted; a request body that ran past the ceiling contains the same counts under bodyCapture, and the body itself is stored as bodyText rather than parsed\\nJSON. The response you get is unaffected: the ceiling bounds the recording, not\\nthe request. Raise or lower it with VEYYON_REQ_DEBUG_MAX_BYTES, in bytes; a\\nvalue that is not a positive integer is a typo rather than a request for no\\nceiling, so veyyon warns and keeps the default. An omitted count of null on a\\nrequest body means the sender declared no length, so the bytes past the ceiling\\nwere never counted.","breadcrumbs":"Observability » Recording raw provider traffic","id":"1383","title":"Recording raw provider traffic"},"1384":{"body":"Common failure paths.","breadcrumbs":"Troubleshooting » Troubleshooting","id":"1384","title":"Troubleshooting"},"1385":{"body":"veyyon --version\\nveyyon plugin doctor veyyon plugin doctor reports extension health and missing optional binaries/keys. Non-zero exit: fix the reported check and re-run.","breadcrumbs":"Troubleshooting » Install or startup","id":"1385","title":"Install or startup"},"1386":{"body":"Check API key / auth store / models.yml for that provider id, base URL, and scopes. See Models and providers. A provider error message contains the failing status and the body the server sent, with three\\nlimits. At most 64 KiB of the body is read, and the message states it: [truncated, showing 4096 of 65536 chars read, 134505 of 200041 bytes not read], or read stopped at 65536 bytes\\nwhen the server declared no length. Control characters and terminal escape sequences are\\nremoved, so an error page cannot repaint the screen. Credential-shaped text is replaced with : an Authorization or Cookie value, a bearer token, a JWT, and the\\nvendor key prefixes. A proxy or captive portal answering HTML instead of the provider is the\\nusual reason a message is truncated. A streamed response is read one frame at a time: a line, a JSONL record, or an SSE event\\nending at a blank line. One frame may occupy 64 MiB. A server or proxy that keeps sending\\nwithout ever sending the delimiter is stopped at that point, the connection is cancelled,\\nand the message states the protocol and both byte counts: an SSE event arrived with no blank-line dispatch: 67109376 bytes exceeded the 67108864 byte frame limit. The same\\nbound covers a stream of data: lines that never dispatches and a keepalive comment sent\\nin a loop. This failure is never retried: the next attempt reaches the same peer.","breadcrumbs":"Troubleshooting » Provider errors","id":"1386","title":"Provider errors"},"1387":{"body":"Policy is tools.approvalMode and tools.approval, plus the working-directory and secret-use boundaries (every rung except yolo) and hard-coded flagged bash patterns: the destructive ones prompt on every rung, yolo included, and the merely dangerous ones ( curl | sh, reboot, nc -e) prompt on every rung below it. There is no OS command sandbox. Schema default is auto. See Approvals and Configuration.","breadcrumbs":"Troubleshooting » Command or edit blocked or prompting","id":"1387","title":"Command or edit blocked or prompting"},"1388":{"body":"Tool results truncate at configured budgets; the result text should state that truncation occurred and how to continue (limit, offset, narrower query). See Bounded reads and search.","breadcrumbs":"Troubleshooting » Truncated tool output","id":"1388","title":"Truncated tool output"},"1389":{"body":"Observability","breadcrumbs":"Troubleshooting » Related","id":"1389","title":"Related"},"139":{"body":"After edit, write, or ast_edit changes a path, Veyyon records the mutation.\\nIf the model tries to finish without a later successful bash, eval, debug,\\nor browser result, Veyyon gives it one targeted continuation turn. The model\\nmust run the check and report what the result established. A successful command is execution evidence. It does not prove that the command\\ntested the right behavior. You should still read the final verification claim\\nand confirm that it matches the command or browser scenario that ran.","breadcrumbs":"Editing and repair » Verification after a mutation","id":"139","title":"Verification after a mutation"},"1390":{"body":"Common questions and errors. For a guided diagnostic path, see Troubleshooting.","breadcrumbs":"FAQ » Frequently asked questions","id":"1390","title":"Frequently asked questions"},"1391":{"body":"","breadcrumbs":"FAQ » Setup","id":"1391","title":"Setup"},"1392":{"body":"veyyon plugin doctor exits non-zero when a check reports an error, and it prints the failed check and the next action. Fix the line it reports, then run it again. For the full diagnostics surface, see Diagnostics and health.","breadcrumbs":"FAQ » veyyon plugin doctor fails. What do I fix?","id":"1392","title":"veyyon plugin doctor fails. What do I fix?"},"1393":{"body":"No OS confinement (no Landlock, seccomp, Seatbelt, bubblewrap). Policy is tools.approvalMode (schema default auto), plus the working-directory and secret-use boundaries, which apply on every rung except yolo, and hard-coded flagged bash patterns: the destructive ones prompt on every rung including yolo, and the merely dangerous ones ( curl | sh, reboot, nc -e) prompt on every rung below it. See Approvals.","breadcrumbs":"FAQ » Does Veyyon sandbox the commands it runs?","id":"1393","title":"Does Veyyon sandbox the commands it runs?"},"1394":{"body":"There is no cross-process lock on a session file: nothing prevents two veyyon processes from opening the same session at once, and the single-writer guarantee is per-process only. Treat one session as belonging to one running process; do not edit or delete its file while that process is alive. For how sessions are stored and resumed, see Sessions.","breadcrumbs":"FAQ » Database and session locking","id":"1394","title":"Database and session locking"},"1395":{"body":"","breadcrumbs":"FAQ » Model authentication","id":"1395","title":"Model authentication"},"1396":{"body":"The process calls the configured provider endpoint with the configured key. Check env var / auth store / models.yml for that provider, key validity, and scopes. See Models and providers.","breadcrumbs":"FAQ » “Invalid API key” or “Authentication failed”","id":"1396","title":"“Invalid API key” or “Authentication failed”"},"1397":{"body":"The base URL you configured must match the provider region and product endpoint. A model id that exists in one region may not exist in another, and the same hostname may host different model catalogs. Verify the endpoint URL in your provider dashboard and compare it with the base_url in your config. Models and providers explains how provider configuration is resolved.","breadcrumbs":"FAQ » “Unsupported region” or endpoint errors","id":"1397","title":"“Unsupported region” or endpoint errors"},"1398":{"body":"Veyyon lists models from a bundled catalog plus live discovery from providers that expose a /models endpoint. If a model is not listed, the provider endpoint may not expose it, or your key may not have access to it. Check the provider catalog and your key scopes first.","breadcrumbs":"FAQ » Why is my model not listed?","id":"1398","title":"Why is my model not listed?"},"1399":{"body":"","breadcrumbs":"FAQ » Workflow","id":"1399","title":"Workflow"},"14":{"body":"Compaction, goal continuation, plan mode, vibe mode, and task subagents live in the session and tool layer, not only in prompt text. Goal mode can keep an idle session moving toward a stored objective. Plan mode writes a plan file and holds back mutation until the resolve and approval paths complete.","breadcrumbs":"Design and mechanisms » Mechanisms » Engine modes","id":"14","title":"Engine modes"},"140":{"body":"Edits honor the approval mode, just as bash does. A tools.approval.: deny policy keeps the tool in the model’s list but rejects every call at dispatch with an error stating the policy. Tools leave the model’s list via .enabled: false, harness-profile allowlists, tools.discoveryMode (BM25 hiding), extension or agent tool-set overrides, or agent definitions. Plan mode keeps the list and blocks mutations at approval time. Hashline is the primary write path, and apply_patch is a compatibility mode. There is no single V4A applier that routes every mutation through a make_update_patch envelope.","breadcrumbs":"Editing and repair » Safety","id":"140","title":"Safety"},"1400":{"body":"The approval mode sets when Veyyon prompts before a tool runs. In ask, every tier prompts, reads included. In ask-command, reads and edits run and anything that executes prompts. In auto, the default, every tier runs with the per-tool, working-directory, credential and critical-call guards still prompting. In plan, exec is blocked outright and write prompts only inside an active plan-mode session. Change mode with --approval-mode ( plan, ask, ask-command, auto, yolo), --auto-approve / --yolo, or tools.approvalMode in config.yml. See Approvals.","breadcrumbs":"FAQ » Why did my edit ask for approval?","id":"1400","title":"Why did my edit ask for approval?"},"1401":{"body":"Run veyyon --continue to continue the most recent session, or veyyon --resume to resume a specific one. The session stores turns and tool activity, so a resumed session keeps its context. For branching, forking, or exporting a session, see Sessions.","breadcrumbs":"FAQ » How do I resume a session?","id":"1401","title":"How do I resume a session?"},"1402":{"body":"Queued follow-ups live in memory for the lifetime of the running process; they are not written to the session file. If you press Esc to interrupt the current turn, queued follow-ups are pulled back into the composer so nothing is lost. See Sessions for the full queue behavior.","breadcrumbs":"FAQ » What happened to my queued follow-up?","id":"1402","title":"What happened to my queued follow-up?"},"1403":{"body":"Output is intentionally truncated when it exceeds a tool budget. The truncation should include a next action, such as increasing a limit, using an offset, or narrowing the search. See Troubleshooting for the public path.","breadcrumbs":"FAQ » Why does my output look truncated?","id":"1403","title":"Why does my output look truncated?"},"1404":{"body":"Troubleshooting for the guided diagnostic path. Models and providers for provider keys, endpoints, and model selection. Approvals for the approval modes. Sessions for resume, fork, branch, and export.","breadcrumbs":"FAQ » Where to go next","id":"1404","title":"Where to go next"},"1405":{"body":"veyyon setup status is the health check. It answers two things in one pass: whether the install itself works, and whether you are signed in to a provider. Plugin health has its own command, and the TUI has its own debug tools.","breadcrumbs":"Diagnostics and health » Diagnostics and health","id":"1405","title":"Diagnostics and health"},"1406":{"body":"$ veyyon setup status\\n$ veyyon setup status --json The install checks run first, because nothing below them can work if the install does not. They are the same checks the installer runs at the end of every install, run against your machine as it is now: Check What it proves veyyon on PATH The shell can find it, and which file it found. PATH copies Only one veyyon is on your PATH. A second one earlier on PATH keeps answering after an update writes the first, which is what makes an update look like it did nothing. veyyon runs It executes and reports the version you are running. If it will not start, the check quotes the system error text. Native addon A real search returns a real match, so the native addon loaded. --version alone passes without it. Install method Whether veyyon update swaps the binary or advances a source checkout. vey alias The short name used throughout the documentation resolves. Shell completions Completion files are installed, and for which shells. On Windows that is the single script beside your PowerShell profile, since PowerShell has no directory it autoloads completions from. None of them touches the network. A health check you cannot run when the network is what broke is not much of a health check. After the install checks come the credential checks: git on PATH (missing is an error), and provider authentication through OAuth or one of GEMINI_API_KEY / OPENAI_API_KEY / ANTHROPIC_API_KEY / KIMI_API_KEY (missing is a warning). The command exits non-zero when any check reports an error, so you can gate a script on it. Warnings exit zero: they are worth reading, not worth stopping for.","breadcrumbs":"Diagnostics and health » System health","id":"1406","title":"System health"},"1407":{"body":"$ veyyon plugin doctor\\n$ veyyon plugin doctor --fix Checks plugin installation health. With --fix, it attempts automatic repairs where implemented.","breadcrumbs":"Diagnostics and health » Plugin doctor","id":"1407","title":"Plugin doctor"},"1408":{"body":"/debug Opens the debug tools selector in the interactive session.","breadcrumbs":"Diagnostics and health » TUI debug","id":"1408","title":"TUI debug"},"1409":{"body":"/memory diagnose\\n/memory stats Run diagnostics and statistics on the configured memory backend ( memory.backend: mnemopi, hindsight, local) from the TUI. See Memory.","breadcrumbs":"Diagnostics and health » Memory diagnostics","id":"1409","title":"Memory diagnostics"},"141":{"body":"Approvals are how you decide which tools run without asking. One setting drives them: tools.approvalMode. There is no operating-system sandbox behind it (no Landlock, seccomp,\\nSeatbelt, or bubblewrap). Shell commands and file writes run as your user, bounded only by\\nthis policy, per-tool tools.approval overrides, and the hard-coded flagged bash patterns\\nbelow. Operator reference. For the model behind it, see Permission model. For the wider boundary, see Safety.","breadcrumbs":"Approvals » Approvals","id":"141","title":"Approvals"},"1410":{"body":"veyyon setup status when veyyon itself is misbehaving: it covers the install and your credentials. veyyon plugin doctor when an extension is misbehaving. /debug and /memory diagnose inside a session. Troubleshooting for common setup failures.","breadcrumbs":"Diagnostics and health » Which one to reach for","id":"1410","title":"Which one to reach for"},"1411":{"body":"Both veyyon setup status and veyyon plugin doctor exit non-zero when a check reports an error, and zero when the worst result is a warning.","breadcrumbs":"Diagnostics and health » Exit status","id":"1411","title":"Exit status"},"1412":{"body":"Install Troubleshooting Plugins","breadcrumbs":"Diagnostics and health » See also","id":"1412","title":"See also"},"1413":{"body":"Veyyon incorporates ideas and code from upstream and peer projects. oh-my-pi ( can1357/oh-my-pi), under the MIT license. Veyyon\\nis a source fork of oh-my-pi: the TypeScript/Bun agent loop and TUI, the Rust natives (search, the\\nshell, the PTY), the hashline edit engine, provider breadth, role routing, session-tree work, and\\nedit ergonomics all carry forward from it. Incorporated MIT code keeps its permission notice; see\\nthe repository LICENSE. codex, by OpenAI, under the Apache 2.0 license. oh-my-pi and Veyyon carry forward the codex apply_patch patch format and parts of the agent-loop shape as an independent TypeScript\\nreimplementation, see NOTICE for exactly which files are format-compatible versus which actually\\nvendor Apache 2.0 code (the OpenAI wire types and the Playwright ARIA-snapshot bundle do; the apply_patch parser and the Codex backend client do not). OpenCode, under the MIT license. Veyyon studies its plan/build workflow, project memory, compact\\ncommand, and file-context UI ideas. Lossless Claw, under the MIT license. Veyyon studies its summary DAG, fresh-tail compaction, and\\ncompacted-history inspection tools. command-code, by Langbase. command-code is proprietary. Veyyon only studies observable mechanisms\\nclean-room, copying no code or bundled implementation text. Legal credits and upstream notices live in the repository LICENSE, NOTICE, and UPSTREAM.md.","breadcrumbs":"Credits and licenses » Acknowledgements","id":"1413","title":"Acknowledgements"},"1414":{"body":"A concise vocabulary of the primitives that shape Veyyon’s runtime behavior. apply_patch: Edit mode ( edit.mode: apply_patch) for a Codex-style *** Begin Patch … *** End Patch envelope. Default edit mode is hashline via the edit tool. Apply-patch shares approval policy with other write paths. approval mode: The autonomy control ( tools.approvalMode) for tool tiers: plan, ask, ask-command, auto (the default), yolo (legacy always-ask → ask, write and auto-edit → ask-command). There is no OS command sandbox; the mode, per-tool tools.approval overrides, the working-directory and secret-use boundaries, and hard-coded flagged bash patterns are the boundary. Of those patterns, the destructive ones prompt on every rung, yolo included, and the merely dangerous ones prompt on every rung below it. model catalog: Bundled provider/model data plus models.yml / models.yaml custom entries. There is no separate backends.toml subsystem. compaction: The compression layer that summarizes a long trajectory into a smaller, information-preserving form instead of truncating it. Compaction preserves the goal card, recent user messages, and deterministic working-set facts across successive windows. edit / write: The edit and write tools change files on disk. Default edit is hashline (content-hash anchors); write creates or overwrites a whole file. Both respect tools.approvalMode. Freeform tool / Function tool: The two tool shapes Veyyon advertises to a model. A Freeform tool emits a raw grammar-shaped body; a Function tool emits JSON arguments matching a schema. The choice depends on the backend wire API. goal state: A structured goal card on the session (session-backed). Holds the objective and lifecycle fields, injected outside the raw conversation tail so compaction does not drop intent. hook: A TypeScript module that default-exports a factory and registers handlers with pi.on(...) (events such as tool_call, tool_result, session_start). Can block tools, inject context, or register commands. See Hooks. MCP: Model Context Protocol. Veyyon is an MCP client that connects to external MCP servers and exposes their tools as mcp__…. Editor embedding uses ACP ( veyyon acp), which is a different protocol. model contract / BYOK: The model contract is your chosen endpoint, model, and credentials. BYOK (bring-your-own-key) means you supply your own provider or local-endpoint key; Veyyon calls that API with your credentials. Optional OTEL export is separate and only when configured. personality: Style-only system prompt block. Built-ins include default, pragmatic, friendly, and none. plugin: A directory with a .claude-plugin/plugin.json manifest (or a package.json containing the veyyon manifest) that can add skills, MCP servers, hooks, and related assets. Plugins are discovered through marketplaces, npm installs, or veyyon plugin link. profile: A directory under ~/.veyyon/profiles// (including default) holding agent settings, sessions, MCP, skills, and related state. Activate with --profile, VEYYON_PROFILE, or /profile (relaunch). prompt-cache discipline: Keeping stable prompt prefixes byte-stable so provider prompt caches hit; context order and compaction are designed around that. repair: Schema-based coercion of malformed tool-call arguments before validation; ambiguous cases return an error tool result (no dispatch). See Repair. repair cascade: The ordered set of sound transforms the repair engine applies to a tool call (parse leniency, alias/typo key repair, strict unknown-key rejection, ambiguity guard). The engine returns a status ( clean / repaired / unrepairable), the coerced arguments, and coaching hints; an unrepairable call returns an error tool result without dispatch. rollout: The append-only JSONL log of a session’s entries. Each entry contains an id and a parentId. Branching moves the in-memory leaf; the next appended entry’s parentId (and an optional branch_summary entry) records the move without rewriting history. session: The unit of interactive work in Veyyon. A session records turns, tool activity, approvals, edits, and verification output. skill: Filesystem package with a SKILL.md. Loaded only from an explicit allowlist: the active profile’s skills directory, veyyon-managed skills, and plugin-bundled skills. Foreign-tool layouts never contribute skills. Metadata enters the system prompt; body is read via skill://. thread / active leaf: A thread is a linear sequence of messages within a session. The active leaf is the currently selected tip of the session tree that receives the next turn; branching moves the leaf without erasing sibling history. tool call: A model message that invokes a tool by name with arguments. Repair may coerce malformed arguments before validation. turn: One model-invocation cycle: assemble context, model response, tool calls until the turn ends. verifier / stop-when-green: Checks whether a goal or task is satisfied. Stop-when-green ends the turn loop once verification passes. See also: Sessions, turns, and threads, Permission model, Model contract, Repair overview, and Compaction and memory.","breadcrumbs":"Glossary » Glossary","id":"1414","title":"Glossary"},"142":{"body":"Tier Examples read read, grep, glob, listing write edit, write exec bash and other command execution","breadcrumbs":"Approvals » Tool tiers","id":"142","title":"Tool tiers"},"143":{"body":"Mode read write exec plan auto ask with an active plan-mode session, denied otherwise denied ask ask ask ask ask-command auto auto ask auto auto auto auto, with the per-tool, working-directory, credential and flagged-command guards still asking yolo auto auto auto, except a blatantly destructive command, which still prompts Schema default: auto. Legacy aliases: always-ask → ask, write and auto-edit → ask-command. $ veyyon --approval-mode ask-command\\n$ veyyon --yolo # same as --auto-approve → yolo\\n$ veyyon --plan-yolo # plan now; yolo after leaving plan mode tools: approvalMode: ask","breadcrumbs":"Approvals » Modes","id":"143","title":"Modes"},"144":{"body":"When the active mode requires approval for a tool call, the TUI shows a Permission required\\ncard. The card shows the tool, states that the decision applies to this call only, separates the\\nreason from the requested command or file operation, and waits on four options: Approve: run this call once. Nothing is remembered. Approve for session: run this and every later call to this tool, until you exit. Deny: reject this call and return Tool call denied by user: to the model. Deny for session: reject this and every later call to this tool, until you exit. The two “for session” rows are session memory, not policy: nothing is written to tools.approval, and the next launch prompts again. A remembered decision also covers only\\nthe ordinary tier prompt. The three prompts that are about a call’s ARGUMENTS rather than\\nits tool name still prompt every time: a flagged bash command, a path outside the working\\ndirectory, and a call that spends a stored credential. The selected option uses a radio marker and includes a short description. Navigate with the usual\\nlist keys ( up/ down, enter to confirm, esc to cancel; cancelling counts as a denial).\\nDenied actions return an error to the model, and permissions are never widened.","breadcrumbs":"Approvals » The approval prompt","id":"144","title":"The approval prompt"},"145":{"body":"veyyon --print has no terminal to prompt in. If the mode would require approval, the tool call\\nfails with an error that explains the required setting or override (set tools.approvalMode: yolo,\\nadd tools.approval.: allow, or use an interactive UI), and the model receives that error. To\\nrun unattended, pass --yolo or pick a mode that does not prompt for the tiers you need. The\\nprocess exit status follows the run.","breadcrumbs":"Approvals » Headless","id":"145","title":"Headless"},"146":{"body":"Some shell commands always prompt in plan, ask, ask-command and auto, even over a per-tool allow override. The guard lives in packages/coding-agent/src/tools/bash-guard.ts and has\\ntwo halves. The first half judges what a command would delete, after expansion rather than as text. It\\nresolves a leading tilde and $HOME, judges every target rather than only the first, and\\nstops a recursive delete of the home directory, of anything containing it, of a system\\ndirectory, or of a directory holding your credentials. It also stops a recursive delete whose\\ntarget it cannot resolve, such as rm -rf \\"$dir\\"/*, because an empty $dir makes that\\ncommand start at the root. It also stops a truncating redirect into a credentials directory,\\nsuch as echo x > ~/.ssh/id_ed25519; appending with >> is left alone. Deletes inside your\\nworkspace, such as rm -rf node_modules or rm -rf dist, run without a prompt, and so do\\nordinary redirects such as bun test > /tmp/results.txt. The second half is a pattern list ( FLAGGED_BASH_PATTERNS, same file) for shapes with no\\npath to expand, and each entry records what it would do. The destructive ones are sudo rm,\\nrecursive chmod/ chown on /, fork bombs, disk and filesystem destruction ( mkfs, dd to a\\ndevice, writes to /dev/sd*), and writes to /etc/passwd/ shadow/ sudoers. The dangerous\\nones are a remote fetch piped to a shell ( curl … | sh and its process-substitution and eval\\nvariants), host control ( shutdown, reboot, kill -9 1), and network shells ( nc -e): these\\nrun code nobody read or restart the machine, without destroying anything. Neither half can be narrowed; the guard exists because a false negative costs data loss or a\\ncompromised host. You can widen the first half with tools.protectedPaths, a list of absolute\\npaths (a leading ~ is expanded) that a recursive delete must also stop for. It only adds:\\nnothing in the built-in judgement reads configuration, so no value there can stop the guard\\nrefusing your home directory. See the permission model for an example. The destructive half, and the whole of the first half, stop for approval in yolo as well, and\\nthe /yolo session bypass does not lift them. That floor is the one place yolo is not\\nabsolute. The dangerous half stops on every rung below yolo and not on yolo itself, because\\na rung whose entire promise is that it does not prompt cannot be stopping an install the operator\\ntyped. To turn the floor off on yolo, set tools.approval.bash to allow; below yolo an allow is outranked by the guard, and deny is a hard block on every rung. Separately, the bash interceptor ( bashInterceptor.enabled, default off) blocks shell\\ncommands that duplicate dedicated tools, so the model uses read/ grep/ glob\\ninstead of cat/ rg/ find. Its rules live in bashInterceptor.patterns.","breadcrumbs":"Approvals » Critical bash commands","id":"146","title":"Critical bash commands"},"147":{"body":"Permission model Non-interactive mode Safety","breadcrumbs":"Approvals » Related","id":"147","title":"Related"},"148":{"body":"Commands and file writes go through approval mode ( tools.approvalMode). There is no OS command sandbox (Landlock, seccomp, Seatbelt, bubblewrap). Policy details: Approvals. Concepts: Permission model. Task subagents can use filesystem isolation (CoW worktree backends via subagent.isolation.*) so their edits land in a private tree until merged. That is change-control for subagents, not an OS process sandbox. See the task tool docs, or the Isolation group in the Subagents settings tab.","breadcrumbs":"Approvals » Safety » Safety","id":"148","title":"Safety"},"149":{"body":"Situation Result Command or edit needs permission Approval prompt with command/path and cwd Approval denied Denial / tool failure; no partial escalation of rights Tool JSON malformed but unambiguous Schema repair, then validation/dispatch Tool JSON ambiguous or unrepairable Error tool result to the model; no dispatch Tool output truncated Truncation recorded in the tool result Config / provider data invalid Load fails with path and context","breadcrumbs":"Approvals » Safety » Operator-visible cases","id":"149","title":"Operator-visible cases"},"15":{"body":"Every profile, including default, lives at ~/.veyyon/profiles//agent/, which holds its settings, sessions, MCP config, skills, and hooks. See Profiles and File locations.","breadcrumbs":"Design and mechanisms » Mechanisms » Profiles","id":"15","title":"Profiles"},"150":{"body":"veyyon --print has no TTY for prompts. Set --approval-mode / --yolo explicitly for the job. Reserve full auto-approve for disposable runners. See Non-interactive mode.","breadcrumbs":"Approvals » Safety » Headless","id":"150","title":"Headless"},"151":{"body":"Approvals Configuration Repair","breadcrumbs":"Approvals » Safety » Related","id":"151","title":"Related"},"152":{"body":"A CPU limit caps how much processor time the processes a session spawns may use. You set it in\\ncores: session.cpuLimitCores: 2 lets the session’s commands consume at most two cores, no matter\\nhow many the machine has. The limit is per session, not per machine: two sessions capped at 2\\ncores each may use 4 between them. There is no cross-session cap, by design. Two settings drive it: session: cpuLimitCores: 2 # 0 (the default) is off cpuLimitKill: false # what to do past the budget; see below","breadcrumbs":"CPU limits » CPU limits","id":"152","title":"CPU limits"},"153":{"body":"Every process a session spawns to do its work joins the budget. That covers bash commands (plain\\nand PTY), MCP stdio servers, the exec calls that custom tools, custom commands, and extensions\\nmake, background processes from the launch tool, the eval kernels (Python, Ruby, Julia),\\nlanguage servers, debug adapters, the managed browser, git and jj, ssh, and the installs\\nthat plugins run. A capped process passes the budget to its own children, so a build that spawns\\na compiler fleet is still one budget. Some processes belong to no single session and join the root session’s budget instead. Those are\\nthe shared harness workers, such as the tiny title model and embeddings, and the speech capture\\nand playback helpers. Five kinds of process stay outside the budget. Each is outside for a reason rather than by\\noversight: Anything that starts before a session exists. Host capability probes, the shell environment\\nsnapshot, model provider probes, and the ssh bootstrap for a remote auth broker all run when\\nthere is no budget to join. The harness itself. Agent turns, the TUI, and the relaunch that replaces the veyyon process. Programs that are yours rather than the agent’s. The editor veyyon opens a file in, the\\nclipboard helper, veyyon shell, and the self-updater. Capping the updater could leave a\\nhalf-written install, and killing your editor on a budget breach would discard unsaved text. Threads rather than processes. The browser tab supervisor and the JavaScript eval context\\nrun as Bun Workers inside the harness process, and a cgroup holds processes, not threads of one. Processes the session did not start. Attaching to a browser that is already running adopts\\nnothing, because the session does not own that process. If you cap a session at 1 core, veyyon stays responsive while the build under it crawls.","breadcrumbs":"CPU limits » What is capped","id":"153","title":"What is capped"},"154":{"body":"Where the operating system offers a per-group CPU quota, the kernel does the capping: Linux uses a cgroup v2 directory per session with cpu.max set to the core count. If the\\nharness’s own cgroup is not writable, veyyon requests a scope from the systemd user manager with CPUQuota instead. Windows uses a Job Object with a hard CPU rate cap. A once-per-second watcher reads the group’s usage on top of the kernel cap. When usage stays\\npinned at the budget for about three seconds, new commands are rejected with an error that names\\nthe budget, the measured usage, and the fix (raise session.cpuLimitCores or wait), until usage\\ndrops. The kernel cap is the enforcement of last resort: if the watcher lags, commands throttle,\\nthey never run free. With session.cpuLimitKill: true, a sustained breach also sends SIGTERM to the group’s\\nprocesses. The kill is reported as a budget action: the notice and the killed command’s result\\nboth state the command was stopped by the CPU budget, not that it crashed. macOS has no per-group CPU quota. There the budget is policy only: new commands are rejected\\nwhile the group is saturated, running members are reniced, and session.cpuLimitKill still\\nkills. Nothing throttles. The settings row and the startup warning state this, and the same\\nwarning appears on any platform where no backend works. A configured limit never fails silently. Changing session.cpuLimitCores mid-session takes effect on the next command: the live quota is\\nrewritten, and setting it back to 0 lifts it.","breadcrumbs":"CPU limits » How it is enforced","id":"154","title":"How it is enforced"},"155":{"body":"Cap a session to 2 cores and run a parallel build: # ~/.veyyon/profiles//agent/config.yml\\nsession: cpuLimitCores: 2 $ veyyon\\n> run make -j16 and watch the load make spawns sixteen compilers, but the whole tree shares two cores: the build takes roughly\\neight times longer than uncapped wall time would suggest, and the rest of the machine stays\\nidle. While the build runs flat out, another command is rejected: Refused to start a bash command: this session\'s CPU budget of 2 core(s) is saturated\\n(spawned commands used ~2.00 cores for the last 3s). New commands run again once usage\\ndrops below the budget. Fix: wait for the running command to finish, or raise\\nsession.cpuLimitCores. With session.cpuLimitKill: true, the same breach ends the build instead: Session CPU budget exceeded: limit 2 core(s), spawned commands used ~2.00 cores for 3s.\\nSent SIGTERM to 9 process(es) because session.cpuLimitKill is on. A command that just\\nstopped was killed by the CPU budget, not a crash.","breadcrumbs":"CPU limits » Example","id":"155","title":"Example"},"156":{"body":"Approvals Non-interactive mode Settings reference","breadcrumbs":"CPU limits » Related","id":"156","title":"Related"},"157":{"body":"You often need the agent to run a command that requires a credential. A deploy needs a token. A database query needs a password. If you paste the value into chat without protection, it can reach the model provider and any session export you create. Veyyon can keep the value away from the provider while the command still works. Some local surfaces can still contain it; those are named below.","breadcrumbs":"Secrets » Secrets","id":"157","title":"Secrets"},"158":{"body":"Secret protection is off by default. The quickest way to turn it on is to store a credential: storing one switches protection on and reports that it did, because a stored credential is only useful once the protection that substitutes it is running. To turn it on without storing anything, use /settings, or write it into config.yml: secrets: enabled: true The setting takes effect in the current session. Veyyon reloads environment variables, secrets.yml, and the vault when you toggle protection or run a /secret command. Moving to another working directory loads that project’s scope and drops the source project’s mappings.","breadcrumbs":"Secrets » Turning it on","id":"158","title":"Turning it on"},"159":{"body":"If the credential is already an environment variable, you have nothing to declare to keep its value out of provider requests. Veyyon treats an environment variable as secret when its value is 8 characters or longer and its name ends with, or has an underscore after, one of KEY, SECRET, TOKEN, PASSWORD, PASS, PASSPHRASE, AUTH, CREDENTIAL, PRIVATE, or OAUTH. That boundary matters, so read each keyword as a whole word rather than a substring: Detected Not detected DEPLOY_TOKEN TOKENIZER API_KEY SECRETIVE_THING KEY_FILE AUTHORIZED_USER GPG_PASSPHRASE PASSTHROUGH APIKEY, PRIVKEY PWD The exclusions are the point of the rule, not a gap in it. Obfuscation replaces every occurrence of a value, so detecting AUTHORIZED_USER would blank out that username wherever it appeared in your transcript. PWD is excluded for the same reason and more sharply: it is your current working directory, it exists in every shell, and detecting it would replace your paths with a placeholder in every message that mentions one. APIKEY and PRIVKEY need no keyword of their own, because KEY at the end of a name already matches them.","breadcrumbs":"Secrets » Your first secret","id":"159","title":"Your first secret"},"16":{"body":"Design goals Roles and profiles Repair","breadcrumbs":"Design and mechanisms » Mechanisms » Related","id":"16","title":"Related"},"160":{"body":"The keyword list is data, not code. Drop a file at either location and its keywords are added to the built-in ones: Level Path Profile /secret-env-keywords.yml Project /.veyyon/secret-env-keywords.yml keywords: - VAULTPASS - SCANSEED Your keywords follow the same boundary rule, so VAULTPASS matches VAULTPASS and MY_VAULTPASS and not VAULTPASSWORDLESS. A user file can only add. It cannot remove a built-in keyword, so a repository you clone cannot turn off detection of TOKEN for you. A file that exists but cannot be read or parsed stops startup, because carrying on would cover fewer variables than you wrote down. If a variable of yours is still not detected, do not assume it is covered: declare it in secrets.yml as shown below, or store it with /secret. So a shell that already has this: export DEPLOY_TOKEN=ghp_R2d2c3poIHRva2VuIGV4YW1wbGU needs no configuration for defensive protection. Start Veyyon and that value is\\nreplaced whenever it appears in provider-bound text. Environment detection does\\nnot give the agent a readable inventory name. When the agent must choose and\\nspend the credential deliberately, store the same value in the vault: /secret from-env DEPLOY_TOKEN That is the form you type in a terminal. Veyyon prompts for a name afterwards and generates one if you skip it. A client with no terminal, such as --print mode or an ACP editor, writes the name on the line as /secret from-env DEPLOY_TOKEN DEPLOY_KEY, because nothing there can prompt; see On a client with no terminal.","breadcrumbs":"Secrets » Adding your own keywords","id":"160","title":"Adding your own keywords"},"161":{"body":"Every occurrence of a known value is replaced before provider dispatch. This boundary covers messages, dynamic system prompts, tool descriptions and schemas, resumed assistant text, replay payloads, and nested model calls such as title generation, image analysis, memory summaries, and speech rewriting. The replacement happens from raw text before trimming, truncation, JSON serialization, or other lossy preparation. Veyyon resolves the live profile, project, environment, and vault runtime again for each physical provider attempt. This includes authentication retries, fallback models, delayed queues, compaction, commit analysis, evaluation, benchmarks, memory services, TTS, and image tools. A refresh cannot leave a retry using an old set of secret values. Provider fields that are authenticated or signed cannot be rewritten safely. If a live value appears in a signature, provider item id, encrypted reasoning block, or other opaque replay payload, the request is rejected with a value-free error. Structured fields are rewritten recursively, including JSON object keys. A rewrite that would collapse two keys into one is also rejected. Suppose a file you read contains this token: DEPLOY_TOKEN=ghp_R2d2c3poIHRva2VuIGV4YW1wbGU The provider receives a machine-keyed placeholder: DEPLOY_TOKEN=#0A1B2C3D4E5F678901234567# The placeholder is stable across restarts on the same machine. It contains a keyed HMAC rather than a load-order index, so seeing it does not give the provider an offline dictionary test for the value. A named vault entry instead uses its readable name, such as #GITHUB_TOKEN#, so the model can choose the right credential. The model is told two things about a placeholder: that putting one where a credential belongs is expected and works, and that it is opaque otherwise. It does not have the value and cannot request it. For named vault entries it is told one more thing, which credentials it currently has, covered under What the agent knows, and when.","breadcrumbs":"Secrets » What the model sees","id":"161","title":"What the model sees"},"162":{"body":"This is the part that makes the feature useful rather than merely defensive. The model can put a placeholder into a command, and veyyon substitutes the real value before the command runs. The model writes: curl -H \\"Authorization: Bearer #0A1B2C3D4E5F678901234567#\\" https://api.example.com/deploy The command that actually executes contains the real token. The substitution happens locally, after the model has produced the command and before the shell sees it. The model never learns the value, and the request still authenticates. The substituted command is not written down. Veyyon records one diagnostic entry per tool call so that a session interrupted mid-call can tell you on resume what was running, and that entry stores the placeholder form, not the substituted one. This matters because /share uploads the session file and backups copy it. What the command prints is a separate question, covered under What this does not protect.","breadcrumbs":"Secrets » Using a secret in a command","id":"162","title":"Using a secret in a command"},"163":{"body":"Store GITHUB_TOKEN today, quit, and start a new session tomorrow. Ask for your open pull requests, and the agent writes #GITHUB_TOKEN# into the curl command without you mentioning the credential again. It can do that because the system prompt contains an inventory: the placeholders the agent is able to spend at that moment, listed by name and sorted. The inventory is built from the live secret runtime rather than from the conversation, and that is the whole reason it survives a restart. The vault is stored on disk; a conversation is not. Knowledge kept only in the transcript went away with the transcript, while the credential it described stayed exactly where it was. The inventory holds names, and nothing else. No value appears in it in any state, and the agent has no way to request one. Around the list the agent is told what the list is for: write the placeholder where the credential belongs, the real value is substituted locally just before the tool runs, and a name that is not listed is not available. Only vault entries are listed, because only they have readable names. A value detected in your environment, or declared in secrets.yml, becomes a machine-keyed placeholder instead, which the agent meets where the value would have appeared rather than in a list. When protection is off, or when nothing is stored, the section is absent rather than empty. An empty heading reads as “you have no credentials”, and that is a different statement from “this session cannot spend any”. Removing the last credential takes the whole section away again, heading included. Four moments, and what the agent learns at each: At session start, or on resume. The inventory, rebuilt from whatever the vault holds right then. Nothing else is needed. A credential you stored last week does not have to be introduced again. When you add one. The inventory is rebuilt so the new name is in it, and the agent is told directly, in that turn, that the credential now exists and where its placeholder goes. When you remove or extend one. Both again: the inventory is rebuilt, and the agent is told what changed. A revocation states the placeholder is revoked and must not be used. A fresh lifetime states the credential is still available under the same placeholder. Neither notice quotes a lifetime, because a duration written into the history is wrong a minute later, and your terminal already shows you the exact time left. When a lifetime runs out on its own. Substitution stops at the deadline itself, not a moment after, and the name leaves the inventory on the next rebuild. There is no notice on this path, because no command ran and so there is no turn to put one in. You are warned twice before it happens, which is covered under Lifetimes. In none of these does the agent learn a value.","breadcrumbs":"Secrets » What the agent knows, and when","id":"163","title":"What the agent knows, and when"},"164":{"body":"Dropping the name from the inventory would be the quieter design, and on paper it conveys the same thing. It does not work. Noticing that something has stopped being present in a long prompt is the kind of thing a model reliably fails at, so it goes on writing a placeholder that worked ten minutes ago. A revoked placeholder cannot reach a tool during the running process. Veyyon remembers the exact name it retired and rejects the call before execution: Stored secret #STRIPE_TEST_KEY# is no longer available. Store the credential again and update the command. Text that was never a live credential, such as #TODO#, remains ordinary input. The revocation notice gives the agent the same fact before it tries the call; the refusal is the backstop when the agent keeps using stale history. For the same reason, the removal notice is delivered even when secret protection is off. The add and extend notices are not: with protection off there is no working placeholder to advertise. A revoked one is different, because it is already sitting in the agent’s history, and the agent needs to hear that it stopped working whatever the setting is. Turning secret protection off also marks every name advertised in the running\\nprocess as retired for tool execution. Redaction keeps using the same readable\\nplaceholders in provider-bound text, but a stale tool call cannot spend or send\\nthem after expansion has been disabled.","breadcrumbs":"Secrets » Why a removal is stated rather than left to the list","id":"164","title":"Why a removal is stated rather than left to the list"},"165":{"body":"Environment detection covers credentials that are already in your shell. For anything else, hand it to the vault. A vault entry is encrypted on disk, has a name, and expires.","breadcrumbs":"Secrets » The vault: storing a credential with /secret","id":"165","title":"The vault: storing a credential with /secret"},"166":{"body":"In a terminal, everything you type after /secret add is the credential. There is no name to invent first: /secret add ghp_R2d2c3poIHRva2VuIGV4YW1wbGU Veyyon takes the value off the line and prompts for what to call it, in a field that shows what you type, because a label is not a secret: Name this secret (optional). The model spends it by writing #NAME#. Leave empty to have one generated.\\n> enter submit esc cancel Press Enter on the empty field and veyyon generates a name, SECRET_1 and upwards. Type one and it is cleaned up and uppercased for you, so github token becomes GITHUB_TOKEN. Press escape and nothing is stored at all: Cancelled. Nothing was stored. That order is deliberate. The credential is what you came to store, so nothing stands between you and storing it, and the name is prompted afterwards where you are free to skip it. Pasting into a hidden field /secret add with nothing after it opens a field that shows nothing as you type: /secret add Paste the secret value here. You can name it afterwards.\\n> •••••••••••••••••••• the value, not a name · hidden as you type, stored encrypted · enter submit esc cancel Your composer is cleared before the field opens, so the value never enters the input buffer and never reaches your scrollback. Press escape and nothing is stored. Submit an empty field and nothing is stored either, and veyyon reports it rather than storing an empty credential. The name field follows, the same one as above. Reading it out of the environment If the credential is already an environment variable, read it from there and type nothing: /secret from-env GITHUB_PAT This is the recommended form, because the value never enters the input buffer and never reaches your scrollback. from-env is a command of its own, alongside add, and its first word is the name of the variable. The name field follows here too, so a bare from-env GITHUB_PAT is the whole line you need. You may write the name on the line instead, and a lifetime and a vault after it: /secret from-env GITHUB_PAT DEPLOY_KEY 7d project The variable comes first and the name second, both by position, which is what lets a secret be called PROFILE or NEVER. The lifetime and the vault come after in either order, because no word is both: a vault is one of profile, project and global, and a lifetime is 30m, 12h, 7d, 2w, never, or anything else beginning with a digit. env is the second spelling of from-env. A value on the command line stays in your scrollback The one-paste form is on screen until you clear the terminal. Veyyon reports it rather than leaving you to work it out: The value was typed on screen, so it is in your scrollback. Use /secret from-env next time to avoid that. Every exact /secret command shape, including malformed input, is excluded from persistent editor history. This prevents the command from being recovered with the Up key or written to history.db. It cannot erase terminal scrollback that was already rendered. The line is kept byte for byte from its first non-space character to its last, so a passphrase may contain spaces and no part of the value is trimmed away. Use the hidden field or from-env when you would rather the value were never on screen at all. A command comes first The first word of a /secret line is a command or it is nothing. The commands are add, from-env, list, rm, clear, rename, value, scope, copy, extend, log, discard and help, plus the second spellings env, remove, delete, wipe, purge, empty, reset, name, replace, move, renew and audit. A line that begins with anything else is rejected, and nothing is stored: Unknown /secret command. Nothing was stored. If what followed /secret was a credential, it is now in\\nyour scrollback and was never protected, so rotate it and store the new one with /secret add. The refusal never repeats the word it rejected, because that word is often the credential itself. Earlier versions read an unrecognised first word as the credential, so /secret ghp_... stored it. That saved one word and cost three mechanisms: every command had to be reserved in advance so it could not be mistaken for a value, a credential beginning with a reserved word collided with the command, and the collision needed an escape spelling of its own. With the value living behind add there is one place a value is read and none of that is needed. /secret add list of words that is really a passphrase stores that line byte for byte, first word included. A command stays a command however much follows it, so a malformed one is rejected rather than quietly stored: /secret log 50 is a log with an unreadable argument, not a new secret called SECRET_1. Every argument is a plain word There are no options anywhere in /secret. Nothing is spelled with a dash, so there is nothing to look up and nothing to get in the wrong order. A word means something because of where it sits, or because it belongs to a set that cannot be anything else: How a word is read Where its position rm , rename , scope , extend , from-env a set of three words a vault: profile, project, global a lifetime shape 30m, 12h, 7d, 2w, never, or any word beginning with a digit a whole number the record count on log Position wins wherever the two could disagree, so a secret really called PROFILE is removed by /secret rm PROFILE and one called NEVER has its lifetime extended by /secret extend NEVER 7d. Where meaning is taken from a word’s shape instead, the sets provably cannot overlap: a secret name may not begin with a digit, so /secret log 50 is fifty records and /secret log GITHUB_TOKEN is one credential’s uses; and a name may not contain a hyphen, so the from-env in /secret value from-env is never a name. The spellings --, --from-env, --ttl, --scope, --limit and --name were the earlier grammar and are rejected, stating the plain word that replaced each one. They are rejected only as the first word after add, so a credential that merely begins with dashes is still stored byte for byte: /secret add -----BEGIN OPENSSH PRIVATE KEY----- -- was the worst of them. A slash command has no options to end, so it meant nothing here and had to be looked up, and storing it on the front of a credential produces a secret that expands into requests failing somewhere else entirely.","breadcrumbs":"Secrets » Storing a credential","id":"166","title":"Storing a credential"},"167":{"body":"Whichever form you used, veyyon confirms with the name it filed the credential under and the placeholder the model will write: Stored GITHUB_TOKEN in the profile vault, 1d left.\\nThe model sees #GITHUB_TOKEN# and never the value. Write that placeholder where the credential goes. Storing over a name that already exists is called a replacement rather than a store. That write is how you rotate a credential, and it is also what a fumbled name does, so it is never reported as if nothing had been overwritten: Replaced GITHUB_TOKEN in the profile vault, 1d left.\\nThe previous value is gone. #GITHUB_TOKEN# now spends the credential you just stored. The agent is told at once that a credential exists and that it should write #GITHUB_TOKEN# where the value belongs. It is never given the value and cannot request it. It also keeps knowing after this session ends, because the inventory in the system prompt is rebuilt from the vault rather than remembered from the conversation. See What the agent knows, and when.","breadcrumbs":"Secrets » What you are told when it is stored","id":"167","title":"What you are told when it is stored"},"168":{"body":"Every verb below works in a terminal and on a client that has none. The value forms above are the only part of /secret that depends on where you are typing. Command What it does /secret list one row per credential: placeholder, scope, time left /secret rename relabel it, keeping the value, the creation time and the deadline /secret value replace the value, keeping the name and the deadline /secret scope project move it to another vault /secret copy put #NAME# on the clipboard, never the value /secret extend 7d give it a fresh lifetime, measured from now /secret rm [global] revoke it /secret clear profile remove every credential in one vault, stating what it removed /secret clear everywhere remove every credential in all three vaults /secret log [] [50] which credentials were spent, and where /secret discard project move aside a vault file that cannot be read /secret help every form, on the surface you are on value is how you correct a credential. It keeps the name, the scope, the creation time and the expiry, so a token pasted with one character missing does not have to be revoked and stored again: storing it again mints a new name while every prompt in the session still spends the old placeholder, and it re-dates the entry, so a secret with two days left would come back with the default lifetime. The field it opens is hidden as you type, and /secret value from-env reads the replacement out of the environment instead. copy copies the placeholder and only the placeholder. #GITHUB_TOKEN# is the thing you paste into a prompt; copying the value would be the disclosure you stored the credential to avoid. clear empties one vault. It requires the scope because there is no default: the vault is three files, project overriding profile overriding global, and the copy you can reach is the one that gets spent, so a guess would empty whichever happened to be in front and leave the other two full. It reports the placeholders it dropped. A name that a wider vault still holds is reported as removed but not as revoked, because #NAME# goes on expanding to that copy. wipe, purge, empty and reset are the same command. clear everywhere empties all three. It reports every scope in one report, including a scope that held nothing, because the question it answers is whether anything is still stored. all, everything and every are the same word. No other verb takes it: “all of them” is not a place to store a secret, not a destination to move one to, and not a vault file to set aside. scope rejects a move onto a name the destination vault already holds, rather than overwriting it. It moves the time REMAINING rather than the original lifetime, so moving a secret cannot lengthen its life. The copy is written to the destination before the source is removed, so an interrupted move leaves two copies you can see rather than none. No verb prints a value: not on a row, not truncated onto one, not behind a key. A value put into the vault has stopped being visible, and the surface most likely to end up in a screenshot is the one that must not break that. Every change reloads the live secret runtime, so a credential you revoke stops being spendable in the session you are sitting in rather than at the next restart. A reload that fails is reported rather than swallowed, because the vault write is already durable and you are the only one who can decide what to do about the gap. Names are never completed. The dropdown after /secret offers verbs and nothing else. Completing a stored name would put part of your vault on screen on a keystroke, and accepting one would type a name onto a line whose first word decides between a command and a credential. /secret list is where names are read.","breadcrumbs":"Secrets » Managing what you stored","id":"168","title":"Managing what you stored"},"169":{"body":"/secret list ends with what the session is masking that no name can reach. This is the counterpart to the composer’s N masked chip: both read one counter, so the number below the table and the number above the prompt are the same number. 2 values masked in what is sent, detected rather than declared. The agent cannot spend them: only a stored secret has a placeholder. From: /home/dev/project/.veyyon/secrets.yml, DEPLOY_TOKEN. To stop masking one, unset the variable or narrow the keywords in env-keywords.yml. Each of these values reached the session through the environment or through secrets.yml, so it has no name and no placeholder the agent can write. What it has is a place it came from: the variable name, or the path of the file that declared it. That label is not a name. It makes the value findable and grants no #NAME# expansion. One credential exported into the environment and also declared in a file is one masked value with two places to look, and both are named. A value handed in by an SDK caller with no label at all is counted, and the count of those is stated rather than left as the difference between two numbers.","breadcrumbs":"Secrets » Finding what is masked and not stored","id":"169","title":"Finding what is masked and not stored"},"17":{"body":"Argot is a per-project token shorthand codec for model interactions. A project dictionary maps short handles to recurring text strings such as paths, import roots, and build commands. The model outputs the handle, and the runtime expands the handle to the full string before passing arguments to tools, rendering UI, or appending to transcripts. For configuration and flags, see Save tokens with project shorthand.","breadcrumbs":"Design and mechanisms » Argot » Argot","id":"17","title":"Argot"},"170":{"body":"--print mode and an ACP editor have no field that can hide what you type, so they cannot accept a credential you type at all. Every command is the same there, and from-env is the way in: /secret from-env GITHUB_PAT GITHUB_TOKEN The name is required on the line here, because nothing on that surface can prompt for it afterwards. add is still listed, with the reason it does not work and the command that does, rather than being hidden and answering only with an error: /secret from-env store the value of an environment variable\\n/secret add not here: a client cannot hide typing, so use from-env An inline value is rejected, because that surface keeps its requests in a history you cannot clear: This client rejects an inline credential, because the line containing it is retained in the client\'s own\\nrequest history. Nothing was stored. Read the value out of the environment instead:\\n/secret from-env MY_TOKEN . The refusal repeats neither word after add. Nothing distinguishes a name followed by a credential from a credential whose first word looks like a name, so a message that quoted the part it took for the name would sooner or later quote the credential. list prints a table: 2 active secrets. The agent spends one by writing its placeholder; the value is never shown. PLACEHOLDER SCOPE EXPIRES STATUS #GITHUB_TOKEN# profile 6d left #PROD_DB_PASSWORD# project 1d left expires soon\\nExtend one before it lapses: /secret extend 7d. No part of any value appears there. A prefix of a credential is still a disclosure, and one on screen is one in a screenshot. The STATUS column and the closing line appear only when at least one entry has crossed a warning threshold, so a table of healthy entries is one column narrower. A cell reads past halfway or expires soon, and those are the same two thresholds that raise the warnings described under Lifetimes. The table and the warnings cannot disagree about which entry is in trouble. With nothing stored, list reports it and shows the one entry form that surface has, rather than printing an empty table. Removing and extending each notify the agent of what changed, whichever surface you did it from, so a placeholder you revoked stops being used instead of arriving at a command as literal text. See What the agent knows, and when.","breadcrumbs":"Secrets » On a client with no terminal","id":"170","title":"On a client with no terminal"},"171":{"body":"Sometimes a vault file survives on disk and stops being readable: a disk filled up mid-write, a\\nbackup tool restored half of it, a sync client merged two copies. Veyyon reports which scope, what\\nit means, and what to run: Your profile vault at /home/you/.veyyon/profiles/work/agent/vault.json exists but could not be\\nread, so it was skipped and the secrets stored in it are unavailable for the rest of this session:\\ntheir placeholders will NOT expand. Every OTHER scope loaded normally, and masking of known secret\\nvalues is unaffected. The vault is encrypted, so a hand edit cannot repair it: run /secret discard\\nprofile to move the unreadable file aside. Then store the secrets it held again. The reason it\\ncould not be read was A vault can also fail in a way Veyyon cannot step around, where nothing in it can be read: the key\\nis gone, the key is not the one the vault was sealed with, or the file is truncated. The session\\nstill starts, and reports it: Your vault could not be read, so this session started WITHOUT it: nothing you have stored is\\navailable, and every #NAME# placeholder it held will be rejected rather than sent as literal text.\\nMasking of secrets from your environment and secrets.yml is unaffected and still running.\\nAffected: project (/home/you/work/repo/.veyyon/vault.json). Run /secret discard project to move\\nthe unreadable file aside. Then store the secrets it held again. The reason it could not be read\\nwas Both notices name one command, because a notice raised by the vault loader cannot determine which client\\nis about to print it, and discard runs on all of them. It moves the file aside rather than deleting\\nit: the bytes still hold a real credential under a live key, and a repair that destroyed them would\\nbe worse than the fault it was fixing. Read the second sentence of the second notice carefully, because it is the part that keeps a broken\\nvault from becoming a leak. A scope Veyyon could not read is treated as unreadable, never as empty. #NAME# in a prompt is rejected rather than passed through as the literal text #NAME#, and Veyyon\\nwill not act as though you had stored nothing. The session starts because the repair is a command inside it. If a vault that would not open also\\nstopped Veyyon from launching, the only way out would be deleting the file by hand, which is the\\none thing an encrypted store exists to stop you doing casually. Either route runs the same repair, and prints the same result: Moved the unreadable profile vault to\\n/home/you/.veyyon/profiles/work/agent/vault.json.unreadable-1753660800000-8e3a8d58, so that scope\\nworks again. The file still holds your sealed entries, so re-add the secrets it held rather than\\nassuming they are gone. The scope keeps working from there. You can store secrets in it again immediately, and the other two\\nscopes were never affected: only the file you named moved. Your file is moved, not deleted. The name it moved to is in the message because that file is the\\nonly route back to what it held. It is still encrypted with a key that is still on disk, so if the\\ndamage is a truncated tail, the entries before the damage are still in there. Veyyon will not destroy\\na credential store to make itself usable again, so the cleanup is yours to do once you are sure you\\nno longer need it. You have to name the vault. Every other command that takes one defaults to profile, because\\nthere it chooses where to put something and /secret list shows you the result. Here it chooses a\\nfile to move aside, so a default would let a bare /secret discard move a working vault out from\\nunder the session you are sitting in. A bare invocation is rejected and states the word to add. Two things the repair refuses, both on purpose: A scope that reads normally. This is not a second way to delete secrets. Revoke the entry\\ninstead with /secret rm , which reports what it removed. The check happens at the moment\\nyou run the repair rather than from the earlier warning, so a file that was fixed in between is\\nleft alone. A scope that shares its file with another scope. If your profile directory is your config\\nroot, the profile and global vaults are one file, and moving it aside as one would take the other\\nwith it. The refusal states the other scope so you can decide which you meant. The repair is reachable from every client, not only the terminal, because a broken vault is most\\nlikely to turn up in a headless run.","breadcrumbs":"Secrets » When a vault file cannot be read","id":"171","title":"When a vault file cannot be read"},"172":{"body":"Every entry expires. The default is one day, which you can change in /settings under Secret Lifetime. That setting is the whole answer for /secret add, because the line after add is the credential\\nand there is no room on it for anything else. To give such an entry a different lifetime, store it,\\nthen run /secret extend 30m. The lifetime you name there is measured from now, not from\\nwhen the credential was stored. from-env takes one on the line, after the variable and the name: /secret from-env DEPLOY_KEY DEPLOY_TOKEN 30m\\n/secret from-env SIGNING_KEY SIGNING_TOKEN never project Lifetimes are written the same way everywhere: 30m, 12h, 7d, 2w, or never. Weeks are accepted and reported back in days. A lifetime and a vault may be given in either order, because no word is both. You are warned before a lifetime runs out, once at the halfway point and again near the end: Warning: secrets: #DEPLOY_KEY# expires soon, 2h left. Extend it with\\n/secret extend DEPLOY_KEY 7d, or it will be deleted. The remedy is one command, and it runs wherever the warning is read: a notice raised while the vault\\nis loading cannot determine which client is about to print it. The thresholds are fractions of the lifetime rather than fixed times, so one rule fits every entry. A one-day secret is mentioned after twelve hours; a ninety-day secret is mentioned on day forty-five, not on day eighty-nine. Each warning states the command that prevents expansion from being revoked. Expiry revokes substitution immediately. If an expired secret merely stopped being obfuscated, its value could flow to the model provider when protection lapsed. Veyyon instead removes the in-memory expansion mapping and keeps a forward-only redaction tombstone. The deadline is enforced when the credential is used, not only when a session starts. A session left open over a weekend stops substituting a one-day secret on the day it expires and reports both the runtime and persisted state: Warning: secrets: #GITHUB_TOKEN# has expired and its in-memory expansion has been\\nrevoked. Its encrypted value has not yet been deleted from the vault; a successful\\nvault refresh will prune it. Store it again with /secret from-env if you still\\nneed it, or /secret from-env GITHUB_TOKEN in a client with no terminal. In a terminal the name field that follows is where you type GITHUB_TOKEN to get the same\\nplaceholder back. The hot-path expiry check does not write to the vault. The encrypted entry remains on disk until the next successful vault refresh prunes it. It remains encrypted and cannot be expanded after the deadline. If a command still refers to an expired secret, Veyyon rejects it before the tool starts and reports only the retired placeholder. It does not send an empty header or the literal placeholder to a remote service. Old transcript text containing the raw value remains covered by the forward-only redaction tombstone for the life of the same working-directory runtime.","breadcrumbs":"Secrets » Lifetimes","id":"172","title":"Lifetimes"},"173":{"body":"An entry belongs to one scope and is invisible from the others: Scope Where it lives Use it for profile (default) the active profile’s agent directory credentials for one line of work project /.veyyon/vault.json, kept out of your commits credentials for one repository global ~/.veyyon/vault.json credentials you want everywhere A credential you store in a terminal goes to the profile vault. Scope is an option, the argument line there is the credential, so there is no place on it to put one. Profile is the default because that is usually the boundary you want: a credential you use for one kind of work should not be reachable from a session you opened in another profile. A credential already stored can be moved with /secret scope project. To file one somewhere else as you store it, name the scope on a client that takes a verb and a value on one line: /secret from-env SCAN_TOKEN SCAN_TOKEN project When the same name exists in more than one scope, /secret list reports it. The table stays one row\\nper name, because one row per name is what the agent can spend, and a sentence underneath states the\\ncopies it is not spending: 2 active secrets. The agent spends one by writing its placeholder; the value is never shown. PLACEHOLDER SCOPE EXPIRES #SHARED_TOKEN# project 24h left #SOLO_TOKEN# profile 24h left #SHARED_TOKEN# is also stored in the global vault, shadowed by the project one. Only the project copy is spent. Remove it with /secret rm SHARED_TOKEN global. That copy is inert, not gone. It is still on disk, still decryptable, and it becomes the live one\\nthe moment the copy in front of it is removed. Before the list mentioned it, the only way to find\\nout was to remove the copy in effect and read what the removal told you, which is late: you learn\\nabout a credential at the moment it starts being spent. The narrowest copy is the one that wins, and a removal that specifies no vault takes it. Removing that\\ncopy uncovers the next one out, and the command reports that too: /secret rm shared-token Removed SHARED_TOKEN from the project vault. A profile secret of the same name was underneath it,\\nso #SHARED_TOKEN# still spends a credential, now that one. Run /secret rm SHARED_TOKEN profile to\\nremove that one too. That second sentence is the part that matters, because the placeholder keeps working. Without it\\nyou would read a removal, assume the name was dead, and leave a live credential reachable under a\\nname you believe you revoked. The agent is told the same thing, so it does not treat the name as\\nrevoked either. To take a particular copy rather than the one in effect, name its scope: /secret rm SHARED_TOKEN profile Naming the scope of a copy that is already shadowed removes it without changing what the\\nplaceholder spends, and the command reports that rather than implying something changed. A vault word belongs to from-env, rm, clear, scope and discard. On a command that does\\nnot read one it is a word that fits no slot, and it is rejected rather than ignored: /secret extend GITHUB_TOKEN global /secret extend cannot read the word in position 2, and a word that would be ignored is rejected\\nrather than dropped silently. That refusal exists because the alternative is worse than an error. An accepted-and-ignored vault\\nword on extend reads as “the global copy was given a fresh lifetime” when what actually happened\\nis that the copy in effect was re-dated and the others were left alone. The same rule covers a\\nlifetime handed to rm and a number handed to list: a word veyyon would drop is a word you meant\\nsomething by. The refusal states the position and never repeats the word, because the realistic slip is muscle\\nmemory for add under a different command, which puts the credential itself in that position.","breadcrumbs":"Secrets » Scope","id":"173","title":"Scope"},"174":{"body":"Vault files use AES-256-GCM. Each write uses a fresh 12 byte nonce and the full 16 byte authentication tag. The key is a 32 byte file at ~/.veyyon/vault.key, created on first use. It never lives inside a project directory. On POSIX, the key is mode 0600. Its directory must be owned by you and not writable by another user. On Windows, Veyyon applies and verifies a protected owner-only ACL. Existing vault files receive the same platform permission checks before they are read. A project-scoped vault lives inside the repository you are working in, so Veyyon keeps it out of your commits. The first time it stores a project secret, it writes .veyyon/.gitignore covering vault.json and the vault.json.unreadable-* file that a discarded vault is renamed to. If that file already exists, Veyyon adds the two rules and leaves your own lines alone. Only the vault is ignored, so anything else you keep in .veyyon/, such as prompt templates, stays trackable. Commit the generated .veyyon/.gitignore along with the rest of your project. Committing a vault would not expose the credentials directly, because the ciphertext is unusable without the machine key. It would still put a credential store in your history, and nobody who clones the repository can open it, including you on another machine. A vault is not a portable backup. The authenticated location includes the semantic scope, canonical path, and physical scope-directory identity. If you move or recreate that directory, store those entries again. Updates use a synchronized owner-only temporary file. Kernel no-replace and exchange operations publish the synced inode without overwriting a destination that appeared after the last check. Veyyon holds the scope directory open during the transaction, so replacing the lexical parent cannot redirect the read or write. Veyyon rejects symlinks, hard-linked files, directories, devices, insecure permissions, and paths whose resolved parent crosses the requested scope. It also rejects ciphertext copied to a different scope or physical directory. The sealed descriptor is limited to 8 MiB before it is read into memory. Writes enforce a separate 6,291,402-byte encoded plaintext limit before serialization, encryption, or Base64 expansion. A legacy version 1 envelope is rejected because it is not bound to its scope and path. Store those entries again so they use the current authenticated format. These failures are deliberately loud: A vault file present with no readable key stops the session. It is never treated as empty. A vault whose nonce, ciphertext, authentication tag, or bound location changed is rejected. An unsafe directory, symlink, non-regular path, hard link, or insecure permission is rejected with the path and fix. What this encryption does not protect against is someone who is already running as you. The key is readable by your own account by design. If you need to defend against a compromised account, use a hardware token or an external secret manager.","breadcrumbs":"Secrets » Encryption, and what it does not do","id":"174","title":"Encryption, and what it does not do"},"175":{"body":"Hiding a value from the provider bounds what the agent could see. It does not show what the agent did with what it could. The expansion log answers that, and /secret log prints it: 3 most recent use(s), oldest first: 12m ago bash #GITHUB_TOKEN# {\\"command\\":\\"curl -H \'Authorization: Bearer #GITHUB_TOKEN#\' https://api.github.com/user\\"} 4m ago bash #DEPLOY_KEY# {\\"command\\":\\"scp -i #DEPLOY_KEY# build.tar deploy@host:/srv\\"} just now bash #GITHUB_TOKEN# #DEPLOY_KEY# {\\"command\\":\\"./release.sh --token #GITHUB_TOKEN# --key #DEPLOY_KEY#\\"} One use is when it happened, which tool received it, which placeholders were substituted, and the command as the model wrote it. The last twenty are shown; /secret log 50 requests more. Narrowing it to one credential A name answers the question worth asking just before a revoke, which is what stops working: /secret log GITHUB_TOKEN Uses of #GITHUB_TOKEN#:\\n2 most recent use(s), oldest first: 12m ago bash #GITHUB_TOKEN# {\\"command\\":\\"curl -H \'Authorization: Bearer #GITHUB_TOKEN#\' https://api.github.com/user\\"} just now bash #GITHUB_TOKEN# #DEPLOY_KEY# {\\"command\\":\\"./release.sh --token #GITHUB_TOKEN# --key #DEPLOY_KEY#\\"} The whole log is read, then narrowed to that credential, and only then cut to the limit. So /secret log GITHUB_TOKEN 20 means the last twenty uses OF that credential, not the last twenty records of which some happened to be it. The two words are told apart by shape rather than by position, and may be given in either order: a limit is a whole number and a secret name may never begin with a digit, so no word is both. The heading states the credential even when nothing follows it, because an empty log for one secret and an empty log altogether support opposite conclusions: the first states this credential has never been spent, the second that nothing has. An empty log, and a log that is off An empty log prints the file it is empty at, so it reads as “nothing has happened here” rather than “something failed to load”: No secret has been used yet. The log is ~/.veyyon/profiles/work/secret-audit.jsonl. A log that is switched off prints the setting instead: Secret use is not being recorded, so there is no log to show. Turn on \\"Record Secret Use\\" in\\n/settings (secrets.auditLog) to start recording. Nothing recorded and nothing being recorded support opposite conclusions about whether a credential was spent, and as an empty list they are the same picture. The log belongs to the profile rather than to one session, so two veyyon windows in the same profile append to the same file. When the records you are shown come from more than one, the output reports it: These records come from 2 sessions sharing this profile\'s log. Without that line the rows read as one session’s history, and you would count uses another window made. The recorded command holds the placeholder, not the value, and that is a property of how the record is built rather than a promise about care taken. Veyyon writes the arguments as they were before substitution, which is the form in which every secret is still a placeholder, so there is no redaction step that could be got wrong and no way for a value to reach the file. Placeholder discovery follows the same recursive string and object-key walk as command expansion. The reader escapes terminal control characters in records, paths, and notices before display. Hard-linked log files are rejected, and generation reads check the 2 MiB bound before allocating a buffer. Recording is on by default and writes to secret-audit.jsonl in your active profile’s directory. Turn it off under Record Secret Use in /settings: secrets: auditLog: false The file is mode 0600 and lives in the profile rather than the project. If veyyon cannot append to it, it reports it and the command still runs. The value is still protected either way. At two megabytes, roughly ten thousand uses, the log is atomically moved to secret-audit.jsonl.1 and a fresh one is started. A cross-process lock covers the size check, rotation, append, and read snapshot, so two sessions cannot overwrite a generation or exceed the record cap at the boundary. Oversized rows bound every field and report how many placeholder references were omitted. Both generations are read, so a report requested right after a rotation still fills up.","breadcrumbs":"Secrets » Seeing which credential was used where","id":"175","title":"Seeing which credential was used where"},"176":{"body":"Environment variable detection covers the common case. For anything else, list entries in a secrets.yml file. Two locations are read: Level Path Use for Profile /secrets.yml Credentials for one line of work Project /.veyyon/secrets.yml Credentials specific to one repository Both levels are read and merged. A project entry with the same content as a profile entry replaces it, so a repository can override a declaration without duplicating the rest of the file. A minimal file protects one literal value: - type: plain content: sk-proj-abc123def456 A regex entry protects anything matching a pattern, which is how you cover credentials you have not seen yet: - type: regex content: \\"AKIA[0-9A-Z]{16}\\" Patterns always scan globally. You do not need the g flag. Veyyon rejects regexes that can make no progress, use sticky matching, or contain conservatively detected catastrophic-backtracking forms. Replacement changes only the exact matched span. Equal text outside the regex context is left alone.","breadcrumbs":"Secrets » Declaring secrets yourself","id":"176","title":"Declaring secrets yourself"},"177":{"body":"Each entry chooses what happens to the value. obfuscate, the default, is reversible. The value becomes a placeholder on the way out and the placeholder becomes the value again on the way back in. Use it when the agent needs to work with the credential. replace is one way. The value is swapped for a fixed or generated string and nothing restores it. Use it when the agent has no business using the credential at all and you only want it out of the context: - type: plain content: hunter2 mode: replace replacement: \\"********\\" A generated replacement is derived with the machine placeholder key, so it does not expose a cross-machine dictionary oracle. A custom replacement cannot look like a named or machine-keyed placeholder. That restriction prevents one-way text from becoming a request to expand a live credential.","breadcrumbs":"Secrets » The two modes","id":"177","title":"The two modes"},"178":{"body":"obfuscate mode replaces every occurrence of the value. A three-character secret would blank out fragments of unrelated words, so short values are not obfuscated. Veyyon rejects them rather than ignoring them. A plain obfuscate entry under 8 characters stops startup with an error stating the entry and the fix. This is deliberate: a session that starts cleanly while sending your declared secret to the provider in plain text is worse than a session that will not start. The fix is mode: replace, which is one way and has no minimum. The same floor applies to a credential you store in the vault, and the field that takes the value applies it there. Type something shorter than 8 characters into the hidden field and it is rejected as you leave it, while the value is still in front of you, rather than after you have also named the secret. A regex match under the floor behaves differently. A short match usually means the pattern reached into ordinary prose, so the match is skipped and the over-matching pattern is reported once. If short matches really are secret, declare it on the entry: - type: regex content: \\"\\\\\\\\b[0-9]{6}\\\\\\\\b\\" minLength: 6 A malformed or unreadable secrets.yml also stops startup, and so does a single entry inside it that is not a valid declaration. A mistyped type:, a missing content:, or minLength on a plain entry each name the entry number and the fix: Refusing to start: 2 entries in /home/you/.veyyon/secrets.yml are not valid secret\\ndeclarations, and skipping them would leave the values they declare unprotected. - entry 0 has type \\"plaintext\\", which must be \\"plain\\" (an exact value) or \\"regex\\" (a pattern). - entry 3 sets minLength, which applies to regex entries only. A short plain secret needs \\"mode: replace\\", which is one-way and has no minimum. Every problem in the file is listed at once, so you fix them in one pass rather than finding the next one on each restart. A missing file does not stop startup, because nothing was declared. Unknown fields are errors too. A misspelled replacement, a plain-only field on a regex entry, duplicate flags, or an option that conflicts with the entry type cannot be silently ignored.","breadcrumbs":"Secrets » The 8-character minimum","id":"178","title":"The 8-character minimum"},"179":{"body":"Two kinds of problem can arise, and veyyon treats them differently on purpose. A problem that would mean a credential reaches the provider stops the session. A declared obfuscate entry under the minimum, a vault file whose key is missing, a secrets.yml that cannot be parsed: each of these is a refusal with the entry and the fix named. A session that starts cleanly while sending your secret out in plain text is worse than one that will not start. A problem that leaves protection intact but degrades something appears in your session as a warning, prefixed with the subsystem that raised it: Warning: secrets: pattern matched a 3-character value, under this entry\'s 8-character floor.\\nSet \\"minLength\\" on the entry if short matches are real secrets, or tighten the pattern. An over-matching pattern is the usual example. It is discovered while obfuscating a message rather than at startup, so it cannot be a refusal, and it is worth seeing because you cannot otherwise tell a working pattern from one that is quietly reaching into prose. In --print mode and other non-interactive clients the same warnings go to stderr, so a scripted run does not lose them. Nothing important goes only to the log file. That was the previous behaviour and it amounted to silence: the log has no console output by default, and nobody opens it.","breadcrumbs":"Secrets » When something cannot be protected","id":"179","title":"When something cannot be protected"},"18":{"body":"A handle consists of a sigil prefix followed by an identifier: The default sigil is §. Identifiers use lowercase ASCII letters, digits, and underscores. Per-project sigils are configured in the dictionary’s sigil field. When the model emits §build, the runtime replaces §build with the configured string before executing the command in the shell.","breadcrumbs":"Design and mechanisms » Argot » Shorthand format","id":"18","title":"Shorthand format"},"180":{"body":"Destination Sees the real value? Model provider No, a placeholder Local session transcript It can. User and tool text is kept locally as written. A session you /share No, a placeholder The vault file on disk No, encrypted The secret-use log No, a placeholder secrets.yml on disk Yes, it is a plain file you wrote Your terminal Only a type: regex pattern you declared in secrets.yml. A vault entry, a detected environment variable, and a plain declaration stay masked on screen. A command the agent runs Yes, substituted before execution The provider boundary is applied again whenever a local transcript is sent. Resuming a session can restore placeholders for display without giving the resumed raw text a path back to the provider. Changing the working directory is transactional. Veyyon loads the destination runtime before committing the move, and restores both the old directory and old runtime if loading fails. A resumed session or persisted subagent starts from its recorded directory before loading project-scoped secrets.","breadcrumbs":"Secrets » Where the value goes","id":"180","title":"Where the value goes"},"181":{"body":"Be clear about the boundary. The two stores differ on disk. Vault entries added with /secret are encrypted. secrets.yml is a plain file: it holds declarations you wrote, in the clear, and anyone who can read it has those credentials. If a value needs to be encrypted at rest, put it in the vault rather than in secrets.yml. A command the agent runs receives the real value. So a command that prints the credential prints it for real. Its output is obfuscated again before it goes back to the model, but it reached the process, and anything that process wrote elsewhere is outside veyyon’s reach. That output is also saved to the session file as it was printed, so a command that echoes a credential puts it there. The arguments veyyon itself records are redacted, but it cannot redact what a command chose to print. Protection begins when the value is known. Once you enable protection or store a value, old local transcript text containing that value is sanitized on subsequent provider requests. The local transcript is not rewritten in place. A value you type on the command line is visible on screen. /secret add puts the credential in your scrollback, and the confirmation reports it. It is excluded from persistent editor history, but the obfuscator cannot scrub a terminal after the fact. Use /secret add on its own, which opens a field that hides what you type, or /secret from-env , which types nothing at all. The secret-use log records use, not intent. It records which credential went into which command. It cannot show what the command did with it once the process had it.","breadcrumbs":"Secrets » What this does not protect","id":"181","title":"What this does not protect"},"182":{"body":"The field-by-field schema, the merge rules between the two files, and the interaction with environment detection are in docs/handbook/src/architecture/secrets.md. For provider credentials specifically, veyyon keeps OAuth tokens and API keys in its own credential store rather than in your context. See Signing in.","breadcrumbs":"Secrets » Reference","id":"182","title":"Reference"},"183":{"body":"You choose an endpoint and a model id. Veyyon then calls that provider’s API directly with your credentials. The endpoint can be a local server, a hosted API, or any OpenAI-compatible gateway. Contract (what the harness owns vs the provider): Model contract Copy-paste provider setups: Configuring providers Built-in provider stack internals: Provider stack and BYOK","breadcrumbs":"Models and providers » Models and providers","id":"183","title":"Models and providers"},"184":{"body":"BYOK means bring your own key. For a provider you configure yourself, Veyyon sends your key straight to that provider’s endpoint. There is no hosted proxy in between. Set the key one of three ways: The provider’s environment variable (see Providers for the full map), or /login inside the TUI, which stores the credential in the auth store, or A models.yml apiKey on a custom provider (an env-var name, or literal:). See Signing in for storage modes and Configuring providers\\nfor full models.yml examples.","breadcrumbs":"Models and providers » API keys (BYOK)","id":"184","title":"API keys (BYOK)"},"185":{"body":"# ~/.veyyon/profiles/default/agent/models.yml\\nproviders: deepseek: baseUrl: https://api.deepseek.com api: openai-completions apiKey: DEEPSEEK_API_KEY models: - id: deepseek-chat name: DeepSeek Chat contextWindow: 128000 maxTokens: 8192 $ export DEEPSEEK_API_KEY=sk-...\\n$ veyyon --model deepseek/deepseek-chat","breadcrumbs":"Models and providers » Minimal BYOK shape","id":"185","title":"Minimal BYOK shape"},"186":{"body":"Veyyon ships a large built-in catalog (Anthropic, OpenAI, Google, Groq, OpenRouter, Mistral, xAI,\\nBedrock, and many hosted gateways) plus three auto-discovered local engines. A provider becomes\\nselectable when it is not in disabledProviders and it is keyless or has resolvable credentials. Provider id Notes anthropic, openai, google, groq, … Cloud providers; set the env var. Some (for example anthropic) also support /login ; see providers. amazon-bedrock Uses the AWS credential chain ( AWS_PROFILE, instance role, …). ollama, lm-studio, llama.cpp Local engines, discovered automatically and keyless by default. Once a provider is available, model ids come from a bundled static catalog, merged with live\\ndiscovery for providers that expose a /models endpoint. Failed discovery returns an error; it does\\nnot invent an empty catalog.","breadcrumbs":"Models and providers » Built-in providers","id":"186","title":"Built-in providers"},"187":{"body":"Both are discovered automatically once the engine is running; no models.yml entry and no key are\\nrequired. $ ollama serve\\n$ ollama pull llama3.2\\n$ veyyon # then /model and choose an ollama/… entry $ lms server start\\n$ veyyon # then /model and choose an lm-studio/… entry Override the base URL with OLLAMA_BASE_URL / LM_STUDIO_BASE_URL if a daemon listens elsewhere. An\\nexplicit models.yml entry for one of these ids replaces its built-in discovery.","breadcrumbs":"Models and providers » Local models: Ollama and LM Studio","id":"187","title":"Local models: Ollama and LM Studio"},"188":{"body":"Action What it changes What it does not change /model (or restart with --model) The interactive model for subsequent turns The subagent and compaction models Switching the interactive model mid-session never blends through a fallback chain into the subagent or\\ncompaction model. /model shows the current interactive model; /session info shows session stats. veyyon plugin doctor checks plugin installation health. $ veyyon --model openai/gpt-5\\n# later, inside the TUI:\\n/model deepseek/deepseek-chat\\n/session info","breadcrumbs":"Models and providers » Mid-session model switch","id":"188","title":"Mid-session model switch"},"189":{"body":"Piece Purpose Config Interactive model Main conversation /model, --model; persisted as modelRoles.default Roles Named assignments ( smol, slow, plan, advisor, …) modelRoles / Settings → Model → Roles Subagent policy Blanket and per-agent model and effort choices subagent.model, subagent.thinkingLevel, and subagent.agents Compaction override Compaction / handoff compaction.model (else inherit interactive) # ~/.veyyon/profiles/default/agent/config.yml\\nmodelRoles: default: openai/gpt-5 # interactive (persisted default) smol: openai/gpt-4.1-mini slow: anthropic/claude-opus-4-5:high plan: anthropic/claude-sonnet-5\\nsubagent: model: deepseek/deepseek-chat:high agents: reviewer: enabled: true thinkingLevel: auto\\ncompaction: model: openai/gpt-5-mini Ctrl+P (default binding) cycles roles listed in cycleOrder (schema default smol, slow, not default). Full role list and aliases: Models, roles, and profiles.","breadcrumbs":"Models and providers » Model selection","id":"189","title":"Model selection"},"19":{"body":"Handles are defined in an AGENTS.dict file: sigil = \\"§\\" [handles]\\nbuild = \\"node --experimental-vm-modules ./scripts/build.mjs --target release --profile ci\\"\\ndbconn = \\"src/server/db/connection.ts\\" The dictionary format enforces length bounds on expansions and includes a schema version. Files with incompatible major versions fail load validation. The dictionary is generated automatically from project contents. When a session starts or the argot_load tool runs on a target directory, the generator identifies recurring strings, ranks candidates by token reduction, and stores the compiled dictionary in the local cache directory. Nothing is written to the repository working tree. Automatic startup loading is controlled by argot.autoload (enabled by default).","breadcrumbs":"Design and mechanisms » Argot » Dictionary structure","id":"19","title":"Dictionary structure"},"190":{"body":"Prompt order, repair enablement, and tool exposure can be set per model id through harness profiles\\nand model roles. See Execution-order prompts and Model contract.","breadcrumbs":"Models and providers » Per-model harness settings","id":"190","title":"Per-model harness settings"},"191":{"body":"Optional overrides in config.yml or ~/.veyyon/profiles/default/agent/harness-profiles.yml. The two files take different shapes: config.yml nests under harness: (the harness.profiles setting), while harness-profiles.yml reads a top-level profiles: map (a top-level harness: key there is silently dropped). # config.yml\\nharness: profiles: \\"openai/gpt-4.1\\": repair: true tools: [\\"read\\", \\"edit\\", \\"grep\\", \\"bash\\", \\"write\\"] promptSectionOrder: [\\"tool-policy\\", \\"delivery-contract\\"] # harness-profiles.yml\\nprofiles: \\"openai/gpt-4.1\\": repair: true tools: [\\"read\\", \\"edit\\", \\"grep\\", \\"bash\\", \\"write\\"] promptSectionOrder: [\\"tool-policy\\", \\"delivery-contract\\"] Keys: exact provider/model-id or provider/*. See Per-model repair posture.","breadcrumbs":"Models and providers » Harness profiles","id":"191","title":"Harness profiles"},"192":{"body":"Set credentials for the new provider and select a model id from that catalog. Tool surface and tools.approvalMode are independent of provider id. $ export OPENROUTER_API_KEY=...\\n$ veyyon --model openrouter/anthropic/claude-sonnet-4","breadcrumbs":"Models and providers » Switching providers","id":"192","title":"Switching providers"},"193":{"body":"Constraint Typical choice Tool-heavy refactors Hosted model with tool calling Long sessions / subagents Choose cheaper models under subagent and compaction.model Low latency Local or flash-tier cloud Offline / private code Ollama, LM Studio, llama.cpp CI Pin exact provider/id with --model Pin models in CI and shared profiles ( --model, modelRoles). Floating “latest” aliases change under you.","breadcrumbs":"Models and providers » Model selection notes","id":"193","title":"Model selection notes"},"194":{"body":"Configuring providers: full copy-paste setups. Model contract: harness vs provider boundary. Getting started: first key and first task. Configuration: model defaults and overrides. Authentication: login, logout, secret storage.","breadcrumbs":"Models and providers » Where to go next","id":"194","title":"Where to go next"},"195":{"body":"A Veyyon session is the unit of interactive work. Start one in the repository you want to modify: veyyon The session records turns, tool activity, approvals, edits, and verification output. Long-running work\\nshould survive context pressure through explicit goal state, compacted history, working-set facts, and\\nresume metadata rather than relying on the model to remember everything from raw transcript text.","breadcrumbs":"Sessions » Sessions","id":"195","title":"Sessions"},"196":{"body":"Start fresh with veyyon. Continue saved work from the session picker on launch, or /resume inside the TUI. Branch a previous conversation with /branch (from a chosen user message) or duplicate the whole\\nsession with /fork. Manage saved sessions with /session; garbage-collect old artifacts with veyyon gc. Run a bounded non-interactive task by passing a prompt: veyyon \\"…\\". Veyyon resumes from the launch picker or /resume, and branches with /branch / /fork.","breadcrumbs":"Sessions » Common session actions","id":"196","title":"Common session actions"},"197":{"body":"For large tasks, make the desired outcome explicit. The harness should preserve active instructions,\\nrecent turns, working files, verification facts, and unresolved blockers through compaction. When a\\nsession resumes, Veyyon should make the important state visible to the next model turn instead of\\npresenting a clean-looking summary that dropped the real constraint.","breadcrumbs":"Sessions » Long work","id":"197","title":"Long work"},"198":{"body":"A session file ( ~/.veyyon/profiles/default/agent/sessions/**/_.jsonl) is an append-oriented log whose entries form a tree. Recorded session entries carry an id and a parentId. Branching appends a new entry whose parentId states an earlier entry, so it starts a sibling branch from that point. The active leaf advances to each appended entry. On load it falls back to the last entry in the file. Not every line carries parentId: the first-line session header does not, and in-place refresh records are full replacements of the original logical record rather than tree entries. Storage maintenance may atomically rewrite the file to update the header or representation, but it preserves the history entries. Branches you navigate away from remain addressable. Four properties are guaranteed by the storage layer: No history deletion during navigation. Branching appends new entries; abandoned entries remain addressable. A corrupt header never initializes over existing bytes. A non-empty file without a valid first session record is rejected. Veyyon leaves it byte-for-byte unchanged so you can inspect or repair it. Recoverable record loss is operator-visible. A malformed later record is skipped so one damaged line does not make the entire session unopenable. The session shows one bounded warning with the file, one-based line and byte offset, and shape problem. It never quotes the dropped record’s content. Duplicate ids are last-write-win, and broken parent chains appear as extra roots. Moves are transactional. Moving a session changes the transcript, its artifacts, and its recorded working directory as one operation through the active storage backend. If relocation or the final header write fails, Veyyon restores the old paths and in-memory working directory. Session files written by older Veyyon versions have no linkage fields; they load as a linear chain,\\nwhich is the exact shape they recorded.","breadcrumbs":"Sessions » Session files are trees","id":"198","title":"Session files are trees"},"199":{"body":"Run /tree in the TUI to browse every entry of the session, including branches you previously\\nabandoned. Picking an entry opens a small action menu: Jump here continues from that point. For a user message the jump lands just before it and\\nplaces the full message text in the composer, ready to edit and resubmit. The start of\\nconversation recalls the original prompt into the composer so you can edit and resubmit it.\\nAnything else (an agent reply, a compaction) branches from that entry with an empty composer. Label… attaches a short free-text label to the entry so you can find it again later. Labels\\nrender as [label] tags in the tree. Submitting empty text, or picking Clear label, removes it. The tree view filter modes ( treeFilterMode in config.yml, also toggled in the /tree UI) are: Mode What it shows default Conversation entries (hides low-signal noise) no-tools default plus hides tool-result-only assistant messages user-only User messages only labeled-only Entries with labels all Every raw entry Typing filters rows by preview and label text. There is no separate Conversation/User/Labeled/All tab chrome beyond these filter modes, see Branching.","breadcrumbs":"Sessions » Navigating the tree","id":"199","title":"Navigating the tree"},"2":{"body":"Section Contents Design and mechanisms Design goals and the main subsystems Get started Install, sign in, first task, providers Features Editing, approvals, models, sessions, plan and goal modes, MCP, plugins, memory, profiles Architecture Internals","breadcrumbs":"The Veyyon handbook » Where things are","id":"2","title":"Where things are"},"20":{"body":"The codec operates under two distinct boundaries: Decoding: Turning handles back into full text is unconditional. Whenever a dictionary is active, all handles are expanded before text reaches tool execution, transcript storage, or terminal display. Encoding: Teaching shorthand syntax to the model is controlled by configuration: Model allowlist ( argot.encode.models): Shorthand instructions are provided only to explicitly allowed models. Context cutoff ( argot.encode.disableAboveTokens): Shorthand instructions are omitted once session context exceeds the specified token limit. When encoding is disabled, the model writes full strings. Decoding remains active so existing handles in session history continue to expand.","breadcrumbs":"Design and mechanisms » Argot » Codec boundaries","id":"20","title":"Codec boundaries"},"200":{"body":"/fork and /branch both create a new session file and never modify the original; /tree\\nnavigation above stays inside the current file. /fork duplicates the entire current session (every entry, including sibling branches)\\ninto a new persisted file. There is no entry picker; for a slice from a chosen point, use /branch. veyyon --fork does the same at startup. The launch session picker\\ninstead copies only the picked session’s ancestor path (the active lineage) into the new file. /branch picks an earlier user message and copies the history up to that point (or resets\\nto a fresh root if the picked message is the first one) into a new session file, then recalls the\\nmessage text into the composer for edit-and-resubmit. There is no /clone slash command in the shipped registry. Labels are stored in the session file itself as append-only bookkeeping lines (last write wins), so\\nthey survive resume and never rewrite history.","breadcrumbs":"Sessions » Forking and branching to a new file","id":"200","title":"Forking and branching to a new file"},"201":{"body":"/export renders the current session as a self-contained HTML file you keep, for backup,\\ninspection, or sharing. /export: write to the session’s working directory under a generated file name. /export : write to . The command prints the destination and opens the result in your browser. It never modifies the live\\nsession. Programmatic access uses the Agent Client Protocol ( veyyon acp) or SDK embedding; no separate daemon\\nis required. Session tree operations in the TUI use /tree, /branch, and /fork.","breadcrumbs":"Sessions » Exporting a session","id":"201","title":"Exporting a session"},"202":{"body":"veyyon gc reclaims disk: it sweeps blobs no session references any more, archives cold sessions, and\\ncheckpoints the database write-ahead logs. It is a dry run unless you pass --apply, and it prints what\\nit would do either way. GC never touches a file that was written recently, because a running veyyon may still be appending to it.\\nThat window is five minutes by default, and you can change it: # ~/.veyyon/profiles/default/agent/config.yml\\ngc: writeGraceMinutes: 15 Pass --write-grace-minutes to override it for one run. The minimum is one minute: a shorter window\\nwould let GC delete a blob a live session wrote a moment ago, so a smaller value is raised to the minimum\\nand the run reports it.","breadcrumbs":"Sessions » Cleaning up old sessions","id":"202","title":"Cleaning up old sessions"},"203":{"body":"Input entered during a running turn goes to one of two places, and the bottom pane always shows\\nwhich: Steer ( Enter). The message is injected into the current turn: the model sees it at the\\nnext tool boundary and adjusts course without abandoning its work. Queue a follow-up ( Ctrl+Q, or Ctrl+Enter where the terminal delivers it). The message is queued in the running process and starts a new turn once the current one finishes. The queue lives in memory for the lifetime of the\\nprocess; it is not written to the session file, so it does not survive a restart. Slash commands and ! shell escapes\\nqueue client-side instead; they are local actions, not model input. Queued messages render under the composer grouped as Steering·N and After yield·N, with the\\ndequeue key shown as a hint. They are never delivered after an interrupt: pressing Esc aborts the\\nturn and pulls every queued follow-up back into the composer so nothing you typed is lost. To edit\\na queued follow-up without interrupting, press the dequeue chord ( Alt+Up by default, remappable):\\nthe most recent follow-up returns to the composer and older ones stay queued. Delivery is governed by steeringMode and followUpMode (both one-at-a-time by default; set to all to deliver every queued message at the next boundary): # ~/.veyyon/profiles/default/agent/config.yml\\nsteeringMode: all\\nfollowUpMode: all Programmatic clients use the follow_up RPC command to queue a follow-up on an active session.\\nRecalling a queued follow-up is a TUI-local action ( Esc or the dequeue chord); the\\nRPC protocol has no recall command. An empty follow-up is a no-op in the TUI, and /queue with no\\ntext shows a usage warning.","breadcrumbs":"Sessions » Typing while the agent works","id":"203","title":"Typing while the agent works"},"204":{"body":"Read Examples for concrete prompts and workflows.","breadcrumbs":"Sessions » Next","id":"204","title":"Next"},"205":{"body":"A subagent is a second veyyon session that your session starts, hands one piece of\\nwork to, and collects a report from. The parent spawns it with the task tool; the\\nsubagent has its own context window, so bulk reading and long grinding work stay out\\nof the conversation you are having. Everything about them is configured in one place: the Subagents tab in /settings, backed by the subagent.* settings. /agents is the live picture of a\\nrun in progress: which agents are working right now and what they are saying to each\\nother. It does not configure anything.","breadcrumbs":"Subagents » Subagents","id":"205","title":"Subagents"},"206":{"body":"One agent type, the general-purpose worker, and delegation that the prompt requests: subagent: delegation: preferred # the default; the prompt requests that substantial work be delegated Every subagent runs the model you are working with. Change the model you are talking\\nto and your subagents follow it. Veyyon also ships five specialists ( scout, reviewer, designer, librarian, sonic), and they are disabled by default. During first-run setup, the Choose subagents step shows every available role with only task checked.\\nEnable the specialists you want the model to start on its own. Each enabled type\\nadds its description to future requests, so leave roles off when you do not use\\nthem.","breadcrumbs":"Subagents » What you get out of the box","id":"206","title":"What you get out of the box"},"207":{"body":"subagent.delegation sets how hard this session is pushed to delegate: Value What happens allowed The tool is there; the model judges when it helps, and the prompt does not request it. preferred The default. The prompt instructs the model to fan substantial work out instead of doing it alone. required The same, plus a first-turn reminder that delegation is the default here. The strength applies only when an enabled role matches the work. If task is\\nenabled, it acts as the general-purpose fallback. If only specialists are\\nenabled, work that matches none of their descriptions stays in the main\\nsession. With no enabled agent, the delegation preamble is omitted. The separate subagent.enabled boolean (default on) is the kill switch: off removes the task\\ntool and every delegation instruction from the prompt, so nothing can be spawned. A legacy delegation: off migrates to subagent.enabled: false. The instructions follow the exact roles you enable. With only task offered,\\nthe prompt uses it as the general-purpose route. Enabling designer or reviewer adds those separate roles without changing what task means.","breadcrumbs":"Subagents » How hard to push","id":"207","title":"How hard to push"},"208":{"body":"subagent.delegation sets how strongly the model is pushed to delegate.\\nThe description of each enabled agent scopes the work that role covers.\\nThese are separate settings. Veyyon preserves concrete roles. It does not infer a second role category from\\nthe tools an agent can call. For example, designer remains a designer and reviewer remains a reviewer. The model chooses the closest matching\\nspecialist for each independent slice: # agents/accessibility-reviewer.md frontmatter\\nname: accessibility-reviewer\\ndescription: Reviews terminal interfaces for accessibility problems and reports findings\\ntools: read, grep, glob Enable that role when you want it available: $ veyyon config set subagent.agents.accessibility-reviewer.enabled true When task is enabled, the model can use it for substantial work that does not\\nfit a specialist. When task is disabled, the model keeps unmatched work\\ninline. This prevents a specialist name from becoming a generic worker merely\\nbecause no closer role is available. An agent role is routing guidance, not a security boundary. Use the sandbox when you need to restrict filesystem or process access.","breadcrumbs":"Subagents » What counts as delegable work","id":"208","title":"What counts as delegable work"},"209":{"body":"subagent.agents holds one row per agent name. You choose initial permissions in\\nthe first-run Choose subagents step, then edit them through /settings →\\nSubagents → Agents. The settings screen lists every discovered agent with its\\nstate, resolved model, and deciding setting. Enter opens one agent to set its\\nstate, model, and effort, or reset it to defaults. To add an agent, put a markdown definition in your own or the project’s agents/ directory, or start from the shipped definitions by running veyyon agents unpack. The definition makes the role available. Enable its row\\nbefore the model may start it. A row has two states: State Meaning Enabled Listed in the task tool and choosable by the model. Only the bundled task worker defaults to enabled. Disabled Refused even when named, with a message pointing at the setting. Specialists and user or project agents default to disabled. The built-in flows still work with the specialists disabled because a command can grant its\\nagent for the turn: /review requests agent: \\"reviewer\\" through a per-turn grant, and so can\\nyou (“use the scout agent to map the parser”). Writing an agent file makes the role available but does not grant spawn\\npermission. Enable the role during setup or in the Agents settings table.","breadcrumbs":"Subagents » Choosing agents","id":"209","title":"Choosing agents"},"21":{"body":"Cache entries are content-keyed using the repository commit hash or a directory fingerprint: Cache files are immutable once written. Distinct commits produce separate cache entries. Stored transcripts contain expanded text rather than raw handles, allowing cache entries to be discarded and rebuilt without corrupting session history. To rebuild a project cache, remove the cached dictionary directory under ~/.veyyon/cache/argot/.","breadcrumbs":"Design and mechanisms » Argot » Cache storage","id":"21","title":"Cache storage"},"210":{"body":"Four things can set the model a subagent runs. The first that specifies one wins: that agent’s own row, subagent.agents..model the blanket subagent.model the agent definition’s own model: frontmatter, for an agent you wrote otherwise the subagent inherits the model you are working with subagent: model: openai/gpt-5:high # every subagent agents: reviewer: enabled: true model: anthropic/claude-opus-4-5 # except this one No bundled agent pins a model, so layer 4 is the normal case and subagent.model\\nmoves all of them together. subagent.thinkingLevel does the same for effort. Inherit passes the\\ncurrent session’s effective effort into the child, while an explicit auto requests that the provider\\nchoose. An explicit :effort suffix on a model pattern always wins over an agent’s own default.","breadcrumbs":"Subagents » Choosing models","id":"210","title":"Choosing models"},"211":{"body":"Every one of those four places takes a list, not just one model: subagent: model: anthropic/claude-opus-4-5,openai/gpt-5 The first entry is what subagents run on. The rest are held in reserve: when a run errors on the\\nmodel in use, that agent retries on the next entry rather than failing. The settings picker writes\\nthe value for you: open the model row, add a fallback, and press Enter on any entry to move it up\\nthe list. A longer chain reads better as a list, and both spellings mean the same thing: subagent: model: - anthropic/claude-opus-4-5 - openai/gpt-5 Write it whichever way suits the file. compaction.model takes a chain the same two ways. A chain only covers errors at run time. A model pattern that matches nothing is still a\\nconfiguration mistake, so veyyon will not spawn the agent and states the setting, rather than\\nquietly running it on the next entry: a typo must not silently downgrade every subagent you spawn. In the Subagents block above the composer, an agent that fell back is marked with ↓ before its\\nmodel badge, so you can tell a deliberate model from a retried one at a glance. Effort is chosen from a list: off, minimal through max, auto, or Inherit.\\nThe same list appears in both places, so you cannot set a level that does not exist. If a hand-written config\\nholds one that does not, veyyon reports the levels that work, rather than\\ntreating it as Inherit and leaving you with a setting that reads as configured and\\nchanges nothing. A configured model that matches nothing available does not quietly fall through to\\nthe next layer. The spawn is rejected and the message states the setting to fix, because\\nfalling through is indistinguishable from your setting having no effect. Both agent\\nsurfaces show, for the selected agent, the pattern, the model it resolves to, and\\nwhich of the four layers decided.","breadcrumbs":"Subagents » Fallback models","id":"211","title":"Fallback models"},"212":{"body":"While a spawn is in flight, the Subagents block sits above the composer with one\\nlane per agent. A lane reads left to right: a rail, the agent’s id, what it is doing,\\nand the model it resolved to. Subagents ▏ DockerSecretHarness bash cargo test --workspace --all-targets claude-opus-5 high ▏ SecretModeFlowUX read modes/interactive-mode.ts claude-opus-5 high ▏ SecretModularityAudit Audit secrets subsystem modularity, wiring, and… claude-opus-5 med ▏ RateLimitedWorker Retrying (2/5) in 38s · 429 rate limit exceeded claude-opus-5 high The id is painted in that agent’s own accent, the same hue the status line gives its\\nname and the same one a delegated todo row uses to point back at it. The middle column holds the most urgent fact the agent has. An agent asleep between\\nprovider attempts shows the recovery, its attempt count and the reason, counting down.\\nAn agent running a tool shows the tool and its argument. An agent waiting on the model\\nhas nothing to report, so it shows the work it was given instead, dimmed. Every lower\\nrank is still true when a higher one is, and a lane that printed the description while\\nthe agent was asleep on a rate limit was byte-identical to one thinking. Light travels down the rail while agents are working, and a lane is lit only while it\\nhas a tool in flight. The head crosses the whole block, so the cycle belongs to the\\nblock rather than the row, and arrives cold on a lane that is waiting or recovering.\\nWhere display.transitions is off, the block is still. There is no elapsed clock and no context gauge. Total age ranks agents by seniority,\\nwhich nothing acts on, and a parent decides nothing with a subagent’s remaining\\nwindow. Whether a lane is stuck is answered by the recovery column. /agents carries\\nthe roster with the numbers. A lane keeps its badge on its own row.\\nNarrow the terminal and the model badge comes off first, then the columns shrink to\\nwhat is left. Nothing wraps: the block draws no row it cannot fit, and draws nothing\\nat all rather than overflow. Eight lanes are drawn. Past that the block states how many more are running and points\\nat /agents, which is the full roster. That row is the only place a count appears; the\\nheader is bare.","breadcrumbs":"Subagents » Watching a run","id":"212","title":"Watching a run"},"213":{"body":"The remaining groups in the tab are operational: how many subagents run at once\\n( subagent.maxConcurrency), how deeply they may nest ( subagent.maxNestedSpawnDepth),\\nper-run wall clock and request budgets, how long a finished subagent stays live\\nbefore parking ( subagent.idleTtlMs) and how long it stays listed after that\\n( subagent.autoClose.*), and whether its edits land in an isolated\\ncopy of the tree first ( subagent.isolation.*, see Safety). subagent.idleTtlMs defaults to five minutes for every model and provider. Set\\na positive millisecond value to override it. Set 0 to keep idle agents live\\nuntil exit. Parking releases the live session but keeps its transcript, so\\nmessaging or opening the agent can revive it. A parked subagent is eventually closed, which drops it from the roster so a long\\nsession does not accumulate every agent it ever spawned. Closing removes the\\nrevivable reference; it does not touch the transcript, which stays readable at history://. Four settings in the Auto Close group control the whole lifecycle, in the order they\\nhappen. subagent.idleTtlMs (“Park After”) is stage one: how long a finished subagent\\nstays live before parking, five minutes by default. subagent.autoClose.enabled is on by\\ndefault; turn it off to keep every parked subagent listed and revivable until you exit. subagent.autoClose.parkedMs (“Close After”) is how long a parked subagent stays listed,\\ncounted from the moment it parked, and defaults to five minutes. subagent.autoClose.waitingMs (“Close After (Waiting)”) is the same budget for a subagent\\nwhose last message reported waiting on another agent, and defaults to thirty minutes:\\nit stopped on purpose to let a peer finish, so it is the agent you are most likely to\\nmessage next. Set the two equal to treat both the same. Turning auto-close off does not turn parking off. Parking is what releases the session, and\\nit happens either way; the toggle only sets whether the parked reference is eventually\\ndropped. That is why “Park After” sits in this group but is not hidden when the toggle is\\noff.","breadcrumbs":"Subagents » Limits and isolation","id":"213","title":"Limits and isolation"},"214":{"body":"Both budgets count from the agent’s last transition, not from when it was spawned. An\\nidle agent’s park budget starts when it went idle. A parked agent’s close budget starts\\nat the moment it parked. A revived agent starts its park budget again from the revival,\\nso messaging a parked agent gives it a fresh five minutes rather than resuming a clock\\nthat was already half spent. That is why a long-lived session does not slowly close everything at once: each agent’s\\ndeadline moves with its own activity.","breadcrumbs":"Subagents » When each budget starts counting","id":"214","title":"When each budget starts counting"},"215":{"body":"A running agent has no deadline at all, and neither does an aborted one. Nothing\\nparks or closes an agent that is mid-turn. This matters when an agent looks stuck. A subagent waiting for you to answer an approval\\nprompt is still mid-turn, so it stays running and no park or close timer applies to it.\\nIf a finished agent is not being cleaned up, check its status first: the lifecycle only\\nacts on idle and parked, so an agent stuck in running is a different problem and the\\nauto-close settings will not affect it.","breadcrumbs":"Subagents » Only idle and parked agents have a deadline","id":"215","title":"Only idle and parked agents have a deadline"},"216":{"body":"Set subagent.autoClose.parkedMs to 0 and no parked agent is ever closed. That also\\nforces the waiting budget to 0, whatever subagent.autoClose.waitingMs is. That coupling is deliberate. If a zero parked budget still honoured a separate waiting\\nbudget, the only agents that ever closed would be the ones that stopped to wait on a peer,\\nwhich are the agents you are most likely to message next. Zero means never close, for\\nboth kinds.","breadcrumbs":"Subagents » Turning it off","id":"216","title":"Turning it off"},"217":{"body":"Parking flushes the agent’s session to disk before releasing it. If that flush fails, the\\npark is cancelled and the agent stays live with its timer re-armed. You keep a live agent\\nrather than losing unsaved state, and the attempt repeats on the next expiry.","breadcrumbs":"Subagents » If the session cannot be saved","id":"217","title":"If the session cannot be saved"},"218":{"body":"subagent.maxNestedSpawnDepth is inclusive. The default is 0: the top-level session,\\nat depth 0, may spawn direct subagents, but those children are leaves and cannot spawn\\nmore subagents. A value of 1 also lets direct children spawn, producing children at\\ndepth 2. Higher values extend the same rule, and -1 allows nesting without a depth\\nlimit. An agent-specific value takes precedence over the blanket value: subagent: maxNestedSpawnDepth: 0 agents: reviewer: maxNestedSpawnDepth: 1 Here ordinary direct subagents remain leaves. A direct reviewer may spawn its own\\nchildren because its effective limit is 1. A subagent’s working directory is its own. If a subagent calls set_cwd, only that\\nsubagent moves: its tool paths resolve against the new directory and its system prompt\\nis rebuilt for it, while your session and every other subagent stay where they were. That matters because subagents run inside the same process you do. The main session\\nalso moves the process working directory when it re-roots, so that a command you run\\nand a relative path you write agree with the project you have open. A subagent doing\\nthe same would move the ground under everyone else, and the symptom would be a command\\nrunning in the wrong repository with nothing on screen to explain it. The trade is that a subagent working elsewhere does not pick up that project’s\\nsettings, capabilities or plugins, because those are read once for the process. Give a\\nsubagent a task in another project only when the work is self-contained, and re-root\\nyour own session instead when you want that project’s configuration to apply. The full key list is in the settings reference.","breadcrumbs":"Subagents » Nesting depth","id":"218","title":"Nesting depth"},"219":{"body":"The interactive TUI is the main surface for one session. Status line, session tree, background jobs, the Agent Control Center, and optional swarm orchestration cover multi-agent work.","breadcrumbs":"Multi-agent monitoring » Multi-agent monitoring","id":"219","title":"Multi-agent monitoring"},"22":{"body":"Subagents evaluate shorthand independently: Each agent instance expands its output before invoking tools, writing transcripts, prompting subagents, or returning values to a parent agent. Raw handles do not cross agent boundaries. The argot.subagents setting controls whether child agents inherit the parent dictionary, generate a project-specific dictionary, or operate without shorthand. See Subagents configuration.","breadcrumbs":"Design and mechanisms » Argot » Subagent boundary","id":"22","title":"Subagent boundary"},"220":{"body":"Configure under Settings → Appearance → Status Line ( /statusline jumps to this group), or in config.yml: Key Purpose statusLine.preset default, minimal, compact, full, nerd, ascii, or custom statusLine.leftSegments / statusLine.rightSegments Segment lists when preset: custom statusLine.enabled Show the footline at all (on by default) statusLine.showAccount Name the account serving the next request, when the provider stores more than one (off by default) statusLine.sessionAccent Tint the editor border with the session color statusLine.showHookStatus Show active hook status when hooks run Built-in segment IDs include: pi (legacy product mark segment), profile, model, account, secrets, mode, path, git, pr, subagents, token_in, token_out, token_total, token_rate, cost, context_pct, context_total, time_spent, time, session, hostname, cache_read, cache_write, cache_hit, session_name, usage, collab. The model segment shows the model you are working with, then two things that are easy to confuse, so they are drawn differently: The thinking effort joins the model label as one unit ( Sonnet 4.5 @high in the quiet footline, Sonnet 4.5 · high elsewhere), in the model’s own color. It is how much reasoning the model does per turn. Change it with /effort (its alias is /thinking), or set a per-model default under Settings → Model → Default Effort.\\nThe effort picker has a Default row followed by only the active model’s valid variants. Choose Default to clear the session override and return to the saved per-model effort or the model default. The priority tier follows the effort as its own chip, named and in the warning color ( ⚡ priority, or just priority when your symbol preset has no icon). It is how your requests are queued and served, not how deeply the model thinks. Toggle it with /fast, or set it per provider family under Settings → Model → Service Tier. The git segment shows the current branch, and appends the multi-step operation you are part-way through when there is one: main on a branch, nothing in progress\\ndetached detached HEAD\\ntopic|REBASE rebasing topic\\nmain|MERGE a merge that stopped on a conflict\\nmain|CHERRY-PICK a cherry-pick that stopped on a conflict\\nmain|REVERT a revert that stopped on a conflict\\nmain|AM applying a patch series with git am\\ndetached|BISECT bisecting A rebase detaches HEAD, so without the suffix the segment could only say detached, which shows neither the branch being rebased nor that a rebase is running. Veyyon reads the branch back from the rebase’s own record, so you see topic|REBASE. A merge is the opposite case: it leaves HEAD on its branch, so the segment would look like an ordinary checkout while your working tree holds conflict markers. The suffix is what distinguishes them. The pr segment is skipped while any of these operations is in progress. A branch being rebased does not yet point where it is going to end up, so a pull request looked up against it would describe a state that is about to be replaced. The profile segment shows the active profile name ( work, rec, a client sandbox), so you always know which profile’s config, sessions, and keys are live. It hides itself on the built-in default profile, so an unconfigured status line stays clean. Every built-in preset places it, so switching profiles is visible without any configuration. The secrets segment shows that a stored credential is live where you are working: 2 secrets. It counts\\nwhat would actually expand at the tool boundary in this directory, not what is in the vault file, so\\na credential scoped elsewhere or already expired is not counted. It hides itself when nothing is\\nlive, so a session with no vault shows nothing. When the soonest deadline is under an hour it appends\\nthe time left in the warning color, 2 secrets (34m left), which is the window in which extending it\\nwith e in /secret still helps; a deadline further out belongs to the card’s EXPIRES column. The context_pct segment answers one question: how much room is left before the context runs out. “Runs out” means whichever comes first, auto-compaction firing or the model’s window filling, so with auto-compaction on the segment measures against the compaction trigger, not the window. The window itself is what context_total prints. In the composer’s quiet footline the segment renders as an 8-cell bar with a labelled percentage: ▰▰▰▰▰▰▱▱ 76% left ∞. The bar drains, one cell per eighth of the room remaining, so the bar and the number always agree. Filled cells take the usage hue (silver, then gold, ember, and red as room runs out). While the model is running, the last remaining cell pulses between filled and empty, faster once you are past 90 percent used; at rest the bar never moves, so an idle screen is still. A session-accent ∞ after the bar means auto-compaction is on, so the session continues past the trigger. Classic status-line presets render the same measurement as text, in tokens on both sides of the slash: 47K/170K. For a full picture of what is in the window, and how it is divided between the system prompt, tools, skills, and messages, run /context. Two run clocks tick alongside the segments, both measuring model runtime, never idle wall time. While the agent runs, the location line (path and git branch) ends with the current run’s elapsed, in M:SS form and widening to H:MM:SS past the hour: …keyhog · main * 12:34. When the run finishes the clock freezes as ✓ 12:34 (checkmark plus the final M:SS/ H:MM:SS readout); before the model has ever started it shows nothing. The working line shows how long the current step has been running, between the step label and the esc hint: Running tests · 0:42 ⟦esc⟧; that clock restarts whenever the step changes. The time_spent segment is related but cumulative: it sums every run in the session (a fresh session with /new starts it at zero) and appears in the full and nerd presets.","breadcrumbs":"Multi-agent monitoring » Status line","id":"220","title":"Status line"},"221":{"body":"Command Effect /tree Browse the session entry tree; jump or label entries /branch Branch a new session file from an earlier user message /fork Duplicate the current session into a new file /session info Session metadata and stats /agents Agent Control Center: the live roster (agent type, status, activity; Enter opens one agent’s session) and the Comms stream of agent-to-agent messages /jobs List background async tool jobs /cockpit and /hub are aliases of /agents, as is the app.agents.hub keybinding and a double-tap of the left arrow on an empty composer. They used to open a separate screen with its own roster and its own drill-in, which meant “which agents are running” had two answers that could disagree with each other. They all open the one card now.","breadcrumbs":"Multi-agent monitoring » Session tree and agents","id":"221","title":"Session tree and agents"},"222":{"body":"Each row is one agent that exists right now: a status glyph, its call sign, the TYPE of agent it was spawned from ( reviewer, scout), its status, how long since it last did anything, and what it is doing. Rows sit in spawn order, oldest first, with your own session at the top. The row you are on is marked two ways, a cursor glyph in the first column and a band across the whole row, so it stays readable on a terminal that renders no colour. Agents from earlier runs of the same session appear too, marked parked, because their transcripts are still on disk even though this process never started them. Press Enter on a row and the main view becomes that agent’s session: the transcript, the composer, and the status line all point at it, so you read what it is doing and then answer it. Press Esc there to come back to your own session. While you are focused on an agent, the composer footline contains a persistent · esc to go back badge at the left, and the inline Subagents block above the composer lists that session’s own spawns, so inside a leaf agent with no spawns of its own the block disappears. Opening a parked agent revives it on the way in. To terminate a subagent, select its row and press x, or hover the row and click the [x] at its right edge. Both gestures open the same confirmation card. Choose Dismiss to return without changing the agent, or choose Yes, terminate to abort any turn in flight and release the session. The transcript stays on disk. Your main session and read-only advisor transcripts do not show the termination action. Clicking elsewhere on a row does the same thing as Enter, and clicking a name in the view strip switches to that view. Opening an agent is reversible with Esc, so a row click opens rather than only moving the cursor. The scroll wheel moves whatever the arrow keys move: the roster cursor on Live, the stream on Comms. Page up and page down move by a screenful, on either view. The roster is a table, so you can scan a column instead of reading every row: the status, the age, the model and the activity each start at the same place on every line. A name long enough to crowd the row out is truncated rather than paid for by every other row, and the model badge is dropped when the card is too narrow to show enough of it to recognise. Two agents open a read-only transcript instead of handing the main view over. An advisor is observability-only and is not an addressable peer, so there is nothing on the other end to receive a reply. A collab guest’s agents live on the host, so there is no local session to point at. The inline task widget also shows the model each subagent runs on, right in its status line, so you can see which model every launched subagent used without opening the Control Center. The Subagents block above the composer in your main session shows the same badge on every running row, so the answer to “what is that one running on” is on screen without opening anything. To show the badge, keep subagent.showResolvedModelBadge on (Subagents settings); turning it off hides it on all three surfaces. Session files are append-only JSONL under the active profile’s agent sessions/ directory. See Sessions.","breadcrumbs":"Multi-agent monitoring » The Live roster","id":"222","title":"The Live roster"},"223":{"body":"Subagents and the main agent use the irc tool ( send, wait, inbox, list) over a process-global mailbox. The Comms view of /agents streams that traffic as it happens, oldest first, including the messages that failed to reach their recipient and the reason they did not land. Long messages are folded to their first few lines with a count of what was hidden; ctrl+o unfolds every message in the view, and pressing it again folds them back. Both ends of a message are labelled with the call sign the Live roster shows for that agent, so you follow a conversation by who is speaking rather than by an id you have to look up on the other view. An agent that has since been released has no call sign left to show, so its messages print its id instead. The view reads the message bus, not the session files. A subagent’s transcript records what THAT agent received, so a view built from transcripts would show each half of a conversation in a different file and would never show a message that failed to arrive at all. /btw is an ephemeral side question; /tan spawns a background agent for tangential work. ( /omfg is unrelated: it forges a TTSR rule from a complaint to stop a recurring behavior.)","breadcrumbs":"Multi-agent monitoring » Inter-agent messaging","id":"223","title":"Inter-agent messaging"},"224":{"body":"@veyyon/swarm-extension runs multi-agent DAG workflows from YAML ( pipeline, parallel, or sequential). Standalone: veyyon-swarm path/to/swarm.yaml. In the TUI, add the package to extensions, then: /swarm run path/to/swarm.yaml\\n/swarm status \\n/swarm help State and logs: /.swarm_/ ( state/pipeline.json, logs/*.log).","breadcrumbs":"Multi-agent monitoring » Swarm extension","id":"224","title":"Swarm extension"},"225":{"body":"Remap TUI shortcuts from ~/.veyyon/profiles/default/agent/keybindings.yml (YAML map of action ID → chord or chord\\nlist). Run /hotkeys in a session to see active bindings.","breadcrumbs":"Keybindings and Vim mode » Keybindings","id":"225","title":"Keybindings"},"226":{"body":"app.model.cycleForward: Ctrl+P\\napp.model.selectTemporary: Alt+P\\napp.plan.toggle: Alt+Shift+P\\napp.history.search: [] # disable Chord names match the UI ( Ctrl+P, Alt+Shift+P, Shift+Enter). Older keybindings.json files migrate\\nto .yml on load. Action IDs from older releases ( interrupt, fork, cursorUp) are renamed to their current IDs\\n( app.interrupt, app.session.fork, tui.editor.cursorUp) the first time veyyon loads the file, and\\nthe file is written back. The rename happens where the binding already sits, so your comments, blank\\nlines, and key order come back unchanged: # hold this one, muscle memory\\ninterrupt: ctrl+x becomes # hold this one, muscle memory\\napp.interrupt: ctrl+x Common action IDs include app.model.cycleForward, app.model.select, app.plan.toggle, app.history.search, app.tools.expand, app.thinking.toggle, app.thinking.cycle ( Shift+Tab), app.editor.external ( Ctrl+G), app.message.followUp, app.retry, app.display.reset, and app.clipboard.pasteImage. Engineering detail: docs/handbook/src/reference/keybindings-config.md.","breadcrumbs":"Keybindings and Vim mode » Customize keybindings","id":"226","title":"Customize keybindings"},"227":{"body":"Command Action /hotkeys Show active chords /settings Settings UI (includes keymap-related options) Remap keys by editing keybindings.yml; /hotkeys shows the current bindings. There is no Vim or modal editing mode; the composer uses the bindings above.","breadcrumbs":"Keybindings and Vim mode » Slash commands","id":"227","title":"Slash commands"},"228":{"body":"The web_search tool runs a multi-provider search and returns ranked results (and, for some\\nproviders, answer-plus-citations). Use it for current docs, package versions, and online references.","breadcrumbs":"Web search » Web search","id":"228","title":"Web search"},"229":{"body":"Two settings control the tool: Setting Behavior web_search.enabled Boolean, default true. When false, the web_search tool is not offered to the model at all. providers.webSearch Which search backend to use. Default auto walks the configured provider chain; pin a provider id or use providers.webSearchExclude to drop providers. Both are in settings → Tools / Providers, or in config.yml: web_search: enabled: true\\nproviders: webSearch: auto You can also scope this to a profile, so one profile searches the web and\\nanother stays offline. A profile stores its own settings under its agent dir; set the key in\\nthat profile’s config.yml: # ~/.veyyon/profiles/research/agent/config.yml\\nweb_search: enabled: false","breadcrumbs":"Web search » Configuration","id":"229","title":"Configuration"},"23":{"body":"Save tokens with project shorthand Mechanisms","breadcrumbs":"Design and mechanisms » Argot » Related","id":"23","title":"Related"},"230":{"body":"The tool queries a configurable search backend, API-backed providers (using keys you have\\nconfigured) or credential-free engines (Startpage, Google, DuckDuckGo, Ecosia, Mojeek, or public, which fans out\\nto every credential-free engine and consolidates deduplicated results). auto resolves the\\nfirst available provider in the built-in priority order; providers.webSearchExclude removes\\nproviders from that chain entirely.","breadcrumbs":"Web search » Provider support","id":"230","title":"Provider support"},"231":{"body":"Beyond ranked search, Exa hosts two MCP servers that Veyyon can turn into agent tools. Both\\nare off by default, because each one adds tools to every session and costs a discovery\\nrequest at startup. exa: enableResearcher: true enableWebsets: true exa.enableResearcher adds exa_deep_researcher_start and exa_deep_researcher_check. The\\nmodel starts a research run and then polls it, so a single question can take minutes and\\nreturns a written report rather than a result list. exa.enableWebsets adds one tool per webset operation the server offers, named exa_. The list comes from the server, so new operations appear without a Veyyon\\nrelease. Websets require EXA_API_KEY in your environment; if it is missing, Veyyon logs the\\nreason at startup and registers no webset tools rather than failing later inside a tool call. exa.enabled: false turns off all of it, including search.","breadcrumbs":"Web search » Exa research and webset tools","id":"231","title":"Exa research and webset tools"},"232":{"body":"With web search enabled, the tool runs without a per-call approval prompt, because it reads\\npublic web content rather than touching your machine. If you want the model to never reach\\nthe web, set web_search.enabled: false. See Sandbox and approvals.","breadcrumbs":"Web search » Approvals","id":"232","title":"Approvals"},"233":{"body":"Review surfaces: /review: bundled interactive review command (branch, commits, or uncommitted work). Advisor: optional second model that comments on main-agent turns. Plan review: /plan-review while plan mode is active. Non-interactive: free-form review prompts under veyyon -p.","breadcrumbs":"Code review » Code review","id":"233","title":"Code review"},"234":{"body":"Bundled custom command ( packages/coding-agent/src/extensibility/custom-commands/bundled/review).\\nLaunches a review flow over a chosen target and uses the review tool surface ( report_finding, …).\\nSee the command help in-session and docs/handbook task guides for example prompts.","breadcrumbs":"Code review » /review","id":"234","title":"/review"},"235":{"body":"The advisor is a second model role that reads each main-agent turn and can inject notes\\n(nit, concern, or blocker). Enable with --advisor or the advisor.enabled setting; assign its\\nmodel via the advisor role.\\nUses its own context and model assignment when configured.","breadcrumbs":"Code review » Advisor","id":"235","title":"Advisor"},"236":{"body":"Inside plan mode, /plan-review reopens review of the current plan file. See Plan mode.","breadcrumbs":"Code review » Plan review","id":"236","title":"Plan review"},"237":{"body":"$ veyyon -p \\"review the uncommitted diff for correctness and missing tests\\"\\n$ veyyon -p --yolo \\"review this branch against main; list P0/P1 findings only\\" Typical targets: uncommitted work, a branch delta, or paths named in the prompt. Exit status\\nfollows the print-mode run ( veyyon --help).","breadcrumbs":"Code review » Non-interactive","id":"237","title":"Non-interactive"},"238":{"body":"Tool approvals use tools.approvalMode and per-tool policy. See Approvals and Safety.","breadcrumbs":"Code review » Approvals","id":"238","title":"Approvals"},"239":{"body":"Approvals Non-interactive mode Roles and profiles","breadcrumbs":"Code review » Related","id":"239","title":"Related"},"24":{"body":"Model quality depends on the agent harness: tool schemas, edit format, context handling, and control flow. The same weights can succeed or fail depending on those choices.","breadcrumbs":"Harness design goals » Harness design goals","id":"24","title":"Harness design goals"},"240":{"body":"--print (short -p) runs Veyyon without the interactive TUI: one prompt, tools run under the active approval mode, then exit. Use this from scripts, CI, and other programs. $ veyyon -p \\"add a unit test for parse_config and run it\\"\\n$ echo \\"summarize the diff on this branch\\" | veyyon -p\\n$ veyyon -p - <<\'EOF\'\\nReview src/auth.rs for missing error handling.\\nEOF The prompt may be a CLI argument or stdin. If both are present, the piped stdin content is prepended to the argument prompt (stdin first, then the argument, separated by a newline). Run veyyon --help for the generated flag set. Common options:","breadcrumbs":"Non-interactive mode » Non-interactive mode ( veyyon --print)","id":"240","title":"Non-interactive mode ( veyyon --print)"},"241":{"body":"Option Effect --no-session Do not persist the session (an ephemeral run) --no-rules Do not discover or load rules files --no-skills Do not discover or load skills --profile Activate a named profile ( -p is --print, not profile) --config Load an extra config overlay for this run, repeatable and never persisted --cwd Working directory for the session --allow-home Start in your home directory instead of auto-switching to a temp dir","breadcrumbs":"Non-interactive mode » Session and config","id":"241","title":"Session and config"},"242":{"body":"Option Effect --mode json Machine-readable event stream on stdout --model / role flags Model selection for the run --approval-mode / --yolo Approval policy for the run Headless runs have no TTY for approval prompts: choose a mode that does not block ( --yolo only on disposable runners) or expect the turn to stop when a prompt would be required. See Approvals.","breadcrumbs":"Non-interactive mode » Output and models","id":"242","title":"Output and models"},"243":{"body":"Approvals Safety CLI","breadcrumbs":"Non-interactive mode » Related","id":"243","title":"Related"},"244":{"body":"Veyyon’s interface is built around near-black, near-white, silver structure ( #C6CBD4), and a single ember accent ( #F0862E) — the same tokens the website ships.","breadcrumbs":"Themes and identity » Themes and identity","id":"244","title":"Themes and identity"},"245":{"body":"File Name Notes defaults/titanium.json Titanium Default dark theme. Pitch black #000000, silver #C6CBD4, ember accent #F0862E; mirrors the website tokens ( website/site.css) dark.json Veyyon Dark Bundled alternative. Pitch black #000000 / #FAFAFA / silver #B8BDC7; predates the ember accent light.json Light Default light theme. White #FFFFFF ground with dark-silver structure #5C6470, ember accent A larger bundled catalog ships under modes/theme/defaults/ and is selectable from the theme picker.","breadcrumbs":"Themes and identity » Bundled themes","id":"245","title":"Bundled themes"},"246":{"body":"Settings UI: /settings → Appearance → theme (or the theme picker on first run). Config: theme in ~/.veyyon/profiles/default/agent/config.yml (profile-specific when using --profile). Custom themes: drop JSON under ~/.veyyon/profiles/default/agent/themes/; schema in docs/handbook/src/reference/theme.md. Terminal capability detection maps the same hierarchy for truecolor, ANSI-256, ANSI-16, unknown background, and no-color modes. Reduced-motion settings remove decorative animation without hiding state changes.","breadcrumbs":"Themes and identity » Changing theme","id":"246","title":"Changing theme"},"247":{"body":"Veyyon paints no backgrounds by default. The transcript (user messages, tool\\noutput, extension messages), the composer, and the status line all inherit your\\nterminal’s own background, so the UI looks native on any terminal color. Two\\nopt-ins bring painted surfaces back: Turn off statusLine.transparent ( /settings → Appearance → Status Line) to\\npaint the theme’s statusLineBg bar, including powerline end caps. A custom theme can declare a composerBg color to paint the composer card;\\nwhen omitted, the composer stays unpainted.","breadcrumbs":"Themes and identity » Backgrounds","id":"247","title":"Backgrounds"},"248":{"body":"tui.paintGround ( /settings → Appearance → Display) controls whether Veyyon sets the\\nterminal’s own background color (OSC 11) to the theme’s ground while it runs, so the UI\\nfills the window edge-to-edge instead of floating on the terminal’s configured background.\\nThe original background is restored on exit, including crash exits. The ground is the theme’s page background, the same export.pageBg color the HTML export\\nuses (see docs/handbook/src/reference/theme.md). Every built-in theme declares one. Value Behavior auto (default) Paint only when the terminal’s reported background is already close to the theme ground, so no visible seam appears while painting. If the terminal doesn’t report its background, inherit it. always Always paint the theme ground. never Never touch the terminal background. A custom theme that declares no page background has no ground to paint, so Veyyon inherits\\nthe terminal’s own background regardless of this setting. With always, it also logs once\\nthat the active theme declares no ground, since that is the one case you asked to paint and\\nit could not. Add an export.pageBg to the theme to give it a ground. Terminals that don’t support OSC 11 ignore the sequence; nothing breaks.","breadcrumbs":"Themes and identity » Painted ground","id":"248","title":"Painted ground"},"249":{"body":"The contract applies to onboarding, composer, menus, dialogs, status line, markdown, tables, diffs, tool output, approvals, progress, and errors, not only the chat pane.","breadcrumbs":"Themes and identity » What the theme covers","id":"249","title":"What the theme covers"},"25":{"body":"Edit format. Formats that are hard to emit cause apply failures and retries. Hashline (and model-specific edit prompts) is the main write path in packages/coding-agent. Control flow. Stop when verification passes; bound retries; budget context and subagent fan-out. Plan mode, goal mode, and tool-approval tiers encode parts of this in the engine.","breadcrumbs":"Harness design goals » Primary mechanisms","id":"25","title":"Primary mechanisms"},"250":{"body":"CLI binary: veyyon Config root: ~/.veyyon ( VEYYON_CONFIG_DIR; XDG paths after veyyon config init-xdg) npm packages: @veyyon/*","breadcrumbs":"Themes and identity » Identity elsewhere","id":"250","title":"Identity elsewhere"},"251":{"body":"/collab shares a running session with other Veyyon instances over a relay. Guests open the session in their own TUI (assistant text, tool cards, footer state, /dump); the host process runs the agent and tools. This is not terminal multiplexing.","breadcrumbs":"Live collaboration » Collab: Live Session Sharing","id":"251","title":"Collab: Live Session Sharing"},"252":{"body":"Host: /collab prints Collab session started! • Join from another terminal: veyyon join \\"mgAYTZwEnpRQtca0CTgn-Q.gdJUbTovD94ofDaa8YvhY0-ty16w4fn8PgB6PLnoA30\\" • or any web browser: share.veyyon.dev/#mgAYTZwEnpRQtca0CTgn-Q.gdJUbTovD94ofDaa8YvhY0-ty16w4fn8PgB6PLnoA30 The browser line is click-to-join (an OSC 8 hyperlink to the full https:// deep link): the relay serves the web guest client at /, and the room id + key ride in the URL fragment. From another veyyon (any directory, any machine), either form works: Running /collab or /collab view starts or displays the active hosting session, rendering both the terminal/browser join links and their corresponding QR codes. /join share.veyyon.dev/#mgAYTZwEnpRQtca0CTgn-Q.gdJU… The guest’s previous session is restored on /leave (or when the host stops).","breadcrumbs":"Live collaboration » Quick start","id":"252","title":"Quick start"},"253":{"body":"Command Effect /collab Start sharing full-control (or re-print the link/QR when already hosting) /collab Start sharing through a specific relay ( relay.example.com, ws://localhost:7475) /collab view Start sharing read-only (or re-print the link/QR when already hosting) /collab status Show link + participants /collab stop Stop sharing /join Join a shared session as a guest /leave Leave (guest) or stop sharing (host)","breadcrumbs":"Live collaboration » Commands","id":"253","title":"Commands"},"254":{"body":"Accepted by /join and veyyon join \\"\\": . → default relay (wss://share.veyyon.dev)\\n#