-
Notifications
You must be signed in to change notification settings - Fork 1.5k
RFC: dedicated agent content API for stable named memory carriers #1251
Description
RFC: Dedicated Agent Content API for Stable Named Memory Carriers
Status
Draft
Summary
OpenViking currently supports session-based memory extraction, but for memory categories such as CASES and PATTERNS, the extracted output is written as randomly named fragment files (e.g. mem_{uuid}.md) under a parent directory.
This works for raw memory extraction, but does not support a common integration need: maintaining a stable, predictably named memory carrier file for long-lived, project-level cumulative memory.
This RFC proposes adding a dedicated agent content API that allows official creation and mutation of stable named memory carrier files under agent-scoped memory paths, without coupling that behavior to session extraction internals.
Problem
Current behavior
In the current session memory extraction flow, extracted memories for normal categories are written to random UUID-based files under the target parent directory.
Observed behavior:
- Session flow completes successfully
- Extracted content is persisted as random fragment files
- A fixed expected URI such as
distilled-<project>.mdis never materialized - Public Session API does not expose a way to specify a target output URI
Why this is insufficient
For many long-lived integrations, especially project-oriented memory accumulation, random fragment files are not a full substitute for a stable named carrier file.
A stable carrier file is useful for:
- Deterministic URI references
- Idempotent adapter writes
- Project-level cumulative memory
- Cross-session and cross-workflow stable attachment points
- External tooling that expects a durable logical memory endpoint
Scope of the gap
This is not merely a missing parameter on one API. It is a contract gap between:
- Memory extraction behavior
- AGFS resource semantics
- External adapter expectations
- Stable memory accumulation workflows
Evidence
1. Session extraction currently uses random filenames
Server-side extraction logic for ordinary extracted memories generates UUID-based filenames and writes directly to parent memory directories, rather than targeting a stable named file.
See Appendix A.1 for source reference.
2. Stable named merge behavior already exists for some categories
Within the same codebase, categories such as TOOLS and SKILLS already use stable filenames and merge/update semantics.
This is strong prior art that:
- Stable named files are compatible with OpenViking's architecture
- Merge-oriented memory writing is already an accepted pattern
- The limitation is not conceptual impossibility
See Appendix A.2 for source reference.
3. Underlying storage primitives already support the needed write shape
At the storage layer, functions such as write_file() and append_file() already support writing to agent-scoped URIs, including create-if-missing style behavior.
This suggests the main gap is not in storage capability, but in the exposed API and resource contract.
See Appendix A.3 for source reference.
4. Existing error text suggests a missing official interface
Some current error messaging already points users toward a "dedicated interface" for adding agent-scope content, but no such public interface is currently discoverable in the documented/public API surface.
See Appendix A.4 for source reference.
Design Goals
The proposed solution should:
- Allow official creation of stable named agent memory carrier files
- Allow controlled mutation of these files through explicit modes
- Preserve compatibility with existing fragment-based extraction
- Avoid over-coupling session extraction with arbitrary resource writes
- Provide a clean contract for external adapters and automation systems
- Support idempotent and auditable updates
Non-Goals
This RFC does not aim to:
- Replace the existing session extraction model
- Force all memory categories into stable carrier files
- Remove fragment-based memory extraction
- Allow unrestricted arbitrary filesystem mutation without memory semantics
- Redesign memory categorization as a whole
Proposal
Primary Proposal: Dedicated Agent Content API
Add an official API for creating and mutating agent-scoped memory carrier files.
Example conceptual operations:
- Create/register a stable named carrier file
- Append content to an existing carrier
- Replace content in an existing carrier
- Merge content into an existing carrier using category-aware strategy
Example conceptual API surface
Create/register carrier
POST /api/v1/agent-content{
"agent_id": "<agent-id>",
"uri": "viking://agent/<agent-id>/memories/patterns/distilled-project.md",
"category": "PATTERNS",
"content": "# Initial carrier content",
"create_mode": "create_if_missing"
}Mutate carrier
POST /api/v1/agent-content/write{
"uri": "viking://agent/<agent-id>/memories/patterns/distilled-project.md",
"mode": "append",
"content": "new lesson content",
"idempotency_key": "<request-id>"
}Possible mode values:
append— add content to the end of the filereplace— overwrite the entire filemerge— category-aware merge with existing content
Why this is the preferred design
This approach:
- Keeps session extraction and resource mutation decoupled
- Makes stable carrier semantics explicit
- Is easier to reason about than adding arbitrary write-target logic into session commit
- Aligns with existing hints in current error messaging
- Gives integrators a predictable and auditable contract
Alternative Options Considered
Option A: Add target_uri to session commit / extraction
This would allow users to direct extracted output into a fixed URI during session extraction.
Why not preferred as the main solution:
- Couples extraction with resource-writing semantics
- Makes session commit behavior more complex and less predictable
- Risks mixing two responsibilities: extraction and persistent file targeting
- May create unclear merge/overwrite semantics
This could still be considered later as a convenience layer built on top of a dedicated content API.
Option B: Allow content/write to create missing files automatically
This is likely the smallest implementation change, especially if lower-level storage already supports create-if-missing behavior.
Pros:
- Minimal change
- Fast to implement
- Unblocks integrations quickly
Cons:
- May blur current API semantics
- Does not clearly establish memory-carrier-specific contract
- May be too generic for long-term clarity
This is a reasonable short-term bridge, but weaker than a dedicated API as a long-term public contract.
Proposed Semantics
Resource model
A stable memory carrier is:
- An agent-scoped memory file
- Predictably named
- Intended for cumulative updates across time
- Explicitly managed rather than incidentally created by fragment extraction
Write modes
The API should explicitly support:
append: append content to the carrierreplace: replace full carrier contentmerge: category-aware merge into existing content
Idempotency
To support robust external integrations, the API should support one or more of:
idempotency_key- Revision preconditions
- Expected existence flags
- Optimistic concurrency guards
Auditability
Mutation behavior should be observable and auditable:
- Request mode
- Timestamp
- Actor/session identity
- Whether file was created or updated
Compatibility
This proposal is backward-compatible.
Existing behavior can remain unchanged:
- Session extraction continues producing fragment files where appropriate
- Current workflows do not break
The new API simply adds a new official capability for integrations that require stable named carriers.
Migration / Adoption
Typical adoption path for integrators:
- Continue using session extraction for raw memory generation if desired
- Use the dedicated agent content API to create stable carrier files
- Append/merge distilled or curated memory into those stable carriers over time
- Stop relying on undocumented assumptions about session extraction materializing fixed URIs
Open Questions
- Should stable carrier support be limited to certain memory categories, or available to all agent-scoped memory namespaces?
- Should
mergebehavior be generic text merge, or category-aware merge with structured heuristics? - Should create and write be separate APIs, or one API with explicit create policies?
- Should the API expose revision/version metadata for concurrency control?
- Should session extraction later support optional routing into a stable carrier, but only through this API layer?
Recommendation
The recommended path is:
- Primary: Introduce a dedicated agent content API for stable named memory carriers
- Secondary / short-term bridge: Consider allowing
content/writecreate-if-missing semantics for agent-scoped files - Not recommended as the primary design: Directly adding
target_urito session commit/extraction
This keeps the architecture clean while filling a real integration gap that is already surfaced by current usage patterns and hinted at by existing product behavior.
Conclusion
OpenViking already appears to have:
- Lower-level storage capability for the needed write patterns
- Prior art for stable named merge behavior in TOOLS/SKILLS categories
- An API-layer separation that is consistent with introducing a dedicated interface
What is missing is a clear, official, public contract for stable named memory carriers in agent-scoped memory spaces such as CASES and PATTERNS.
This RFC proposes adding that missing contract.
Appendix A: Source Evidence
Note
All references are to the OpenViking open-source repository. File paths and line numbers are from the version available at the time of writing.
A.1 Random UUID filename generation
File: openviking/session/memory_extractor.py
Location: create_memory() method, lines 487–493
# Generate file URI (store directly as .md file, no directory creation)
memory_id = f"mem_{str(uuid4())}"
memory_uri = f"{parent_uri}/{memory_id}.md"
# Write to AGFS as single .md file
try:
await viking_fs.write_file(memory_uri, candidate.content, ctx=ctx)Significance: This confirms that for CASES and PATTERNS categories, the filename is always a random UUID. There is no parameter or code path to specify a fixed target filename. This is the root cause of the inability to create stable named carrier files through session extraction.
A.2 TOOLS / SKILLS stable naming prior art
File: openviking/session/memory_extractor.py
Location: _merge_tool_memory() method, lines 630–631
agent_space = ctx.user.agent_space_name()
uri = f"viking://agent/{agent_space}/memories/tools/{tool_name}.md"Significance: The TOOLS category already uses stable, deterministic filenames derived from the tool name — not random UUIDs. The same file is read, merged, and rewritten on subsequent encounters (lines 681–744). This proves that:
- Stable named files are architecturally supported
- Merge/update semantics for a single file are already implemented
- The limitation on CASES/PATTERNS is a design choice, not a technical impossibility
The same pattern exists for SKILLS via _merge_skill_memory().
A.3 Storage layer already supports create-if-missing
File: openviking/storage/viking_fs.py
write_file() — lines 1510–1525:
async def write_file(self, uri, content, ctx=None):
"""Write file directly."""
self._ensure_access(uri, ctx)
path = self._uri_to_path(uri, ctx=ctx)
await self._ensure_parent_dirs(path) # auto-creates parent dirs
...
self.agfs.write(path, content)append_file() — lines 1604–1626:
async def append_file(self, uri, content, ctx=None):
"""Append content to file."""
...
try:
existing = ""
try:
existing_bytes = self._handle_agfs_read(self.agfs.read(path))
...
except Exception:
pass # gracefully handles missing file
await self._ensure_parent_dirs(path)
final_content = (existing + content).encode("utf-8")
self.agfs.write(path, final_content)Significance: Both write_file() and append_file() at the storage layer already handle file creation transparently — including auto-creating parent directories. append_file() even gracefully handles the case where the target file does not yet exist.
The gap is therefore not in the storage layer. It is in the API layer, which performs an existence check before calling these underlying methods, and in the resource service, which blocks agent-scope URIs.
A.4 Error message hints at missing interface
File: openviking/service/resource_service.py
Location: add_resource() method, lines 163–168
# add_resource only supports resources scope
if to and to.startswith("viking://"):
parsed = VikingURI(to)
if parsed.scope != "resources":
raise InvalidArgumentError(
f"add_resource only supports resources scope, "
f"use dedicated interface to add {parsed.scope} content"
)Significance: The error message explicitly tells users to "use dedicated interface to add agent content". This indicates the current API contract explicitly distinguishes agent-scope content from resources-scope additions, while no such public dedicated interface is currently discoverable in the documented/public API surface.
A.5 content/write API rejects writes to non-existent files
Observation (live API test):
POST /api/v1/content/write
{
"uri": "viking://agent/<id>/memories/patterns/distilled-project.md",
"content": "...",
"mode": "replace"
}Response:
{
"status": "error",
"error": {
"code": "NOT_FOUND",
"message": "File not found: viking://agent/<id>/memories/patterns/distilled-project.md"
}
}Significance: Even with mode: "replace", the content/write API requires the target file to already exist. Combined with the fact that there is no public API to create agent-scope files (A.4), this creates a practical create/write gap: public write rejects missing files, while no public agent-scope create interface is currently available.
Metadata
Metadata
Assignees
Labels
Type
Projects
Status