Working files for the current Ahentic session (the open agent tab / ahentic-session CPT). Artifacts let the orchestrator and abilities stage large payloads once (drafts, block trees, HTML) and apply them later by key, without re-reading or re-emitting huge content from the chat transcript.
Canonical should: Working memory PRD — “artifacts” are the payload namespace;
editor.refsis a sibling.
Session contract: CONTRACT.md. This file is how-it-works for payload staging/apply; if it disagrees with the PRD, the PRD wins.
Implementation: src/session/class-artifacts.php (storage + stage/list/delete abilities). Orchestrator expands from_memory before execute / browser pause.
Related: Session · Orchestrator · Abilities · Server abilities · Client abilities
Today, long drafts only survive as assistant / tool text in _ahentic_entries. When the model later calls ahentic-browser/set-blocks or ahentic/create-post, it must rediscover that text in history and paste it again into tools_planned. That is expensive, lossy (truncation), and unreliable.
Artifacts fix the draft → create path: stage once, apply by reference.
- Not site knowledge (
ahentic_site_knowledge) — artifacts die with the session unless we deliberately promote something later. - Not a full “working memory” dump in every LLM think — prompts only get pointers (key, title, size, status), never the full body every turn.
- Not a replacement for tool execution — staging does not mean the write already happened. Mutating abilities still run; they may load input from an artifact.
- Not permanent storage across sessions — new session ⇒ empty artifacts.
| Concern | Location |
|---|---|
| Persistence | Session post meta, e.g. _ahentic_artifacts (JSON) |
| API | Session helpers (e.g. Ahentic_Session_Artifacts or methods on Ahentic_Session_Repository) |
| Who writes | Orchestrator + abilities (PHP and browser result merge) |
| Who reads | Orchestrator (prompt summary + from_memory expansion), abilities |
Keep artifacts session-scoped. Clear or leave them when the session is deleted with the CPT; do not write them to localStorage.
Suggested companion namespaces (same meta blob or adjacent keys) can hold small vars / editor.ref_map / facts later. This doc specifies artifacts only; other namespaces may share the store but must not bloat the every-think prompt with artifact bodies.
{
"version": 1,
"updated_at": "2026-08-03T04:00:00Z",
"items": {
"article_draft": {
"kind": "blocks",
"title": "10 Ways to Brew Better Coffee",
"status": "ready",
"payload": {
"blocks": [
{
"name": "core/heading",
"attributes": { "level": 1, "content": "…" },
"innerBlocks": []
}
]
},
"meta": {
"source": "model_stage",
"step": 4,
"bytes": 12400,
"block_count": 12,
"created_at": "…",
"updated_at": "…"
}
}
}
}| Field | Meaning |
|---|---|
items.{key} |
Stable id the model / tools use (article_draft, section_2, …). Prefer snake_case, max ~64 chars. |
kind |
blocks | html | markdown | post_content | json — how payload is interpreted at apply time. |
title |
Short label for UI / prompt pointer. |
status |
ready | applied | stale | empty. |
payload |
The body. For blocks, prefer Gutenberg-shaped { name, attributes, innerBlocks }[] (same shape set-blocks expects). |
meta.source |
Who wrote it (model_stage, ability name, orchestrator). |
meta.step |
Session step count when written (optional). |
meta.bytes / block_count |
For prompt pointers and size caps — do not require the model to count. |
(missing) → drafting → ready → applied
↓ ↓
stale stale (document/context changed, or superseded)
- drafting — chunks still landing (
mode=append,complete=false);from_memoryapply is rejected. - ready — safe to apply via
from_memory(complete=true, default). - applied — successfully used by a mutate; keep briefly for audit or delete.
- stale — must not apply until restaged (e.g. user switched posts, or a newer draft replaced it).
1. Model produces structured content (blocks/HTML) intended for later write
2. Stage → artifacts.items.article_draft = { kind, payload, status: ready }
3. Later thinks see only a pointer in the prompt
4. Model plans set-blocks / create-post with input.from_memory = "article_draft"
5. Orchestrator expands from_memory → full tool input, then runs the ability
6. On success → status applied (or delete key)
Ways to stage (implement at least one; prefer ability-authored):
- Explicit stage ability (e.g.
ahentic/stage-artifact) — model passeskey,kind,payload. Clearest contract. - Side effect — a “draft complete” control-block field or memory op merges into artifacts.
- Avoid scraping free-form chat prose as the primary path — brittle.
On stage:
- Upsert
items[key](mode: replace|append,complete→ drafting vs ready). appendonly merges while status isdrafting. If the key is alreadyready/applied/stale, a newappendis treated asreplaceso revisions do not concatenate duplicate sections.- Empty
complete=truefinalize (no new payload) is allowed only while drafting. - Refresh
meta(bytes, block_count, timestamps). - Append a short tool/event note to the session (“Staged artifact
article_draft(12 blocks)”) — not the full payload in chat. - Enforce size caps (reject or truncate with error; do not silently corrupt).
- Orchestrator may auto-stage oversized inline tool bodies and rewrite to
from_memory.
Supported consumers (minimum):
ahentic-browser/set-blocks—input.from_memory→ loadpayload.blocks(or converthtml/markdownonce if we support that).ahentic/create-post/ahentic/update-post—from_memory→content/ block serialization as appropriate.
Pending HITL / browser tools keep key only on _ahentic_pending_tool; REST expands for the browser runner. HITL shows key / title / size / short excerpt.
Expansion happens in the orchestrator (or ability wrapper) before execute, so:
- Browser pause receives the expanded input (sidebar does not need to load artifacts for apply).
- PHP abilities receive expanded input the same way.
- The model is not required to re-emit the article in
tools_planned.
If from_memory is set:
- Missing key → tool error
artifact_missing. status !== ready→artifact_not_ready(include status).kindincompatible with the ability →artifact_kind_mismatch.- On success → mark
applied(or delete).
Explicit inline blocks / content in input still works; from_memory wins when both are present (document that rule).
Every LLM think may include a compact section, for example:
Session artifacts (bodies omitted — use from_memory to apply):
- article_draft: ready · blocks · "10 Ways…" · 12 blocks · staged step 4
Rules for the system prompt:
- Prefer
from_memorywhen applying a staged draft; do not paste the full artifact intotools_planned. - Do not invent artifact keys; only use keys listed (or stage a new one first).
- Staging is not publishing — still call the mutate ability.
Never inject full payload into the system/user prompt by default. If a rare “revise draft” path needs the body, load it via a dedicated read (ability or orchestrator one-shot), not every turn.
// Illustrative — exact class name TBD in src/session/
Ahentic_Session_Artifacts::get( $session_id ): array;
Ahentic_Session_Artifacts::list_pointers( $session_id ): array; // for prompts
Ahentic_Session_Artifacts::get_item( $session_id, $key ): ?array;
Ahentic_Session_Artifacts::stage( $session_id, $key, array $item ): true|\WP_Error;
Ahentic_Session_Artifacts::set_status( $session_id, $key, $status ): true|\WP_Error;
Ahentic_Session_Artifacts::delete( $session_id, $key ): void;
Ahentic_Session_Artifacts::clear( $session_id ): void;
// Expand tool input before execute / browser pause:
Ahentic_Session_Artifacts::apply_from_memory( $session_id, $ability_name, array $input ): array|\WP_Error;REST: expose pointers on the session payload for the sidebar/debugger (artifacts summary). Full payloads are not required in every poll — optional ?include=artifacts for debugging.
- Before tool run / browser pause — if
input.from_memoryis present, callapply_from_memory; replace input; continue HITL/browser/PHP paths as today. - After successful mutate that used an artifact — mark
appliedor delete. - On
run_start(new user message) — product choice:- Keep artifacts (default recommended: user may say “now put that draft in the editor”), or
- Clear on each new message if we want a hard reset — document the chosen policy in code.
- Prompt build — append
list_pointers()next to page context / plan (not inside the transcript dump).
Browser abilities do not need a separate artifacts store. If a browser tool stages something, return a memory / artifacts patch in the browser-result POST; handle_browser_result merges via stage().
- Cap payload size per artifact (e.g. 100–200KB JSON) and max items per session (e.g. 10).
- Sanitize on write (same expectations as ability input schemas).
- Do not put secrets in artifacts.
- HITL summaries should mention the artifact key + title/size, not dump the body into the Allow card.
| State | Role vs artifacts |
|---|---|
_ahentic_entries |
Audit / chat; may note “staged” / “applied”; not the body store. |
_ahentic_page_context |
Open tab identity / editor routing — not draft bodies. |
_ahentic_plan |
Checklist for the run — not content payloads. |
_ahentic_pending_tool |
One in-flight tool handoff — may carry expanded input after from_memory. |
editor.refs (working memory) |
Editor addressing (b1 ↔ clientId); session-backed + validated per Working memory PRD — sibling namespace to payload artifacts. |
- Draft then set-blocks — Model stages
article_draft, later callsset-blockswith{ "from_memory": "article_draft" }. Editor receives full blocks; model output does not contain the full article again. - Draft then create-post — Same with server
create-postwhen the block editor is not open. - Revise — Restage same key with new payload; status
ready; previous body replaced. - Stale — Applying
applied/ missing / wrong kind returns a clear tool error; run continues. - Prompt size — Think prompts list pointers only; full payload is not re-injected each step.
- Meta storage + PHP get/stage/delete/list_pointers.
apply_from_memorywired in the orchestrator forset-blocksandcreate-post(andupdate-postif straightforward).- Stage path (ability and/or control-block merge).
- Prompt pointers + system-prompt guidance.
- Mark applied / size caps / debugger summary on session REST.
- (Later) broader working memory:
vars,facts,editor.ref_mapin the same session store.
Artifacts are session-scoped staged payloads. The model and tools share them by key. The orchestrator expands from_memory at execute time so large drafts are written once and applied reliably, without using the chat transcript as a bulk content store.