A local-first event pipeline for independent agents, built on Jazz.
thought-stream spec proposals.md
17 kB

Agent proposals #

Boundary #

A proposal tool lets a sandboxed conversational model ask trusted machinery to preserve a bounded memory change, self-correction, or focus declaration. It does not let the model edit a file, append a Jazz event directly, activate a focus, approve its own request, label training data, start training, promote an adapter, publish, send, or execute another provider turn.

The authority chain is:

model tool call → sandbox validation → trusted snapshot binding → atomic agent-proposal event → human decision → deterministic correction projector or bounded memory materializer

Model request authority is real. Canonicalization authority is not.

Declaration #

Proposal tools are separate from read-only evidence acquisition:

policy:
  tools: []
  proposals:
    - memory-change
    - self-correction
    - focus-declaration
  externalActions: false

Only a standard Pi declaration using telegram-conversation context, conversation-text output, sensitive input, exact subscribed documents, and stream.thought.derived.message.observation output may declare proposals. Repair, deterministic, Letta Agent SDK, ATProto, review, and conceptualizer declarations may not. externalActions remains false.

Changing proposal capability requires a declaration-version bump. The proposal set and exact capability manifest are included in the declaration fingerprint and retry-stable context snapshot.

Snapshot capabilities #

The trusted Telegram context builder derives proposal capability from evidence already admitted by that exact context snapshot.

Memory target #

memory-change is available only when the snapshot contains the exact current memory.md version from filesystem:telegram-agent-context. The capability binds:

  • source;
  • stable document id;
  • normalized path memory.md;
  • immutable version id;
  • SHA-256;
  • content type.

The model never supplies a path, source, document id, version, hash, root, or materialization destination.

Correction targets #

self-correction targets only prior assistant turns admitted by the bounded transcript. Each target must have:

  • a same-chat delivered Telegram receipt;
  • exactly one completed run from an allowlisted conversation-history agent;
  • exactly one canonical output event;
  • the same Telegram source, chat, sender, and source root as its delivery receipt;
  • an output contract matching the completed run.

The target capability binds run id, output event id, delivery receipt event id, source root event id, and output-contract identity. Current, undelivered, omitted, unrelated, arbitrary, or multiple-output runs are unavailable.

Evidence ids #

A tool may cite only ids admitted by the snapshot:

  • selected source-message event ids;
  • selected assistant delivery receipt event ids;
  • the exact output event ids behind selected assistant turns.

The sandbox and trusted parent both reject any other evidence id. Evidence selection does not itself authorize a proposal target.

Sandbox tools #

The packet/result protocol carries a bounded proposal-capability object and is versioned independently from event schemas. The worker exposes only the declaration-selected fixed tools.

request_memory_change #

Arguments:

  • operation: append or replace-document;
  • proposed_text: 1–32,768 characters;
  • reason: 1–1,000 characters;
  • evidence_event_ids: unique array of at most 16 snapshot-admitted ids.

submit_correction #

Arguments:

  • target_output: exact snapshot-admitted output event id, or the literal string latest to select the most recent eligible assistant turn from the snapshot;
  • replacement: 1–4,096 characters;
  • reason: 1–1,000 characters;
  • evidence_event_ids: unique array of at most 16 snapshot-admitted ids.

propose_focus #

Arguments contain one complete strict declaration under the contract in focuses.md: identity/version, lineage, scope, objective, subscriptions, budgets, requested permissions, and retirement rule. The tool is intended for /focus <description> and creates proposal evidence only. The trusted parent recomputes the declaration fingerprint and fixes all authority flags false; event/source/capability/lineage resolution remains explicitly unchecked until a separate approval/compiler exists.

When target_output is latest, the trusted parent resolves it to the most recent correction target in the snapshot's correctionTargets array before validation. This lets the model say "correct my last reply" in plain language instead of copying an event id. The resolved id is the actual output event id used for all downstream authority, projection, and receipt purposes. The literal latest never appears in durable events or receipts.

All three tools use strict TypeBox schemas with no additional properties plus trusted Zod validation. The worker independently validates arguments against the packet capability. It captures at most three calls total and at most one call of each kind. A captured call returns a content-dark result with terminate: true. Pi uses tool_choice: auto. Every captured call in the assistant message must correspond one-to-one with the validated result packet. Arguments never enter traces.

The provider broker remains one-request-only. A proposal call never causes a tool-result follow-up request. Malformed, unknown, duplicate, too-many, oversized, or capability-escaping calls produce no proposal event.

The sandbox has no host filesystem, home, repository, Jazz, channel, provider credential, general network, or model-visible canonicalization handle.

Conversation output #

A valid proposal completion may contain one bounded visible text part plus one to three validated proposal calls. Thinking is non-authoritative and redacted as before.

If visible text exists, the trusted parent preserves it as the conversational summary and appends no proposal narration.

If a valid completion is tool-only, the trusted parent constructs one fixed conversational acknowledgment:

  • memory only: I saved that as a memory suggestion.
  • correction only: I saved that as a proposed correction.
  • both: I saved those as memory and correction suggestions.
  • focus only: I saved that as a focus proposal.

These acknowledgments describe durable proposals only. They do not claim application, approval, learning, training, publication, or future behavior. They do not contain event ids, run ids, output ids, agent ids, or any technical identifiers.

On later Telegram turns, durable proposal calls are reconstructed from the exact atomic completed-run receipt and validated proposal events, never from the acknowledgment sentence or redacted traces. Native history contains the canonical tool name/arguments, a normalized result stating that capture executed and created a durable inert proposal while approval/application remains pending, and then the delivered acknowledgment or model-authored text. If the completion receipt names no proposal, no tool history may be synthesized even when the visible text claims something was saved. Original provider call ids, literal result wording, and literal latest shorthand are not durable; reconstruction uses a stable synthetic id and the resolved correction target.

Tool calls are invalid in strict-JSON mode. Tool-call parts are excluded from semantic output persistence after one-to-one proposal validation.

Atomic proposal settlement #

A successful conversation run settles one transaction on the agent source containing, in order:

  1. canonical semantic output;
  2. zero to three proposal events;
  3. completed lifecycle event;
  4. completed run row;
  5. consumer progress.

A failed semantic output emits no proposal. Deterministic event ids make replay idempotent. A crash cannot leave durable progress or a completed run while dropping a returned proposal.

Memory proposal #

stream.thought.agent.memory-change.proposed@1 is sensitive, agent-authored, rooted in the current Telegram source root, and parented to the just-produced output event. Its strict payload contains:

  • literal proposalState: agent-proposed;
  • proposer run, output, trigger, agent id/version, declaration fingerprint, provider/model, and context-snapshot id;
  • exact parent-bound memory target;
  • validated operation, proposed text, text length/hash, reason, and evidence ids;
  • literal publicationEligible: false.

Correction proposal #

stream.thought.agent.correction.proposed@1 is sensitive, agent-authored, rooted in the target output's source root, and parented to the target output event. Its strict payload contains:

  • literal proposalState: agent-proposed;
  • proposer run/output/trigger/agent/version/declaration/provider/model/context-snapshot provenance;
  • exact target run, output, delivery receipt, source root, and output contract;
  • a parent-canonicalized full replacement output satisfying that output contract;
  • reason and evidence ids;
  • literal qualityEligible: false;
  • literal externalExportEligible: false;
  • literal publicationEligible: false.

A proposal is not a judgment and never changes effective output.

Focus proposal #

stream.thought.focus.declaration.proposed@1 is sensitive, agent-authored, rooted in the current Telegram source root, and parented to the just-produced output event. It contains the strict declaration and canonical fingerprint, exact proposer run/output/trigger/agent/declaration/provider/model/context provenance, explicit unchecked dependency resolution, and literal false authority for activation, data expansion, external actions, training, and adapter promotion. It does not enter the memory/correction decision or training paths.

Human decisions #

stream.thought.agent.proposal.decision@1 is an append-only, sensitive system event created only by the bounded local proposal CLI/API. It names one proposal and one disposition:

  • accept: use the proposed text or correction;
  • edit: use an exact bounded operator-supplied replacement;
  • reject: preserve refusal with no replacement.

The decision records proposal kind, submission id, actor, and literal human authority. Edit requires replacement text; accept/reject forbid it. One deterministic Jazz event identity per proposal is the concurrency boundary: same-payload retries converge even when they race, while any concurrent or later different payload conflicts rather than silently superseding the first. The submission id remains operator/idempotency provenance, not the uniqueness boundary.

The bounded local surface is proposal-list, proposal-decision, and proposal-project. proposal-decision reads edit text from an explicit file rather than a shell argument and prints ids/status only. proposal-project reconciles a decision left between append and projection after process interruption. There is no generic event mutation endpoint. Browser integration is a separate exact-live-inspector phase.

Correction projection #

An accepted or edited correction decision is projected through existing recordJudgment with:

  • kind correct;
  • criterion agent-self-correction;
  • criterion version 1;
  • human actor from the decision;
  • exact target delivery receipt;
  • decision event as feedback source;
  • qualityEligible: true;
  • externalExportEligible: false.

The replacement is reconstructed as a full canonical output under the frozen target contract. A rejected proposal creates no judgment. Re-running the projector is idempotent. Effective output changes only after the human-authorized judgment exists.

Memory materialization #

An accepted or edited memory decision may be materialized only against the configured context root for The Stream, source filesystem:telegram-agent-context, and path memory.md.

Before deciding, projecting, or writing, one shared trusted validator replays the complete proposal authority chain: completed proposer run, exact proposer output and trigger, declaration fingerprint, durable context-snapshot bytes and manifest, atomic completed-run receipt naming the proposal, admitted evidence ids, and exact snapshot memory/correction capability. Before writing, the materializer additionally revalidates:

  • proposal and decision schemas and lineage;
  • unresolved accepted/edit disposition;
  • current Jazz document pointer exactly matches proposal document id, version id, and SHA-256;
  • immutable base version content, source, path, content type, size, and hash;
  • configured root is a real directory, not a symlink;
  • memory.md is a regular file, not a symlink, and remains inside the root;
  • on-disk bytes match the frozen base version;
  • old and new Markdown have valid YAML frontmatter with the same nonempty id;
  • resulting file stays within the configured size limit.

No stale request is rebased. append preserves the frozen document and appends normalized text. replace-document replaces the whole document after frontmatter identity validation.

The materializer holds an owner-only root lock carrying boot id, PID, kernel process-start identity, and a random ownership token; a verified dead/rebooted/PID-reused owner is reclaimed, a live owner conflicts, and malformed or replaced lock evidence fails closed. It writes an owner-only temporary file, fsyncs, atomically renames, enforces mode 0600, and invokes the existing FilesystemConnector.scan() for the exact configured source. It then verifies the immutable new version, current pointer, file event, byte count, and SHA-256 before appending stream.thought.agent.memory-change.materialized@1.

After a proposal and decision have parsed and established trusted receipt ids, any content, stale-base, path, symlink, frontmatter, size, write, scan, or receipt failure appends stream.thought.agent.memory-change.materialization.failed@1 with a bounded content-dark reason code and no rejected content. Malformed or forged proposal/decision lineage fails before materialization and emits no synthetic receipt from untrusted ids. A materialization receipt is required before claiming the memory changed.

Private training export #

Agent proposals are excluded from every training projection.

private-training-export selects active schema-v2 judgments with qualityEligible: true, regardless of externalExportEligible, only when the complete exact private-provenance chain is present and revalidates: judgment, completed run, output, trigger, feedback, exact single-run delivery receipt, and retry-stable context snapshot. Missing or inconsistent provenance is excluded rather than represented as exact. The writer independently rejects any supplied example missing one of those ids. It remains separate from training-export.

The command requires:

  • an explicit destination file;
  • an explicit sensitive/private-data acknowledgment;
  • private-destination validation;
  • owner-only atomic output and manifest files.

The exact CLI is private-training-export --output <private-file.jsonl> --acknowledge-sensitive-private-training. It never writes examples to stdout. External export continues to require both quality and external-export eligibility and therefore excludes accepted self-corrections from The Stream.

Recovery and proof #

The /focus capability moves the Telegram declaration from v17 to v18. Activation must build the current sandbox bundle, freeze webhook ingress, stop v17 before starting v18, install matching v18 declaration/prompt/context, initialize v18 progress at the exact frozen Telegram source head under replay: now, and verify the v18 consumer's loaded source root and worker bundle before advertising the command. Register the Telegram menu only after v18 is ready; use telegram-menu-register --skip-help-notice unless a new help notice is intentional. Resume ingress only after progress/readiness checks. Bare /focus deterministic proof and one natural description-bearing /focus event/run/proposal/completion/delivery chain remain separate receipts; do not synthesize private inbound.

Test proof must cover native serialization/parsing, one provider request, tool-only acknowledgment, text-plus-tool preservation, malformed/unknown/duplicate/too-many/oversized calls, trace redaction, atomic settlement/replay, exact snapshot binding, proposal privacy and non-authority, memory decisions/materialization failures, correction projection, private export inclusion, external-export exclusion, and image/text/Telegram /correct regressions.

Natural proof remains separate and explicit. Activation itself creates no proposal and changes no memory.