docs(processes): process spawn policy, hardening and redaction
extensions.md: api.Processes and api.Redactor, ExecAsync on the policy, ProcessSpawnEvent, ctx.TimeZone and PrepareProcessAsync, and a Spawning processes section. architecture.md: Processes slice, the Abstractions contract, a Processes and secrets section, and where the loop redacts. CLAUDE.md: Processes slice and bullet. Spec: status implemented (scheduler and MCP spawners come with their specs); a hook returning null fails the spawn; test projects consume the one-shot hardening from a module initializer; the Hardener seam; 'allow: none' in the Telegram log line.
This commit is contained in:
@@ -12,8 +12,10 @@ tests/* xunit
|
||||
|
||||
`Abstractions` is deliberately small and stable: messages and content blocks, `Model` and `IProvider`,
|
||||
the streaming event protocol, `ITool`, the extension API and its events, session entries and stores,
|
||||
`IUserInterface`, and the `AgentSessionEvent` family a UI subscribes to. Extensions reference only this
|
||||
assembly, which is what allows them to be loaded from separate directories.
|
||||
`IUserInterface`, the `AgentSessionEvent` family a UI subscribes to, and the process contract in `Processes/`
|
||||
(`IProcessSpawnPolicy`, `ProcessSpawnRequest`/`ProcessSpawnContext`, `ProcessSpawnHook`, `ProcessSpawnEvent`,
|
||||
`ISecretRedactor`). Extensions reference only this assembly, which is what allows them to be loaded from separate
|
||||
directories.
|
||||
|
||||
## Vertical slices
|
||||
|
||||
@@ -30,6 +32,7 @@ owns everything for that feature and exposes one entry type:
|
||||
| `SystemPrompt` | `SystemPromptBuilder` | Base prompt, tools section, guidelines, project context files, environment |
|
||||
| `Compaction` | `Compactor` | When to compact, what to summarise, the summary prompt |
|
||||
| `Agent` | `AgentSession`, `AgentSessionFactory` | The conversation facade and the loop |
|
||||
| `Processes` | `ProcessSpawnPolicy` | The environment, command and cwd of every agent-requested process (deny-list, allow-list, secret values, host hooks), host hardening, secret redaction |
|
||||
|
||||
Slices talk to each other only through their entry types and the Abstractions contracts. `AgentSession`
|
||||
is the composition root of one conversation; a host never touches the other slices directly.
|
||||
@@ -70,6 +73,8 @@ loop:
|
||||
context → extensions may rewrite the message list
|
||||
stream the provider → message_start / message_update* / message_end (with retry + overflow recovery)
|
||||
tool calls? → tool_call hook, execute (parallel by default), tool_result hook, tool result messages
|
||||
(every result, progress update and error is redacted before tool_result handlers,
|
||||
and again after them, before tool_execution_end and the persisted message)
|
||||
turn_end
|
||||
more tool calls or steering messages or continue requested? → next turn
|
||||
follow-up queued? → next turn
|
||||
@@ -101,6 +106,34 @@ Two independent event systems exist:
|
||||
(`Block`, `Cancel`, `MarkHandled`) or setting properties (`Text`, `Payload`, `Message`, `Override`).
|
||||
Handlers run sequentially in load order and see each other's changes; some events short-circuit.
|
||||
|
||||
## Processes and secrets
|
||||
|
||||
Every process started on the agent's behalf (`bash`, `IExtensionApi.ExecAsync`, and, with their specs, scheduler
|
||||
scripts and MCP servers) gets its command, cwd and environment from one `IProcessSpawnPolicy`, configured through
|
||||
`AgenticCoreOptions.ProcessEnvironment`:
|
||||
|
||||
```
|
||||
inherit the host environment → filter (Deny, Allow, SecretValues) → explicit request.Environment (never filtered)
|
||||
→ TZ from TimeZoneId → host hooks → ProcessSpawnEvent to extensions (only in a session, via PrepareProcessAsync)
|
||||
```
|
||||
|
||||
The result is the child's complete environment; spawners clear `ProcessStartInfo.Environment` and copy it in. A
|
||||
throwing hook or handler fails the spawn instead of falling back to the inherited environment.
|
||||
|
||||
`HostProcessHardening` makes the host non-dumpable on Linux (`prctl(PR_SET_DUMPABLE, 0)`), so a same-user child
|
||||
can't read `/proc/<host pid>/environ` or `ptrace` the host. It runs from the `ProcessSpawnPolicy` constructor, or
|
||||
explicitly from a host at startup (the Telegram host does), and only once. It warns when `DOTNET_EnableDiagnostics=0`
|
||||
is missing, since the diagnostic socket would hand out the same environment. `HardenHostProcess = false` turns it off
|
||||
for debugging. Test projects make the one call with the flag off from a module initializer, so the test runner stays
|
||||
dumpable.
|
||||
|
||||
`ISecretRedactor` replaces every `SecretValues` entry with `[REDACTED]`. `AgentSession` applies it to tool
|
||||
results, progress updates and error messages (see the loop above), so every tool is covered in one place.
|
||||
|
||||
The Claude Code provider's own `claude` processes are outside the policy on purpose: they run the model and already
|
||||
get an allow-listed environment. None of this is a sandbox: the agent still runs as the host's OS user and can read
|
||||
the files that user can read.
|
||||
|
||||
## Persistence
|
||||
|
||||
A session is a tree of entries (`message`, `model_change`, `thinking_level_change`, `compaction`, `custom`,
|
||||
|
||||
Reference in New Issue
Block a user