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
- 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
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 harness stores each injection in one common envelope:
type InjectionKind =
| "system_note"
| "system_guidance"
| "system_update"
| "client_view"
| "artifact_update"
| "workspace_update"
| "background_result";
type InjectionDelivery = "next_turn" | "steer" | "follow_up";
type RuntimeInjection = {
id: string;
kind: InjectionKind;
occurredAt: string;
source: "client" | "server" | "tool" | "workspace" | "background_job";
delivery: InjectionDelivery;
requiresDecision: boolean;
summary: string;
dedupeKey?: string;
subject?: {
ref?: string;
path?: string;
revision?: string;
};
origin?: {
actor?: "user" | "collaborator" | "agent" | "service";
interactionId?: string;
toolCallId?: string;
};
details?: Record<string, unknown>;
};The model receives the kind, summary, and necessary subject data. The harness keeps routing, origin, and deduplication data in private metadata.
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 protocol uses these Pi delivery paths:
| Injection | Pi delivery |
|---|---|
system_note |
Next turn, or steering when the note affects active work |
system_guidance |
Steering |
system_update |
Steering |
client_view |
Next user turn |
artifact_update |
Steering |
workspace_update |
Steering |
Nonurgent background_result |
Follow-up |
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 starts a turn only when an active task requires a decision. This rule prevents an inactive session from waking for passive state changes.
The harness orders injections by occurredAt. It uses id for idempotent processing.
The harness coalesces repeated state snapshots:
- The latest
client_viewreplaces an older view. - The latest file revision replaces an older unsent revision for the same path.
- The latest workspace revision replaces an older unsent workspace update.
- A completed background job produces one result event for each job.
The harness does not coalesce events when each event needs a separate decision.
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 origin data connects an event to its interaction or tool call. The harness suppresses an event when that event only repeats a change that is already present in the active context.
The harness sends an artifact_update when a user, a collaborator, or an asynchronous service changes the artifact after the active tool result.
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 that artifact state changed. It carries a stable reference, a revision, an actor, and a short change summary. It does not carry a complete file or document by default.
The agent processes a relevant artifact update in this order:
- Match the event subject to the active task.
- Read the current artifact from its source of truth.
- Preserve user and collaborator changes.
- Reconcile the new state with the active plan.
- Continue the task from the current revision.
If the artifact is not relevant to the active task, the agent records the update and continues.
The brief editor is the first direct use for artifact_update.
When the user saves brief.xml or another active brief file, the workspace file service emits an event with the file path, the new revision, and actor: "user". If the agent is using that brief, Pi delivers the event as steering. The agent reads the brief again before it generates or repairs the deck.
<artifact_update
id="evt_123"
ref="file:gamma-agent/session/generations/q3/brief.xml"
revision="sha256:abc"
actor="user"
change="content_saved"
>
The user edited the audience and structure sections.
Read the brief before the next generation step.
</artifact_update>The current workspace file save route is a natural producer for this event. It writes editor changes to the virtual file system. The protocol adds event emission after a successful save.
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 event includes the Gamma reference, the document revision, the affected card identifiers, the actor, and a change category. The agent reads the affected cards before it writes more content. This prevents the agent from silently replacing a user or collaborator edit.
<artifact_update
id="evt_456"
ref="gamma:file:g_123:d_456"
revision="rev_89"
actor="collaborator"
change="cards_edited"
>
Cards card_3 and card_5 changed in the editor.
Read their current content before another write.
</artifact_update>The current client context reports open tabs and views. It does not report live document content changes. Live editor support needs a revision event from the editor, the multiplayer service, or the Gamma API.
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.
- Convert the current pane reminder to
client_viewdata on the next user turn. - Emit
artifact_updateafter a user saves an active workspace file, including a brief. - Emit updates for asynchronous artifact jobs that finish after their tool calls.
- Add live Gamma editor updates when a document revision event is available.
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: true;
injectionType:
| "system_note"
| "system_guidance"
| "system_update"
| "knowledge_hints"
| "client_view"
| "artifact_update"
| "workspace_update"
| "background_result";
injectionId: string;
occurredAt: string;
dedupeKey?: string;
};
};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.
The harness stores these values outside the model context:
type AgentExecutionState = {
activeSkills: string[];
tasks: Task[];
pendingInjections: TrustedInjection[];
currentArtifact?: ArtifactReference;
};After context compaction, the harness restores:
- Active workflow skill instructions
- Required reference skill instructions
- Task state
- Pending runtime injections
- Current artifact references
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 skills.
One session state controller can own all persistent execution state:
type AgentExecutionState = {
activeSkills: ActiveSkill[];
tasks: TodoSnapshot;
};The todo controller becomes one part of this controller.
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?