docs: reorganize developer documentation
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# Compaction
|
||||
|
||||
Compaction exists because long-running Pods need durable continuity without sending the entire transcript forever.
|
||||
|
||||
## Pruning vs compaction
|
||||
|
||||
Pruning is request-local packing of existing history. It chooses what to include for one model request while preserving the underlying committed log.
|
||||
|
||||
Compaction is a durable transition. It creates a new summarized state and should be recorded so later turns can explain why older details are no longer directly present.
|
||||
|
||||
## Token-budget protection
|
||||
|
||||
Safety checks should use effective backend/context limits, including in-flight usage. Cached input tokens still occupy context even if upload displays subtract them.
|
||||
|
||||
When a provider returns an exact `input_total_tokens` measurement for the whole prompt shape, Yoi can treat it as authoritative and estimate only incremental growth after that measurement. It should not fake a system/history split or tune thresholds just to mask estimator bugs.
|
||||
|
||||
## After compaction
|
||||
|
||||
Compaction output is not automatically safe. The post-compact context must be revalidated before the next request.
|
||||
|
||||
A `just_compacted` flag must not bypass safety checks. It is easy for a compact summary, retained tail, or prompt resource change to still exceed a context limit.
|
||||
|
||||
## Large sessions
|
||||
|
||||
Large-session compaction should not send an entire prefix transcript as the summary input. Prefer bounded overview/index inputs plus exploration, then keep the retained tail small and explicit.
|
||||
|
||||
This keeps compaction cost predictable and avoids turning a context-recovery mechanism into the largest prompt in the session.
|
||||
|
||||
## History integrity
|
||||
|
||||
Compaction should preserve persisted reasoning history and avoid serializing unverified hidden reasoning context. Trace and metrics can count request shape and reasoning items without smuggling hidden provider state into model input.
|
||||
|
||||
The important property is explainability: after compaction, records should still show what summary replaced which older context and why future turns can rely on it.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Context and history
|
||||
|
||||
Any input that can influence the model across a turn must be committed to history before it is placed in model context.
|
||||
|
||||
This rule protects both explainability and prompt-cache behavior. If the model reacts to a context-only insertion that is not in history, later turns cannot explain why the assistant made that decision, and cache anchors become harder to reason about.
|
||||
|
||||
## Allowed context transformations
|
||||
|
||||
A context transformation is acceptable when it is reproducible from durable Pod state and does not introduce new volatile facts.
|
||||
|
||||
Examples:
|
||||
|
||||
- Pruning old history according to persisted/session-derived state.
|
||||
- Truncating oversized tool result content while preserving the committed result boundary.
|
||||
- Adding prompt-cache anchors derived from stable context structure.
|
||||
- Rendering the same materialized system prompt string after compaction.
|
||||
|
||||
These transformations change how context is packed, not what happened.
|
||||
|
||||
## Forbidden context injection
|
||||
|
||||
Do not insert turn-crossing information directly into context without first appending it to `worker.history`.
|
||||
|
||||
Forbidden examples:
|
||||
|
||||
- Delivering a `Notify` or `PodEvent` only as a temporary context note.
|
||||
- Adding a `<system-reminder>` that explains behavior but is not persisted.
|
||||
- Rewriting old messages to include new facts.
|
||||
- Letting UI/controller-only state become model-visible without a committed record.
|
||||
|
||||
If new information should affect the model, append it to history and commit it. `history.json` / session persistence follows from the Worker history path.
|
||||
|
||||
## Prompt cache implications
|
||||
|
||||
Yoi optimizes for predictable prompt shape, not only small requests. Hidden context insertions make cache behavior hard to reproduce because the model-visible input can change without a corresponding history record.
|
||||
|
||||
Ordinary new inputs are not the problem: user messages, committed events, compacted summaries, and tool results are expected additions to history. The avoidable problem is changing prior or side-channel context in a way that is neither durable nor explainable from records.
|
||||
|
||||
## Pruning and compaction
|
||||
|
||||
Pruning is a request-time packing operation that chooses which existing history to include under a token budget. Compaction is a durable history operation that creates a new summarized state and must be committed.
|
||||
|
||||
After compaction, context safety must be revalidated. A flag such as `just_compacted` must not suppress safety checks, because compact output can itself be too large or malformed for the next request.
|
||||
|
||||
## UI and orchestration consequences
|
||||
|
||||
The TUI should display committed events rather than inventing transcript blocks. Orchestration should treat child notifications as prompts to inspect state, not as state themselves.
|
||||
|
||||
This keeps future turns able to answer: "what did the agent know, when did it know it, and where is that recorded?"
|
||||
@@ -0,0 +1,38 @@
|
||||
# Memory and Knowledge
|
||||
|
||||
Yoi memory is generated context, not project authority.
|
||||
|
||||
The authoritative record for work is still code, git history, work item files, tickets, session logs, and explicit user instruction. Memory helps the agent retrieve durable preferences and prior rationale, but it must not replace the records that made those facts true.
|
||||
|
||||
## Record types
|
||||
|
||||
- `summary.md` is resident background context for normal Pods.
|
||||
- `decisions/` stores durable decisions that are useful across turns.
|
||||
- `requests/` stores durable user requests and preferences.
|
||||
- `.yoi/knowledge/` stores curated Knowledge records when available.
|
||||
- `_logs/` stores append-only audit observations.
|
||||
- `_staging/` is generated candidate state before consolidation.
|
||||
|
||||
Generated `.yoi/memory` is personal/generated state in this repository. Curated workflow/Knowledge assets may be tracked separately when intended.
|
||||
|
||||
## What memory should not do
|
||||
|
||||
Memory should not duplicate authoritative project records. Do not copy ticket threads, TODO lists, implementation reports, or full docs into memory merely to make them resident.
|
||||
|
||||
The useful memory is the small part that changes future behavior: a policy, a rationale, a user preference, or a durable conclusion that would otherwise be hard to find.
|
||||
|
||||
## Lookup policy
|
||||
|
||||
Agents should use memory and Knowledge when the request depends on prior decisions, historical rationale, project workflow, or durable preferences.
|
||||
|
||||
Agents should not query memory every turn. Local repository files, current user instructions, command output, and tickets are more authoritative for exact current state.
|
||||
|
||||
## Audit and mutation
|
||||
|
||||
Memory extraction/consolidation writes append-only observations under `_logs`. No-op and idle notices belong there rather than in user-facing UI.
|
||||
|
||||
Memory mutation should be explicit work or part of the configured memory maintenance path. Casual edits during unrelated tasks make memory harder to trust.
|
||||
|
||||
## Language
|
||||
|
||||
Memory follows configured memory language policy. Conversation prose follows the user's language unless configured otherwise.
|
||||
@@ -0,0 +1,33 @@
|
||||
# Design overview
|
||||
|
||||
Yoi is organized around one rule: durable authority must live in explicit records, while runtime conveniences remain reconstructable hints.
|
||||
|
||||
That rule shapes the crate split. The runtime can restart, attach, compact, or delegate work without relying on hidden controller state, and developer tooling can inspect the records that explain why an agent acted.
|
||||
|
||||
## Core layers
|
||||
|
||||
- `yoi` owns the product CLI and top-level command shape. It is the façade that wires profile selection, memory linting, and normal TUI launch.
|
||||
- `pod` turns a `Worker` into a named runtime entity with scope, session persistence, protocol handling, tools, and Pod metadata integration.
|
||||
- `llm-worker` owns model-facing turns: history append, retries, continuation, pruning/compaction mechanics, tool loops, and provider-independent callbacks.
|
||||
- `session-store` owns replayable append-only conversation/session logs.
|
||||
- `pod-store` owns current Pod metadata keyed by Pod name.
|
||||
- `protocol` defines the socket message boundary between clients and Pods.
|
||||
- `client` contains reusable one-shot socket/runtime-command mechanics so lower crates do not depend on the product CLI.
|
||||
- `manifest` resolves Profiles, Manifests, model/provider references, scopes, prompts, and tool permission policy into a runtime contract.
|
||||
- `tools` implements built-in tools with bounded output and policy-aware execution.
|
||||
- `memory` owns generated memory, Knowledge records, linting, staging, and audit observations.
|
||||
- `tui` is a UI over Pod authority; it should not invent durable state.
|
||||
|
||||
## Why these boundaries exist
|
||||
|
||||
The Worker should not know process identity, Pod names, live sockets, spawned children, or UI state. It should know how to run an LLM turn over committed history and tools.
|
||||
|
||||
The Pod should not make provider-specific wire decisions. It coordinates runtime identity, persistence, scope, and protocol delivery around a Worker.
|
||||
|
||||
The TUI should not be an alternate source of truth. It may queue local input, show optimistic affordances, and render snapshots, but durable state comes from Pod/session records.
|
||||
|
||||
The CLI should own product command shape. Other crates should expose library APIs and typed runtime commands rather than re-parsing product arguments.
|
||||
|
||||
## Documentation boundary
|
||||
|
||||
Design docs explain the constraints that code must preserve. They should not duplicate every public type, exact schema field, or command output; those are owned by code, tests, and work items.
|
||||
@@ -0,0 +1,39 @@
|
||||
# Pod, session, and state authority
|
||||
|
||||
Yoi separates replayable history from current Pod identity because they answer different questions.
|
||||
|
||||
A session log answers: "what happened and what can be replayed?" Pod metadata answers: "what does this Pod name currently refer to?" Live sockets and registries answer only: "what seems reachable right now?"
|
||||
|
||||
## Session logs
|
||||
|
||||
Session JSONL is the durable replay record. It contains committed user inputs, assistant items, tool results, system/runtime events that must explain later behavior, segment boundaries, and persisted effective snapshots needed to understand a run.
|
||||
|
||||
The session log should be append-oriented and schema drift should be compile-visible. Compatibility shims that silently reinterpret old plural/current entries make future readers less safe.
|
||||
|
||||
Session logs do not own current Pod-name state. A historical session can be replayable without being the active session for a Pod name.
|
||||
|
||||
## Pod metadata
|
||||
|
||||
Pod metadata is the current-state layer keyed by Pod name. It records active/pending session pointers, resolved manifest snapshots, current delegation metadata, spawned-child visibility, and restoration information.
|
||||
|
||||
This avoids reconstructing current Pod state by scanning every session log. It also gives `--pod <name>`, TUI resume, `ListPods`, and `RestorePod` a single current authority.
|
||||
|
||||
Pod metadata should stay thin. It is not a second transcript, and it should not duplicate model conversation content.
|
||||
|
||||
## Live runtime hints
|
||||
|
||||
Sockets, process registries, and runtime files are liveness hints. They are useful for attach, status probing, and fast discovery, but they are not final proof that work completed or that a Pod's state changed durably.
|
||||
|
||||
A reachable pending Pod should be visible even if durable logs have not materialized yet. Missing restore labels should degrade labels and diagnostics, not hide a live attachable Pod.
|
||||
|
||||
## Spawned children and delegation
|
||||
|
||||
Parent-visible children are sourced from Pod metadata, not from a transient runtime mirror. Restoring a parent should reconstruct reachable children where possible and keep stopped-but-restorable children visible when metadata supports it.
|
||||
|
||||
Delegated write scope is a capability loan. Stopping, shutting down, or pruning a child must reclaim the parent's effective write permissions while preserving explicit base denies.
|
||||
|
||||
## Notifications are not authority
|
||||
|
||||
Pod completion notifications are UX hints. Before treating delegated work as complete, inspect queryable evidence: child output, session/log state, worktree status, diffs, and validation output.
|
||||
|
||||
This is why orchestration code should expose state-aware operations such as `ListPods` and `RestorePod`, rather than letting a background alert decide workflow state by itself.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Profiles, Manifests, and prompts
|
||||
|
||||
Profiles are reusable recipes. Resolved Manifests are runtime contracts. Prompt resources are managed assets. Keeping those layers separate prevents runtime state from leaking into reusable configuration.
|
||||
|
||||
## Profiles
|
||||
|
||||
A Profile describes how a Pod should normally be built: worker language, model/provider selectors, prompt choices, tool policy defaults, and other reusable preferences.
|
||||
|
||||
A Profile should not contain runtime-bound fields:
|
||||
|
||||
- `pod.name`
|
||||
- concrete delegated `scope.allow`
|
||||
- sockets or process identifiers
|
||||
- session pointers
|
||||
- restored spawned-child state
|
||||
- raw secret values
|
||||
|
||||
Those fields depend on one run, one parent, or one machine. Putting them in a reusable Profile makes reuse unsafe.
|
||||
|
||||
Yoi is Lua Profile first. Lua gives project/user authors a controlled recipe layer through host-provided `require("yoi")` modules without treating Nix as runtime authoring. Nix remains useful for packaging and development records.
|
||||
|
||||
## Manifests
|
||||
|
||||
A resolved Manifest is the concrete contract used to create or restore a Pod. It carries defaults, resolved paths, permissions, scope, prompt references, provider/model decisions, and runtime identity.
|
||||
|
||||
Source/partial layers may omit fields. Resolved manifests should be explicit enough that Pod creation does not depend on ambient configuration later changing under it.
|
||||
|
||||
`--manifest <path>` exists as an explicit low-level escape hatch. Normal fresh startup should select a Profile through `profiles.toml` / builtin defaults rather than ambient manifest cascades.
|
||||
|
||||
## Spawned Pods
|
||||
|
||||
`SpawnPod.profile` is optional and resolves through defaults when omitted. The only concrete capability delegation in the tool call is `SpawnPod.scope`, and it must be a subset of the parent's effective scope.
|
||||
|
||||
`inherit` derives reusable settings from the parent's resolved Manifest while replacing child identity and delegated scope. It should not blindly reuse the parent's original Profile source or runtime state.
|
||||
|
||||
## Prompt resources
|
||||
|
||||
Prompts live under `resources/prompts` so builtins, project overrides, and user overrides have one asset boundary.
|
||||
|
||||
The prompt layer should explain policy and behavior, but it should not smuggle volatile state into model context. Runtime facts that affect later turns must still go through history.
|
||||
|
||||
Builtin resources should be embedded at compile time. User/project profiles, explicit profile paths, prompt overlays, provider/model overrides, and explicit manifests remain filesystem-based.
|
||||
|
||||
## Why this separation matters
|
||||
|
||||
Without this split, configuration becomes unreproducible: a Profile might accidentally depend on a parent Pod's socket, a prompt override might act like hidden state, or a restored Pod might observe different defaults than the run that created it.
|
||||
|
||||
The boundaries make it clear which information is reusable authoring, which is resolved runtime contract, and which is durable run history.
|
||||
@@ -0,0 +1,45 @@
|
||||
# Provider and model boundary
|
||||
|
||||
The Worker should be provider-independent. Provider-specific wire formats, auth mechanisms, model catalogs, reasoning knobs, and terminal error shapes belong below or beside it, not inside ordinary turn orchestration.
|
||||
|
||||
## Worker responsibility
|
||||
|
||||
`llm-worker` owns turn lifecycle:
|
||||
|
||||
- committed history append
|
||||
- tool loops
|
||||
- retry before stream start
|
||||
- continuation after safe partial output
|
||||
- pruning/compaction decisions
|
||||
- provider-neutral callbacks and events
|
||||
|
||||
It should not know where a Codex OAuth token lives, how a provider encodes reasoning effort, or which environment variables a provider once supported.
|
||||
|
||||
## Provider responsibility
|
||||
|
||||
Provider/model layers own:
|
||||
|
||||
- resolving model aliases and builtin model metadata
|
||||
- provider-specific request/response conversion
|
||||
- auth integration
|
||||
- context/window metadata
|
||||
- terminal error classification
|
||||
- trace points around HTTP/auth/body/SSE lifecycle
|
||||
|
||||
External API facts change quickly. Repository docs should record Yoi's boundary decisions, not snapshots of vendor documentation.
|
||||
|
||||
## Secrets
|
||||
|
||||
Local credentials use explicit secret references. Secret values must not appear in diagnostics, Debug output, CLI/TUI output, work items, docs, session logs, model context, or persisted plaintext store files.
|
||||
|
||||
Codex OAuth remains a separate integration because its existing `auth.json` / `CODEX_HOME` shape is not the same as normal provider secret refs.
|
||||
|
||||
## Error and trace semantics
|
||||
|
||||
Provider terminal errors that are displayed live, such as context-length failures, must persist as errored runs rather than successful empty turns.
|
||||
|
||||
Event trace sidecars are optional parsed lifecycle traces. They are not complete raw SSE logs and should not become a hidden source of model context.
|
||||
|
||||
## Why this boundary exists
|
||||
|
||||
Provider APIs drift. If provider details leak into Worker logic, every new model behavior risks changing core orchestration. Keeping the boundary explicit lets Yoi adapt provider integrations while preserving stable turn semantics.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Tool permissions and scope
|
||||
|
||||
Yoi treats tools as explicit capabilities. Model-visible tool names are not permission by themselves; the resolved Manifest and Pod scope decide whether a call is allowed.
|
||||
|
||||
## Permission policy
|
||||
|
||||
Tool permissions are built into PreToolCall policy.
|
||||
|
||||
- `allow` permits the call to proceed.
|
||||
- `deny` rejects only that call with a synthetic error result.
|
||||
- `ask` fails closed until a real approval/resume protocol exists.
|
||||
|
||||
Failing closed matters because an unresolved approval state is not the same as permission. A future approval flow must be able to pause and resume the same unresolved call, not merely ask the model to retry later.
|
||||
|
||||
## Filesystem scope
|
||||
|
||||
Filesystem scope is separate from tool allow/deny. A file tool may be registered and permitted, but the concrete path still must be inside readable or writable scope.
|
||||
|
||||
Symlinks do not grant extra authority. Access decisions should be made on the canonical target when it exists, and broken or out-of-scope links should produce diagnostics rather than escaping scope.
|
||||
|
||||
Directory traversal tools should not follow symlink directories as a way around scope. If an external checkout is needed, add its real path to read scope.
|
||||
|
||||
## Delegation
|
||||
|
||||
Child Pods receive an explicit subset of the parent's scope. Delegation is a capability loan, not a copy of all parent authority.
|
||||
|
||||
When a child stops, shuts down, or is pruned as unreachable, delegated write permissions must be reclaimed. Explicit base denies remain in force.
|
||||
|
||||
## Tool output
|
||||
|
||||
Tool output should be bounded before it enters history/model context. The system may truncate or summarize mechanical output boundaries, but it should not hide the fact that a tool call happened or fabricate successful results.
|
||||
|
||||
Network tools such as WebSearch/WebFetch are disabled/unconfigured by default, fail closed, and need explicit manifest/profile configuration. Their outputs are untrusted content and must remain bounded.
|
||||
|
||||
## Why this design exists
|
||||
|
||||
LLM tool calls are suggestions from an untrusted planner. Yoi can let the model propose operations while keeping final authority in manifest policy, scope checks, and durable records.
|
||||
Reference in New Issue
Block a user