Manage context

Organize with tags

Each Atomic Memory is an independent Artifact with its own tags. Tags follow the logical identity without changing content revisions, lineage, or embeddings. Other built-in Artifact families also support Scope-local tags.

Current family values are atomic-memory, topic-memory, experience, skill, handoff, profile, and prompt. The Profile ID is profile; a Prompt ID is its prompt key, such as atomic_memory.extract. Tags require a persisted Artifact. An unsaved built-in default Prompt has no independent tag set; save a Prompt Artifact before assigning tags.

With access control enabled, tags follow their target's read and write permissions. A viewer of a shared target can read its tags but cannot edit them or run a Scope-wide tag query. Queries require scope.read. Tags on Topic Memory Artifacts require scope.read to read and scope.admin to edit. Prompt tags use the Prompt's read permission; editing requires current scope.admin permission, even if an Artifact owner binding remains after Scope access is revoked. Profile, Experience, Skill, Handoff, and individual Memory entries use their target's artifact.read / artifact.write permissions. Insufficient permission returns 403, and revoking a share also revokes tag access.

Manage tags through the Python Client below or the public API. The Dashboard reads saved content and does not provide tag editing or query pages.

Use the Python Client

You need a running Server and an existing Scope and Artifact. Take their IDs from the Dashboard or the corresponding Scope and Artifact APIs; a title is not an ID. Set these non-secret example variables in your terminal:

export POWERCONTEXT_TAG_SCOPE='your-existing-scope-id'
export POWERCONTEXT_TAG_FAMILY='skill'
export POWERCONTEXT_TAG_ARTIFACT='your-existing-artifact-id'

Run this with powercontext installed. If the Server requires authentication, provide its bearer token through POWERCONTEXT_SERVER_AUTH_TOKEN; do not embed it in the script.

import asyncio
import os

from powercontext.client import PowerContextClient
from powercontext.http import QueryArtifactTagsRequest, ReplaceArtifactTagsRequest


async def main():
    scope = os.environ["POWERCONTEXT_TAG_SCOPE"]
    family = os.environ["POWERCONTEXT_TAG_FAMILY"]
    artifact = os.environ["POWERCONTEXT_TAG_ARTIFACT"]
    async with PowerContextClient(
        "http://127.0.0.1:8000", token=os.getenv("POWERCONTEXT_SERVER_AUTH_TOKEN")
    ) as client:
        current = await client.get_artifact_tags(scope, family, artifact)
        if current is None:
            raise RuntimeError("An unconditional read must return the current tag set")
        saved = await client.replace_artifact_tags(
            scope, family, artifact,
            ReplaceArtifactTagsRequest.model_validate({"tags": ["customer-a", "release"]}),
            expected_etag=current.etag,
        )
        print(saved.tag_set.model_dump(mode="json")["tags"])
        matches = await client.query_artifact_tags(
            scope, QueryArtifactTagsRequest.model_validate({"tags": ["CUSTOMER-A"]})
        )
        print([item.target.model_dump(mode="json") for item in matches.items])


asyncio.run(main())

The output contains the saved labels and a matching target. Atomic Memory uses the same get_artifact_tags and replace_artifact_tags operations with family="atomic-memory" and the returned artifact_id.

Legacy entry tag routes still accept (scope_id, artifact_id, entry_id) and resolve the migrated Atomic identity in the API adapter. Entries already absent from the old current manifest cannot use this logical lookup; retained old citations support exact historical reads. New memories have no legacy entry ID. See Atomic Memory.

HTTP and retrieval filters

MethodPathPurpose
GET / PUT/v1/scopes/{scope_id}/artifacts/{family}/{artifact_id}/tagsRead or replace an Artifact's labels
GET / PUT/v1/scopes/{scope_id}/artifacts/memory/{artifact_id}/entries/{entry_id}/tagsCompatibility read or replacement of a migrated legacy entry's labels
POST/v1/scopes/{scope_id}/artifact-tags/queryFind tagged targets across families

PUT accepts {"tags":["customer-a","release"]} and requires the ETag returned by GET in If-Match. Missing If-Match returns 428; stale or wrong-target state returns 412. An unchanged conditional GET returns 304. tag_digest describes the canonical tag set, but is not the HTTP mutation precondition.

Artifact listing accepts repeated tag parameters and optional tag_match=all|any, for example /v1/scopes/{scope_id}/artifacts/skill?tag=release&tag=customer-a&tag_match=all. Supplying tag_match without tag is invalid.

Atomic Memory list/search requests accept these tag fields:

{"tags":["customer-a","release"],"tag_match":"all"}

Compatibility routes such as /v1/memory/search retain the old tag_filter request shape. Both filter each independent memory's tags. Authorization and tag eligibility are applied before candidate limits and reranking; search returns active memories only. Atomic vector search currently uses exact distance ordering on SQLite and OceanBase, with work proportional to eligible vector count and dimension.

Tag queries return exact current Artifact references, ordered by family, target type, Artifact ID, and target ID. Omit families for currently supported families or use, for example, {"tags":["release"],"families":["atomic-memory","skill"]}. Legacy Memory collections are not a current memory discovery surface. Tags survive content revisions and Server restarts.

Pass next_cursor unchanged with the same filters, Scope, and caller. Cursors expire after one hour; invalid or mismatched cursors return 400, expired cursors 410. Each page is internally consistent, but pagination does not freeze a snapshot across requests.

Label rules and storage

  • A target holds at most 32 labels; a filter accepts 1–16 labels.
  • Each label has 1–64 Unicode code points, no outer whitespace, and no control, surrogate, or unassigned characters.
  • Matching uses NFC normalization followed by Unicode case folding. The submitted display spelling is retained. Normalized duplicates such as Straße and STRASSE are rejected atomically. A normalized key may not exceed 128 code points.
  • Tags are Scope-local discovery metadata, not permissions or trusted instructions. They are not added to model prompts, Skill package frontmatter, or publication/import payloads. Published copies start without the source target's tags.
  • Inactive memories retain their authoritative tags. Rebuilding active search projections does not delete them.

All assignments live in pc_artifact_tags, with a foreign key to the owning Artifact head. The table retains the full normalized key and indexes a 32-byte SHA-256 key fingerprint. This preserves the parent column lengths required by OceanBase while keeping composite indexes below its 3072-byte limit. Matching checks both fingerprint and complete key. Identical replacement preserves assignment timestamps. Existing Artifacts need no backfill; their initial tag sets are empty. Include the table in backups and restore it after Artifact heads.

See the Server's HTTP API reference for complete request and response schemas.

On this page