Status: Design proposal
This document defines a prompt and harness design for the Gamma agent.
The design supports all GPT models in the initial rollout. The harness supplies the same behavior when a model lacks native loading features.
The design combines three reference patterns:
- Codex supplies the static prompt structure and skill discovery model.
- Devin supplies the dynamic context protocol and execution rules.
- Cowork supplies the artifact workflow and final delivery pattern.
The system prompt stays stable and cacheable. The harness adds small context blocks during a session.
The initial rollout includes:
- One stable root prompt
- A small set of stable prompt variants for known workflows
- All Gamma tool schemas loaded at session start
- Workflow and reference skill types
- Strict workflow skill contracts
- An explicit
skilltool - Typed runtime injections
- Persistent task state
- Skill recovery after compaction
- Explicit user yield control
The initial tool set contains approximately 20 tools. Dynamic tool loading adds complexity without enough prompt savings at this size.
The later rollout can include:
- Scoped knowledge pointers
- Knowledge body loading
- Dynamic tool loading
- Dynamic connector schema registration
- A server-owned remote notification channel
- Dedicated workflow evaluations
- Completion audits
The initial design preserves extension points for these features.
- Keep the static system prefix small and stable.
- Load detailed instructions only when the task requires them.
- Keep the initial tool schemas stable across requests.
- Make every instruction load visible in the transcript.
- Keep task state outside the model context.
- Restore active state after context compaction.
- Give every runtime injection one defined meaning.
- Apply workflow skills as strict execution contracts.
- Keep reference skills available as lookup material.
- Use one harness protocol for all supported GPT models.
Static cached prefix
├── Agent identity and safety rules
├── Instruction precedence
├── Runtime injection semantics
├── Skill selection rules
├── Yield semantics
├── Completion requirements
└── Stable tool schemas
Dynamic context protocol
├── Client state notes
├── External state updates
├── Guidance for the next action
└── Task state changes
On-demand loading
├── Workflow skills
└── Reference skills
Later extension points
├── Scoped knowledge pointers
├── Knowledge bodies
└── Dynamic tool schemas
Gamma already has a durable UI-state system. The runtime injection protocol extends this system. It does not replace it.
SessionUIState is the shared UI document for an agent session. The client and agent-server synchronize it with Yjs while the container is live.
It currently contains:
- Panes, tab instances, and the active tab in each pane
- Mobile chat and file-pane state
- Expanded workspace directories
- Recently viewed resources
- Research source labels and viewable files
- The active user-question form and its answers
- The current call to action
- The client schema version
- The UI projection of asynchronous cost records
There is no global active tab or current resource. Each pane owns its active tab. The same resource can appear in more than one tab instance or pane.
Each open tab has a stable tab-instance identifier and an ARI reference. The ARI identifies what the tab shows.
The current ARI schemes are:
file:for a session file or directorygamma:file:for a live Gamma documentform:for a user-question formbuiltin:for a client-owned view
An ARI can also include query state and a location inside the resource, such as a card. Tab identity uses the base resource. View state and location remain part of the complete reference.
The pane state describes what the interface displays. It does not declare one active artifact or one agent work target.
For each local UI edit, the agent client sends two forms of the same state to /sessions/:id/ui/sync:
- An incremental Yjs update
- A complete JSON snapshot
For a live session, the Gamma server proxy forwards the request to agent-server. The agent server applies the Yjs update, publishes the resulting update to connected clients, and persists state.json on the mounted session disk.
The client and server share ownership of this document. For example, the agent writes a form schema, the client writes answers, and the server delivers completed answers to the agent.
For a dormant session, no container is present. The Gamma server data plane reads the transcript and state.json from the Archil session disk. The /preview response lets the client render the conversation and panes without a live agent server.
The client seeds a local UI document from that preview. A dormant /ui/sync request sends the complete snapshot through the Gamma server proxy. The proxy writes the snapshot directly to state.json on Archil.
Dormant workspace reads and existing-file overwrites use the same proxy pattern. The proxy reads or writes the Archil session disk instead of forwarding to a container.
When the user sends a message from a dormant preview, the client performs this sequence:
- Check that the session is still dormant.
- Flush edited session files through the workspace proxy.
- Flush the current UI snapshot through
/ui/sync. - Resume the harness session.
- Connect to the new container after it mounts the same Archil disk.
- Send the user message to the live agent server.
During startup, agent-server restores the Pi transcript and the supported UI state from the mounted disk. It also rebuilds server-owned projections such as viewable files and cost state from their authoritative sources.
PromptClientContext is separate from SessionUIState. It describes the browser that submits one prompt, including its locale, viewport, pane capacity, and visible tab descriptions.
This context travels with the user message. It is not shared durable session state because two browser tabs can show the same session differently.
The static prefix contains rules that apply to every session. Its content remains stable across most turns.
The prefix contains:
- The Gamma agent identity
- The user-facing Gamma vocabulary
- The interface and artifact contract
- The security rules
- The instruction precedence rules
- The runtime injection types
- The skill contract
- The task state contract
- The yield contract
- The completion contract
Detailed CLI instructions belong in skills. The initial tool schemas remain stable across requests.
The skill manifest declares one of two types:
type SkillType = "workflow" | "reference";A workflow skill defines an ordered procedure. Examples include deck-generation, import, and gml-editing.
A reference skill supplies lookup material. Examples include gml-authoring, gml-cli, and ask-user-questions.
Workflow skills use the strict checklist contract. Reference skills do not use that contract.
The harness injects an <available_skills> block when the environment loads skills. The block contains each skill name, description, type, and source path.
<available_skills>
<skill
name="deck-generation"
type="workflow"
path="skills/deck-generation/SKILL.md"
>
TRIGGER for every new presentation request, including a request that names only a topic.
</skill>
</available_skills>The description is also the trigger condition. Each description must state the matching task in direct terms.
Useful description forms include:
TRIGGER when asked to create or edit a presentation.Use BEFORE writing JSX with Chakra UI components.Use for every new presentation request, including a request that names only a topic.
The agent applies these rules before its first task action:
- Identify every skill that matches the task.
- Load all matching skills in the first tool-call batch.
- Load matching skills in parallel.
- Read each loaded skill to its end.
- Resolve overlap only after all matching skills are loaded.
A progress message can share the first tool-call batch. The message cannot delay the skill loads.
The agent cannot defer a matching skill until a later task phase. Deferred selection is a skill-selection error.
The harness exposes one skill tool:
skill({ name: "deck-generation" });The tool returns the complete skill body. The tool result becomes part of the conversation context.
This mechanism makes skill use visible in the normal transcript.
When the agent loads a workflow skill, that skill becomes a primary instruction source.
The agent must:
- Follow every required step in order.
- Keep separate steps separate.
- Use the files, tools, and commands that the skill names.
- Troubleshoot a failed step before it continues.
- Complete every verification step.
- Review the checklist before it reports completion.
- Explain each deviation from a required step.
The agent cannot replace the procedure with its own shorter procedure. Similar output does not satisfy the workflow contract.
The agent can deviate only for an optional step or an unrecoverable error. The completion message must identify each deviation and its cause.
A preloaded workflow skill has the same contract as a skill that the agent loads through the skill tool.
The match activates the contract. The loading method does not change the contract.
For example, a preloaded deck-generation body remains an ordered checklist. The agent must complete its verification and repair steps.
A reference skill supplies information to a workflow skill or a direct task.
The agent reads a reference skill when another instruction directs the agent to that skill. The agent applies only the relevant reference material.
A reference skill does not create an ordered checklist unless its content declares one.
Each skill manifest can declare conflicts and precedence:
type SkillManifest = {
name: string;
type: "workflow" | "reference";
description: string;
conflictsWith?: string[];
requiresSkills?: string[];
};The harness rejects a conflicting skill set before task execution. The agent asks for direction only when the manifests cannot resolve the conflict.
Scoped knowledge is outside the initial rollout. The runtime injection protocol preserves a direct path for this feature.
Knowledge contains facts or constraints for a defined scope. It does not define a task procedure.
The harness injects small pointers before it injects complete knowledge bodies.
<knowledge_hints>
BLOCKING: Fetch each matching unread note before the first task action.
<knowledge
id="note-brand-images"
name="Workspace image rules"
scope="Use for image selection or image generation in this workspace."
source="workspace"
/>
</knowledge_hints>The harness selects candidate notes. The model performs the final scope match.
The agent fetches all matching notes in its next tool-call batch. It discards notes that do not match the task.
The harness exposes a knowledge tool:
read_workspace_rule({ knowledgeId: "note-brand-images" });The tool returns the complete body. A large body can return a file path for focused reading.
The system prompt defines one instruction order:
- Live user instructions
- Active playbook instructions
- User-authored workspace knowledge
- System-authored workspace knowledge
- General agent defaults
Security rules and platform restrictions remain authoritative at every level.
The runtime injection protocol moves trusted state into an active agent session. It keeps platform events separate from user messages. It also gives each event one delivery rule.
An injection is a notification. It is not the source of truth for an artifact. The agent reads the current artifact before it makes a related change.
The session protocol needs two fields:
type InjectionKind =
| "system_note"
| "system_guidance"
| "system_update"
| "client_view"
| "artifact_update"
| "workspace_update"
| "background_result";
type RuntimeInjection = {
kind: InjectionKind;
content: string;
};The content includes a file path or Gamma reference when the agent needs one. Add structured fields only after a concrete producer needs them.
The static system prefix defines each type one time.
<system_note>
Context only. Continue the current task.
</system_note>The agent absorbs a system_note. It does not acknowledge the note to the user.
<system_guidance>
Apply this instruction to the next action.
</system_guidance>The agent applies system_guidance to its next action. It does not expose the guidance to the user.
<system_update>
Evaluate this external state change.
</system_update>The agent evaluates a system_update. It acts, investigates, or records a deliberate dismissal.
Gamma also uses specific event types:
<client_view>
The user opened card 4.
</client_view>
<artifact_update>
The user saved changes to brief.xml.
</artifact_update>
<workspace_update>
Workspace instructions changed.
</workspace_update>The types have these meanings:
| Type | Meaning | Agent action |
|---|---|---|
system_note |
Passive context about the current task | Absorb the note and continue |
system_guidance |
A trusted instruction for the next action | Apply the instruction before the next action |
system_update |
A platform or service state change | Act, investigate, or record a dismissal |
client_view |
The current user view, focus, or selection | Use it as context for the next user turn |
artifact_update |
A task artifact changed outside the active agent action | Re-read the current artifact and reconcile the change |
workspace_update |
Workspace instructions, configuration, or available resources changed | Reload the affected workspace state |
background_result |
Nonurgent child work finished | Review it after the active run stops |
The injection kind selects the Pi delivery path:
const deliveryByKind = {
system_note: "next_turn",
system_guidance: "steer",
system_update: "steer",
client_view: "next_turn",
artifact_update: "steer",
workspace_update: "steer",
background_result: "follow_up",
} as const;During an active run, a steering message enters before the next model call. It does not interrupt the current tool call. A follow-up message waits until the active run stops.
During an idle session, a next-turn message waits for the next user turn. A steering event only starts a turn for an active task.
While the container is active, the Pi queue uses simple rules. The latest client_view replaces the older view. Other messages stay in arrival order.
An agent tool result already reports the immediate result of that tool. The harness does not inject a second event for the same synchronous change.
The harness sends an artifact_update when a user, a collaborator, or an asynchronous service changes the artifact outside the active agent action.
The Gamma session already lives on its Archil disk outside the harness container. The Gamma server data-plane proxy routes each request according to session liveness.
Gamma browser
│
▼
Gamma server data-plane proxy
│
├── live ─────> agent-server in the active container
│
└── dormant ──> Archil session disk
│
user sends a message
│
flush files and UI state to Archil
│
▼
resume container
│
▼
mount the same Archil disk
│
▼
build injections and run Pi
The dormant data path already supports these operations without a container:
- Read and overwrite an existing session file through
/workspace/files/** - Read a dormant session preview from the persisted transcript and UI state
- Write the full UI snapshot through
/sessions/:id/ui/sync
Before resume, the client flushes its file buffers and UI snapshot. The new container mounts the same session disk, including generated files, uploads, imports, Pi state, and state.json.
The runtime injection protocol uses this existing durable path. It does not add another external session store.
A small manifest on the Archil session disk records artifact changes that need model attention after resume.
The manifest contains only changes that meet all three conditions:
- The change happens while the container is absent.
- The model must know about the change on its next turn.
- The prompt and restored state do not already communicate the change.
The first version stores one digest for each changed artifact:
type PendingArtifactUpdates = Record<string, string>;The record key is a file or Gamma reference. The value is its coalesced change digest.
The manifest does not contain forms, other interactions, client_view, pane state, user messages, workspace instructions, model configuration, or remote notifications:
- Forms and other client interactions already live in
SessionUIState. - A submitted or dismissed form is delivered from its restored UI state.
client_viewtravels with the next prompt as sender-specificclientContext.- Pane state already lives in
SessionUIState. - User messages already travel through the wake-and-prompt flow.
- Workspace instructions reload when the container starts.
- Model configuration already lives on the harness session row.
- Remote notifications use a server-owned delivery channel.
The client can persist the manifest through the same server proxy path that persists dormant UI state. A dedicated protocol field keeps pending model input separate from visual pane state.
When the user sends a message, Gamma performs these actions:
- Flush edited files, UI state, and the dormant artifact manifest to Archil.
- Resume the container and mount the Archil session disk.
- Restore the conversation, UI state, and session files.
- Convert pending changes into runtime injections.
- Add the injections before the new user message.
- Record their consumption in durable session state.
- Run Pi.
A remote producer can finish work while the harness is live, dormant, or transitioning. Examples include an import, file conversion, render, approval, or scheduled job.
Remote notifications do not pass through the browser. The producer sends them to a Gamma server endpoint.
The notification record keeps a typed payload:
type ExternalNotification<TType extends string, TPayload> = {
id: string;
type: TType;
payload: TPayload;
};
type ImportCompletedNotification = ExternalNotification<
"import.completed",
{
importId: string;
sourceRef: string;
outputRefs: string[];
}
>;The identifier supports acknowledgment and duplicate suppression in the remote delivery channel. It does not become part of the simple model-facing RuntimeInjection type.
The Gamma server uses this delivery flow:
- Store the notification outside the harness container.
- If the session is live, send it to the agent immediately.
- Mark it consumed after Pi records it in the session.
- If the session is dormant or transitioning, keep it pending.
- After the next user message resumes the session, deliver the pending notifications.
The default policy does not start a container for a remote notification. A later urgency policy can allow selected notification types to resume a session.
The session protocol converts each external notification into the model-facing injection that matches its meaning. An import completion can become a system_update, an artifact_update, or both.
remote producer
│
▼
Gamma notification endpoint
│
├── live ─────> store, deliver to Pi, acknowledge
│
└── dormant ──> store, wait for the next user wake
Later: durable event delivery
Gamma does not need a general event bus for the first version. Archil and the Gamma server proxy already provide durable session writes. The remote notification channel adds narrow server-owned delivery for independent producers. Add a general event system only when those producers need broader routing or delivery guarantees.
An artifact is a task output or a task input that the user can inspect or edit. Examples include a brief, a deck, an imported file, a generated image, and an export.
An artifact_update tells the agent what changed since its last artifact revision. It can include a dirty marker, a changed scope, or a complete inline diff.
The update stays connected to two revisions:
last revision seen by the agent → current artifact revision
The session service coalesces intermediate edits into one digest. The agent does not need every editor operation.
Use the most precise digest that the artifact service can produce reliably.
-
A dirty marker identifies an artifact with an unknown or large change.
The deck changed. Read the current deck. -
A scoped update identifies the changed cards or document properties.
Cards card_3 and card_5 changed. All other cards are unchanged. -
An inline diff contains the complete change for a small update.
On card_3, revenue changed from $4.2M to $4.8M.
A complete inline diff updates the agent context directly. A scoped update tells the agent which parts to read. A dirty marker requires a broader read.
Before the agent writes a changed card, it reads that card unless the digest contains its complete current state.
For decks, a change scope can include:
- Card identifiers
- Card order
- Document theme
- Shared assets
- Document metadata
The brief editor is the first direct use for artifact_update.
When the user saves brief.xml or another active brief file, the Gamma server proxy overwrites that file on the Archil session disk. The client also updates the dormant artifact manifest with the path and a useful digest.
<artifact_update
ref="file:gamma-agent/session/generations/q3/brief.xml"
from_revision="12"
to_revision="17"
>
The user edited the audience and structure sections.
Read the brief before the next generation step.
</artifact_update>For a live session, the proxy forwards the same file request to agent-server. For a dormant session, the proxy writes directly to Archil. The resumed container mounts the updated file.
A content edit in the live Gamma editor is an artifact update. A card selection, pane change, or viewport change is a client_view.
A live editor digest includes the Gamma reference, both document revisions, and the affected card identifiers. The agent reads only the changed cards when the digest does not contain their complete state.
<artifact_update
ref="gamma:file:g_123:d_456"
from_revision="12"
to_revision="17"
>
Changed scope: card_3, card_5.
card_3:
- Title changed from "Market Overview" to "Market Opportunity".
- Revenue changed from "$4.2M" to "$4.8M".
card_5:
- The user removed the competitor table.
All other cards are unchanged.
</artifact_update>The document service can derive this digest from editor operations or a document change log. The model-facing injection receives a compact digest instead of the raw operation stream.
An asynchronous operation sends an artifact update when it changes artifact state after its tool call returns. Examples include:
- An image finishes and becomes part of a card.
- A render, import, or export finishes.
- A background worker changes a deck file.
- A collaborator changes a live deck during generation.
A synchronous tool result is sufficient when the change finishes inside the tool call. General research output uses background_result because it does not change an artifact.
- Add the dormant artifact manifest to the existing Archil-backed session protocol.
- Record brief edits from the existing dormant file-save path.
- Restore and consume the manifest after the container mounts the session disk.
- Build a card-scoped deck digest from Gamma document changes.
- Restore submitted and dismissed forms through the existing UI-state delivery path.
- Add the current
client_viewto the next user turn. - Add the remote notification channel when an external producer needs it.
The provider can require the harness to transport injections in a user-role message. The harness must mark each trusted injection with private metadata.
type TrustedInjection = {
role: "user";
content: string;
metadata: {
gammaInjection: InjectionKind | "knowledge_hints";
};
};Plain user text has normal user authority, even when that text contains a reserved XML tag.
The harness must not parse untrusted user text as a trusted injection.
The initial stable tool set includes:
readbashwriteedittodosubagentshow_gammashow_fileskill
The exact list can grow to approximately 20 tools without a dynamic loading layer.
OpenAI prompt caching uses exact prompt prefixes. Instructions, tool definitions, and schemas must remain identical for the best cache reuse.
The stable tool set protects cache reuse across all initial sessions.
An on-demand skill body returns as a tool result. This method keeps the root prompt and tool schemas unchanged.
The skill body follows variable conversation content. Cross-session caching usually stops before that body.
A preloaded skill body creates a stable prompt variant. The common root prefix remains reusable until the variant begins.
Use a small number of prompt variants for common known workflows:
- Base
- Deck generation
- Import
Do not change the system prompt during a session to activate a skill. Use the skill tool for skills outside the selected variant.
Dynamic tool schemas can reduce cache reuse because the tool list changes. This tradeoff has little value with approximately 20 tools.
OpenAI documents these rules in the prompt caching guide.
Add dynamic tool loading when tool count or schema size causes a measured cost, latency, or selection problem.
The later design can use tool namespaces and Pi active tool selection. The initial protocol does not expose search_tools or load_tools.
The harness stores the task list as session state. The list does not depend on conversation history.
Each task has one status:
type TaskStatus = "pending" | "in_progress" | "complete" | "blocked";Exactly one task can have the in_progress status. The Gamma interface displays the task list during long operations.
The agent updates task state through first-class tools. Every update appears in the transcript.
Every user-facing message includes an explicit yield value:
message_user({
message: "I finished the research. I am building the deck now.",
blockOnUser: false,
});The agent uses blockOnUser: false for a progress update. The harness continues the run after it sends the message.
The agent uses blockOnUser: true only in these conditions:
- The task is complete.
- The agent requires a user decision.
- The agent cannot continue safely.
- The agent requires unavailable user input.
This value is a control signal. The interface does not infer completion from the message text.
Gamma does not create a separate execution-state object. Each kind of state keeps its existing owner:
| State | Owner |
|---|---|
| Conversation and compaction entries | Pi transcript on the Archil session disk |
| Task state | Todo controller |
| Active skill contracts | Skill activation session entries |
| Live pending injections | Pi steering and follow-up queues |
| Dormant artifact updates | Dormant artifact manifest on the Archil session disk |
| Panes, tabs, forms, and visible refs | SessionUIState |
| Session files and generated outputs | Archil session disk |
| Gamma documents | Gamma document state |
| Model configuration | Harness session row |
Compaction restores only the model context that it removed. It does not copy UI refs, files, Gamma resources, or model configuration into another state object.
After context compaction, the harness restores active skill contracts, task context, and pending runtime injections from their owners.
The static prefix tells the agent about this guarantee:
Your task state and active skills survive context compaction. Continue until completion.
The harness must keep this statement accurate.
Gamma artifact tasks use this general lifecycle:
- Understand the request.
- Load every matching workflow skill.
- Ask only for information that blocks execution.
- Research when the workflow requires evidence.
- Create or edit the artifact.
- Render and inspect the result.
- Repair each detected defect.
- Review every active workflow checklist.
- Present the artifact and a short completion summary.
The artifact workflow can send progress messages without a user yield.
The harness implements skills, task state, runtime injections, and a stable tool set.
This implementation supplies one protocol across all GPT models. Native provider features remain optional adapters.
Pi remains the execution engine. Gamma adds a session protocol around each Pi session.
Pi owns:
- Model requests
- Tool execution
- Parallel tool batches
- Message streaming
- Message persistence
- Steering and follow-up queues
- Conversion of messages for each provider
Gamma owns:
- Skill contracts
- Trusted runtime injections
- Product task state
- Yield semantics
Scoped knowledge and active tool policy remain later extensions.
The first implementation does not require a Pi fork.
The agent server builds a message array and calls the low-level agent.prompt(messages) method.
Gamma UI
│
▼
Agent server
│ builds user messages and reminders
▼
Pi Agent.prompt(messages)
│
├── model calls
├── tool execution
├── event stream
└── message persistence
Gamma services
├── server compaction
├── todo sidecar
├── interface state
├── connector dispatch
└── interaction metadata
This low-level path bypasses parts of AgentSession.prompt(). Those parts include prompt expansion and some extension lifecycle hooks.
The session protocol must operate before the low-level prompt call:
Gamma UI
│
▼
Gamma session protocol
├── selects runtime injections
├── restores active state
├── adds trusted custom messages
├── keeps the stable tool set
└── applies yield semantics
│
▼
Pi Agent
| Design feature | Pi support | Gamma work |
|---|---|---|
| Stable system prompt | ResourceLoader |
Refactor prompt content |
| Skill catalog | Pi skill discovery | Add skill types and stronger rules |
| Skill loading | Pi uses read |
Add an explicit skill tool |
| Workflow contract | Prompt instruction | Add the strict checklist text |
| Typed injections | Pi custom messages | Replace plain reminder messages |
| Stable tool set | Pi tool registration | Load all initial Gamma tools |
| Task state | Gamma todo sidecar | Reuse and extend the present system |
| Compaction recovery | Pi session entries | Add active skill anchors |
| Mid-run updates | Pi message queues | Add typed update routing |
| Yield control | Terminating tool results | Add message_user and protocol events |
Pi already discovers skills and puts their names and descriptions in the system prompt.
GammaResourceLoader uses loadSkillsFromDir() for the Gamma skill directory. This behavior supplies the proposed skill catalog.
Pi expects the model to use read for a matching SKILL.md. This action is visible, but it has no explicit activation meaning.
The skill tool adds that meaning:
skill({
name: "deck-generation",
});The tool performs these actions:
- Read the complete skill body.
- Record the skill as active.
- Return the body as a tool result.
- Load required reference skills.
Pi supports parallel tool calls. The model can load all matching skills in one tool batch.
The Gamma loader puts selected skill bodies in the system prompt before the first turn.
The present preload text describes every preloaded skill as reference material. This meaning does not satisfy the workflow contract.
A preloaded workflow skill needs this instruction:
This workflow skill is active because the current task matches it. Follow every required step and complete its verification.
A preloaded reference skill keeps the reference meaning. The loader uses the skill type to select the correct instruction.
Pi does not determine which skills match a task. The system prompt gives that responsibility to the model.
The transcript proves these facts:
- The agent called
skill. - The call occurred before another task tool.
- The agent loaded multiple skills in one batch.
The transcript cannot prove that the model identified every matching skill. Critical product workflows require an additional harness rule.
For example, the harness can require deck-generation for each new deck request. The model selects less critical skill combinations.
Start with prompt selection and normal session inspection. Add deterministic rules for measured product failures.
Pi custom messages contain these fields:
customTypecontentdetailsdisplay- A persistent session entry
Pi converts the content to a user-role message for the model provider. Pi does not send the details value to the model.
The details value can contain trusted Gamma metadata:
{
role: "custom",
customType: "gamma.client_view",
content: "<client_view>...</client_view>",
display: false,
details: {
trusted: true,
injectionType: "client_view",
},
timestamp: Date.now(),
}Gamma pane context now uses plain user messages with <system-reminder> tags. The session protocol replaces them with Pi custom messages.
Plain user text cannot create the trusted details value. The harness does not parse plain text as a trusted injection.
Pi maintains a registered tool collection and an active tool collection.
Gamma registers and activates all initial tools during session creation. This behavior fits the initial rollout.
Pi exposes setActiveToolsByName() and dynamic registerTool() through extensions. These interfaces remain available for later tool growth.
The connector dispatcher remains the initial connector path.
The Gamma todo system already implements most of the proposed task design.
It has:
- A sidecar file
- A revision number
- One
in_progressitem - User-visible labels
- Tool-result details
- Context repair
- Compaction recovery
The todo controller restores active tasks after compaction. It uses a hidden Pi custom message as a context anchor.
This pattern also fits active skill session entries. The todo controller remains the owner of task state. Skill activation entries remain the owner of active skill contracts.
Do not wrap these owners in a second persistent execution-state object.
Gamma owns server compaction because the server uses the low-level Pi prompt method.
The compaction flow performs these actions:
- Generate a structured summary.
- Add a Pi compaction entry.
- Restore the todo anchor.
- Rebuild model context.
- Reload the Pi session.
The session reload restores the base prompt and the initial active tools. The session protocol must restore dynamic state after every reload.
The restoration includes:
- Active workflow skills
- Required reference skills
- Pending runtime updates
The todo controller proves that this recovery pattern works with Pi session entries.
Pi supports steering messages and follow-up messages.
A steering message enters before the next model call. A follow-up message enters after the active run stops.
The runtime injection delivery policy defines the queue for each event type.
Gamma already uses follow-up delivery for background subagent results.
Pi normally stops when the assistant produces no more tool calls.
Pi also supports terminate: true in a tool result. This value skips the automatic model call after the current tool batch.
The message_user tool maps the yield contract to Pi:
message_user({
message: "I finished the research. I am building the deck now.",
blockOnUser: false,
});For blockOnUser: false, the tool returns a normal result. Pi continues to the next model call.
For blockOnUser: true, the tool returns terminate: true. Every result in that tool batch must terminate.
The system prompt includes this rule:
Call
message_useralone whenblockOnUseris true.
The agent server emits a structured interface event from the tool details.
One Gamma-owned SessionProtocolController coordinates these features:
type SessionProtocolController = {
buildTurnMessages(input: TurnInput): AgentMessage[];
restoreContext(): void;
activateSkill(name: string): ActiveSkill;
routeUpdate(update: RuntimeUpdate): void;
};The controller prevents each feature from creating a separate state store and recovery path.
- Add the session protocol controller and its persistent state.
- Convert pane reminders to trusted Pi custom messages.
- Add workflow and reference skill types.
- Add the
skilltool and active skill state. - Restore active skills after compaction.
- Add
message_userwith explicit yield control.
Later work includes scoped knowledge, dynamic tool loading, dedicated evaluations, and completion audits.
Dedicated evaluations and completion audits are outside the initial rollout.
They serve two later needs:
- Detect model regressions after prompt or model changes.
- Measure whether strict workflow skills prevent skipped steps.
The initial rollout does not need an evaluation framework or a completion audit tool.
The skill tool already creates a transcript record. Normal logs also show tool calls, failures, compaction, and final messages.
Use focused automated tests for these implementation contracts:
- Skill type parsing
- Parallel skill loading
- Active skill persistence
- Compaction recovery
- Trusted injection transport
- Yield behavior
Add workflow evaluations after the team observes regressions or starts frequent model comparisons.
- Does the harness select candidate skills, or does the model select from the complete catalog?
- Does a workflow skill load its required reference skills automatically?
- How does the harness represent an unrecoverable step error?
- Which instruction source defines playbook precedence?
- How long does loaded knowledge remain active?
- Which runtime updates interrupt an active tool operation?
- Does
blockOnUser: trueend the run immediately after the message? - How does the interface display checklist progress inside a workflow skill?