added rant
This commit is contained in:
parent
a1ad8b843f
commit
67c77d568e
3 changed files with 104 additions and 73 deletions
|
|
@ -11,6 +11,16 @@ These principles are the single canonical source of truth for the project. All d
|
|||
- Exclusive C14N on ingress and egress.
|
||||
- Malformed XML repaired on ingress; repairs logged in `<huh/>` metadata.
|
||||
|
||||
## Identity Injection & Handler Purity
|
||||
- Handlers are pure, stateless functions with no knowledge of routing, thread context, or their own registered name.
|
||||
- On ingress (external or gateway messages): <from> is provided and authenticated by the client/gateway (enforced by envelope validation).
|
||||
- On response generation (after handler execution and multi-payload extraction):
|
||||
- The dispatcher injects <from> using the executing listener's registered name (e.g., "calculator.add" or "researcher").
|
||||
- For meta/primitive responses: <from> is injected as "core".
|
||||
- <thread> is inherited from the incoming message (or assigned/updated for primitives like spawn-thread).
|
||||
- <to> remains optional and rarely used.
|
||||
- This ensures every enveloped message has a trustworthy, auditable <from> without handler involvement, preventing spoofing and keeping capability code minimal/testable.
|
||||
|
||||
## Configuration & Composition
|
||||
- YAML file (`organism.yaml`) is the bootstrap source of truth, loaded at startup.
|
||||
- Defines initial listeners, agents, gateways, meta privileges, and OOB channel configuration.
|
||||
|
|
@ -46,6 +56,7 @@ These principles are the single canonical source of truth for the project. All d
|
|||
- Subthreading natively supported via hierarchical thread IDs and primitives (e.g., reserved payload to spawn "parent.sub1").
|
||||
- Optional structured constructs like `<todo-until/>` for visible planning.
|
||||
- No hidden loops or state machines; all reasoning steps are visible messages.
|
||||
- Thread management follows the dynamic call tracing model (see thread-management.md). Paths are built by appending target listener names on emission, with automatic popping on responses. Agents remain oblivious, enabling natural delegation and parallelism.
|
||||
|
||||
## Security & Sovereignty
|
||||
- Privileged messages (per `privileged-msg.xsd`) handled exclusively on dedicated OOB channel.
|
||||
|
|
@ -73,4 +84,9 @@ These principles are the single canonical source of truth for the project. All d
|
|||
- Single process, async non-blocking.
|
||||
- XML is the sovereign wire format; everything else is implementation detail.
|
||||
|
||||
## Scheduled Computation
|
||||
- Timers and delays implemented as normal listeners using async sleeps.
|
||||
- Caller idles naturally; wakeup messages bubble back via standard tracing.
|
||||
- Enables recurrent tasks (e.g., periodic monitoring) without blocking or external schedulers.
|
||||
|
||||
These principles are now locked. All existing docs will be updated to match this file exactly. Future changes require explicit discussion and amendment here first.
|
||||
|
|
@ -4,25 +4,56 @@ The AgentServer message pump processes individual messages through a single, lin
|
|||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["WebSocket Ingress\n(enqueue to thread buffer)"] --> B["Dispatcher Loop:\nSelect next message\n(per thread_scheduling strategy)"]
|
||||
B --> C["Repair + Exclusive C14N"]
|
||||
C --> D["Envelope Validation (lxml)"]
|
||||
D --> E["Extract Payload Tree"]
|
||||
E --> F{"Payload Namespace?"}
|
||||
F -->|meta/v1| G["Core Meta Handler\n(introspection & reserved primitives)"]
|
||||
F -->|capability| H["Route by (namespace, root)"]
|
||||
H --> I["Validate Payload vs Listener XSD (lxml)"]
|
||||
I --> J["Deserialize to Dataclass Instance (xmlable)"]
|
||||
J --> K["Call handler(instance) → raw bytes"]
|
||||
K --> L["Wrap bytes in <dummy></dummy>"]
|
||||
L --> M["Repair/Parse → Extract all top-level payloads"]
|
||||
M --> N["Wrap each payload in separate envelope\n(enqueue to target thread buffers)"]
|
||||
G --> N
|
||||
N --> O["Exclusive C14N + Sign"]
|
||||
O --> P["WebSocket Egress\n(sequential per connection)"]
|
||||
P --> B["Continue dispatcher loop if buffers non-empty"]
|
||||
subgraph MessagePump
|
||||
subgraph Init
|
||||
start([Start])
|
||||
raw[/Optional<br>Raw Bytes/]
|
||||
wrapstart["Wrap<br><start>{...}</start>"]
|
||||
end
|
||||
enq1([QUEUE 1])
|
||||
rawwaiting{Raw<br>Msg<br>Waiting?}
|
||||
waitRaw([Wait])
|
||||
subgraph Process
|
||||
extract["Extract<br>Tree"]
|
||||
split["Split<br>Tree"]
|
||||
subgraph foreach [For Each Message]
|
||||
getmsg[Get Msg]
|
||||
badTo{<To> Missing?}
|
||||
endnoto([Discard])
|
||||
addfrom["Add .from"]
|
||||
repair[Repair + C14N]
|
||||
validate[Validate]
|
||||
invalidMsg{Bad<br>Message?}
|
||||
badmsg([Discard])
|
||||
more{More?}
|
||||
end
|
||||
enqueue([QUEUE 2])
|
||||
xmlWaiting{XML<br>Waiting?}
|
||||
waitXml([Wait])
|
||||
subgraph Async
|
||||
lookup[Lookup Listener]
|
||||
route[Route]
|
||||
wait[await Response]
|
||||
wrap["Wrap<br><from>{...}</from>"]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
start --> raw --> wrapstart --> enq1 --> rawwaiting
|
||||
rawwaiting --> |NO| waitRaw
|
||||
rawwaiting ---> |YES| extract
|
||||
extract --> split --> foreach
|
||||
getmsg --> badTo
|
||||
badTo --> |YES| endnoto
|
||||
badTo --> |NO| addfrom --> repair --> validate --> invalidMsg
|
||||
invalidMsg ---> |NO| more --> |Yes| getmsg
|
||||
invalidMsg --> |YES| badmsg
|
||||
more --> |NO| enqueue
|
||||
enqueue --> xmlWaiting
|
||||
xmlWaiting --> |NO| waitXml
|
||||
xmlWaiting ---> |YES| lookup --> route --> wait --> wrap --> enq1
|
||||
|
||||
```
|
||||
|
||||
## Detailed Stages (Per-Message)
|
||||
|
||||
1. **Ingress/Enqueue**: Raw bytes → repair → preliminary tree → enqueue to target thread buffer.
|
||||
|
|
|
|||
|
|
@ -1,65 +1,49 @@
|
|||
# Thread Management in AgentServer v2.0
|
||||
**January 03, 2026**
|
||||
**January 04, 2026**
|
||||
|
||||
This document clarifies the thread ID system, subthreading mechanics, and internals. It supplements the Core Architectural Principles — hierarchical dot notation examples there reflect the wire format.
|
||||
## Overview
|
||||
Thread IDs are dynamic hierarchical paths that trace the exact call chain through the organism. The message pump builds and maintains them automatically.
|
||||
Agents, tools, and handlers are **completely oblivious** to thread IDs — they never read, copy, or emit them.
|
||||
|
||||
## Wire Format: Hierarchical String IDs
|
||||
- Mandatory `<thread/>` contains a **server-assigned** hierarchical string (dot notation, e.g., "root", "root.research", "root.research.images").
|
||||
- Root IDs: Short, opaque, server-generated (e.g., "sess-abcd1234").
|
||||
- Sub-IDs: Relative extensions for readability.
|
||||
- Benefits: LLM/human-friendly copying, natural tree structure for logs/GUI.
|
||||
No explicit spawn primitives are required. Topology emerges solely from the shape (single vs multiple) and targets of emitted payloads.
|
||||
|
||||
## Server Assignment Only
|
||||
The organism assigns all final IDs — agents never invent them.
|
||||
## Wire Format
|
||||
- Mandatory `<thread/>` contains a readable dot-notation string (e.g., `sess-abcd1234.researcher.search.calc`).
|
||||
- Root segment is an opaque server-generated session identifier.
|
||||
- Subsequent segments are registered listener short names appended during routing.
|
||||
|
||||
- **Root initiation**: Client suggests or server auto-generates on first message; uniqueness enforced.
|
||||
- **Subthread spawning**: Explicit reserved payload for intent clarity:
|
||||
```xml
|
||||
<spawn-thread suggested_sub_id="research"> <!-- optional relative label -->
|
||||
<initial-payload> <!-- optional bootstrap fragment -->
|
||||
<!-- any valid payload -->
|
||||
</initial-payload>
|
||||
</spawn-thread>
|
||||
```
|
||||
Core handler:
|
||||
- Appends label (or auto-short if omitted).
|
||||
- Resolves uniqueness conflicts (append "-1" etc.).
|
||||
- Creates queue + seeds bootstrap.
|
||||
- **Always responds** in current thread:
|
||||
```xml
|
||||
<thread-spawned
|
||||
assigned_id="root.research"
|
||||
parent_id="root"
|
||||
message="Thread spawned successfully."/>
|
||||
```
|
||||
## Dynamic Call Tracing Rules
|
||||
1. **Emission** (from current path `parent.path`):
|
||||
- After handler execution and multi-payload extraction:
|
||||
- For each payload, determine target listener name.
|
||||
- Append that name → new path = `parent.path.target_name`.
|
||||
- Single payload → sequential delegation (one deepened path).
|
||||
- Multiple payloads → parallel forks (one deepened path per target, each with its own queue).
|
||||
|
||||
## Error Handling (No Silent Failure)
|
||||
- Unknown `<thread/>` ID → no implicit creation.
|
||||
- **Always inject** system error into parent thread (or root):
|
||||
```xml
|
||||
<system-thread-error
|
||||
unknown_id="root.badname"
|
||||
code="unknown_thread"
|
||||
message="Unknown thread; emit <spawn-thread/> to create or correct ID."/>
|
||||
```
|
||||
- LLM sees error immediately, retries without hanging.
|
||||
- Logs warning for monitoring.
|
||||
2. **Response Bubbling**:
|
||||
- On emission from path `parent.path.listener_name`:
|
||||
- Pump removes the last segment.
|
||||
- Routes all response payloads to `parent.path`.
|
||||
- Injects `<from>` as the responding listener’s registered name.
|
||||
- Replies land directly in the immediate parent’s history.
|
||||
|
||||
## Internals
|
||||
- Per-thread queues: dict[str, Queue].
|
||||
- Scheduling via `organism.yaml`:
|
||||
```yaml
|
||||
thread_scheduling: "breadth-first" # or "depth-first" (default: breadth-first)
|
||||
```
|
||||
- Depth from dot count.
|
||||
- Optional hidden UUID mapping for extra safety (implementation detail).
|
||||
3. **Broadcast**:
|
||||
- Single payload to a capability with multiple gateways → fanned out.
|
||||
- All responses pop to the same parent path, distinguished by their individual `<from>` values.
|
||||
|
||||
## Design Rationale
|
||||
- Explicit spawn = clear intent + bootstrap hook.
|
||||
- Mandatory feedback = no LLM limbo.
|
||||
- Readable IDs = easy copying without UUID mangling.
|
||||
- Server control = sovereignty + no collisions.
|
||||
4. **Internal Uniqueness**:
|
||||
- Readable paths are mapped to UUIDs via a bidirectional resolver.
|
||||
- New unique logical path → new UUID and queue.
|
||||
- Ensures collision-free scheduling while keeping wire paths clean and meaningful.
|
||||
|
||||
Future: Alias registry, thread metadata primitives.
|
||||
## Termination
|
||||
- Paths become idle when their queues empty.
|
||||
- Detection of `<final-answer>` (meta namespace) in the root path triggers terminal egress to the originating client.
|
||||
|
||||
The organism branches reliably, visibly, and recoverably.
|
||||
## Key Advantages
|
||||
- Complete thread obliviousness eliminates prompt bloat and copy errors.
|
||||
- Natural sequential delegation and parallelism without manual management.
|
||||
- Full provenance via trustworthy `<from>` injection.
|
||||
- Audit trails are self-documenting call traces built from registered capability names.
|
||||
|
||||
The organism owns memory and topology. Threads are the living, transparent traces of computation.
|
||||
Loading…
Reference in a new issue