Agotalk Role Package — Creator Guide (v0.3)

This is the public creator guide for creating an Agotalk role package. The stable package file contract lives at /docs/role-package.md. Audience: AI agents and developers. Read this whole file before generating anything.

A role package is a plain directory of files, shipped as a plain zip. The package truth is role.md plus the file tree. agotalk.role.toml is the machine contract that adds identity, preview metadata, and (for applications) a launchable entry. There is no other manifest. Do not invent one.

1. Three capabilities — pick exactly one path

Importable, launchable, and publishable are three different contracts. Decide what you are building first:

A single directory with a valid role.md and NO agotalk.role.toml. Agotalk imports it into the local Role Library as a candidate. Not launchable. Not publishable to the website (publishing requires the machine contract).

kind = "application" with an [entry] that is a complete, valid static_browser entry. Applications get a Launch button.

kind = "role" with NO [entry] section at all. role.md is the sole behavior source: the role talks, it does not launch. The publisher accepts it exactly like an application. In the Role Library it appears as a normal selectable candidate without a Launch button.

Hard rule behind the paths, dispatched by kind:

entry.kind = "static_browser" passes the contract parser. Unknown entry kinds fail loudly with ROLE_ENTRY_KIND_UNSUPPORTED.

with ROLE_CONTRACT_FIELD_INVALID; a missing role.md fails with ROLE_BEHAVIOR_FILE_MISSING. There is no system_prompt entry kind and there never was a working one — the behavior contract is role.md itself.

Publishable examples: words-john, teacher-tori, visual-media-assistant Import-only examples: any Path A directory (role.md without agotalk.role.toml)

The live published examples are words-john (https://roles.agotalk.com/roles/words-john), a Path B application, and teacher-tori (https://roles.agotalk.com/roles/teacher-tori), a Path C conversational role. Their official sources live in the separate agotalk-roles repository, not in the Agotalk app repository.

2. Package layout

<role-id>/
  role.md              # REQUIRED. Role intro, boundaries, how to start. Import entry.
  agotalk.role.toml    # Path B/C: machine contract (identity, preview; entry for B only)
  avatar.png           # optional role avatar at the package root (recommended 512x512 PNG)
  intro.html           # optional rich presentation page (static HTML only)
  skills/              # optional agent instructions (*.md). No software here.
  contents/            # optional real software content: source, pages, locks
  assets/              # optional presentation media (covers, screenshots)

Rules:

(example: words-john). The directory name IS the role id.

contain the software itself.

declares runtime dependencies.

teacher-tori ships curriculum under resources/). Keep everything inside the package directory.

assets/), recommended 512×512 PNG. If omitted the role shows the default Agotalk avatar. Do NOT declare an avatar: field in role.md front matter — that field is retired (tk2332) and the import gate rejects any package still carrying it with ROLE_PACKAGE_LEGACY_AVATAR_FIELD.

3. role.md (required)

Front matter with title and description, then the role narrative: who the role is, what it does, its boundaries, and how a session should start. The Role Library shows title and summary from this file. For Path C roles this file IS the entire behavior contract — write it with care.

The only front matter keys consumed are title and description. The avatar: key is retired (tk2332): the avatar is the package-root avatar.png, never a front matter value. A package whose role.md front matter still contains avatar: is rejected at import with ROLE_PACKAGE_LEGACY_AVATAR_FIELD — delete the line and place avatar.png at the package root instead.

Front matter field status (the parser is the contract):

| Key | Status | Notes | |---|---|---| | title | consumed | Falls back to the role id (directory name) if absent/empty. | | description | consumed | Falls back to "" if absent/empty. | | id | ignored as identity | Role identity is ALWAYS the directory name (path.basename); a front matter id is tolerated but never overrides it. | | avatar | retired (tk2332) | Hard-rejected at import (ROLE_PACKAGE_LEGACY_AVATAR_FIELD); installed roles only log. Use package-root avatar.png instead. | | any other key | tolerated, ignored | Unknown keys do not error; they are simply not read. Do not rely on them carrying meaning. |

4. agotalk.role.toml (machine contract)

Path B — launchable application:

schema_version = 1                # fixed
id = "words-john"                 # must equal the top-level directory name
version = "0.1.0"                 # SemVer string
title = "Words John"
summary = "Typing word game bundled as role package content."
kind = "application"              # dispatches the entry contract
license = "free"                  # v0.2: "free"
min_app_version = "0.1.57"        # optional: minimum Agotalk App version

[entry]                           # mandatory for kind = "application"
kind = "static_browser"           # the only kind that parses and publishes today
target = "contents/word-pop-game/index.html"  # relative, must stay inside the package
address_bar = "hidden"            # "visible" | "hidden"

[preview]
intro_html = "intro.html"
cover_image = "assets/screenshots/word-pop-game-preview.png"
screenshots = ["assets/screenshots/word-pop-game-preview.png"]

[state]
remembers_progress = false        # declare true only if the app really saves progress

[environment]
manager = "agotalk"               # fixed
dependency_mode = "none"          # static content needs nothing else

Path C — conversational role (note: NO [entry] section):

schema_version = 1
id = "teacher-tori"
version = "0.1.0"
title = "Tori"
summary = "Tori — a warm but exacting English teacher."
kind = "role"                     # conversational: role.md is the behavior source
license = "free"
min_app_version = "0.1.57"        # optional: minimum Agotalk App version

[preview]
intro_html = "intro.html"

[state]
remembers_progress = true

[environment]
manager = "agotalk"
dependency_mode = "none"

Notes:

complete [entry]; conversational roles must not declare one at all.

feeds the catalog; the publisher can also receive an explicit --min-app-version. One of the two must exist to publish — the publisher never silently substitutes its own version.

also projects avatar.png, preview.cover_image, and preview.screenshots into the website presentation assets (converted to WebP and served from the catalog) — package images DO drive the website. Recommended specs (not enforced by the parser): - avatar.png at the package root — 512×512, 1:1, PNG. - cover_image — 1280×720, 16:9, PNG. - screenshots — 1 to 3 images, 1280×720, 16:9, PNG. - 16:9 at 1280×720 matches og:image / Twitter card / Steam capsule conventions; authors can drop a 16:9 capture without re-cropping. - Keep each image under ~1 MB. - Avoid JPEG for screenshots (lossy edges blur text) and avoid SVG for screenshots (use SVG only for logos/icons). GIF is accepted but not recommended for detailed captures. - Keep source images PNG; the publisher converts website presentation copies to WebP automatically. Authors never pre-convert to WebP.

locale — e.g. [i18n."zh-CN"] with exactly title and summary keys (same length limits as the top-level fields). Locale keys look like en or zh-CN; at most 8 locales. Top-level title/summary are the canonical fallback — write them in the role's primary language. The catalog and the website language pages pick the locale entry when present.

(including anything you may have seen in older drafts) fails loudly with ROLE_ENTRY_KIND_UNSUPPORTED. Do not experiment with entry kinds that are not in this guide — the parser is the contract.

package root and must stay inside it.

"free". Any other value fails with ROLE_CONTRACT_FIELD_INVALID.

environment.dependency_mode must be exactly "none". Any entry kind that would imply a dev server, dependency install, or local backend is rejected by the parser — today only self-contained static apps ship as role packages.

5. Authoring app content and role-specific skills

This section is for Path B packages (and Path C roles that ship reference content). It describes what the app running inside Agotalk can and cannot do, and how to write the role-specific Markdown under skills/.

What the app environment can and cannot do

A static_browser app opens inside Agotalk's bundled browser view. That view is a sandboxed web page: it renders HTML/CSS/JS from files inside the package, with no access to the local machine. Specifically:

cannot spawn processes, read arbitrary files, run installed tools, or start a backend / dev server.

tmux, git, ffmpeg, mise, etc.). Those tools serve the agent's workspace shell, which is a separate channel from the role-package app.

fetch of package-relative resources, canvas, WebGL, Web Workers, service workers, FileReader, drag-and-drop, clipboard, and so on.

fonts, templates, or data from a CDN or any network endpoint when the app starts — bundle everything into the package.

If your application needs a local backend, a dev server, or a dependency install step to run, it cannot ship as a role-package app today. Convert it to a self-contained static build (vendored libraries, build output only) before packaging.

Putting content into contents/

contents/ holds the runnable artifact of your app — the files Agotalk's browser view will load. There is no install step at launch.

<role-id>/
  contents/
    <app>/
      index.html        # entry.target points here
      app.js            # built or hand-written; vendored, not installed
      style.css
      vendor/           # third-party JS/CSS copied in at a pinned version
      assets/           # app-local media (icons, fonts, templates)

Rules:

a bundler, the compiled bundle is what goes under contents/<app>/; source and build instructions stay in a separate folder or repo. The package must never require npm install / pnpm install / pip install at launch.

the exact version you tested. Record the pinned versions (a comment in index.html or a note under skills/) so the package stays auditable.

[state] remembers_progress = true only if that data actually survives a reload; otherwise leave it false.

What "Agotalk's bundled runtime" means here

Agotalk ships a curated, version-locked toolchain (node, python, pnpm, mise, tmux, git, ImageMagick, ffmpeg, rg, fd, jq, sqlite, and more) that the agent can call from its workspace shell to do real work on the user's machine. That is a powerful capability — but it belongs to the agent channel, not to the role-package app.

to maintain, regenerate, or extend the app's contents/ (for example, "rebuild the bundle with pnpm build", "generate sprites with ImageMagick"). The agent runs these in its workspace shell.

these two channels separate in your design: the agent maintains the artifact, the browser view runs it.

Writing role-specific skills

skills/*.md are Markdown instructions that tell the agent how to use or modify the contents/ shipped with this role. They are NOT software. Put one Markdown file per distinct job, named after the job.

<role-id>/
  skills/
    <job-name>.md   # one file per job; e.g. rebuild-bundle.md, export-data.md

Each file should state: the job, the exact files or UI it touches, the step sequence (including any bundled tools the agent should call from its workspace shell), and how to verify it worked. Keep them short and concrete — they are read by an agent at session time.

Hard rule (repeated from §2): skills/ holds only Markdown. Any executable, script, stylesheet, image, or binary belongs in contents/ or assets/, never in skills/.

Hard rule (repeated from §2): skills/ holds only Markdown. Any executable, script, stylesheet, image, or binary belongs in contents/ or assets/, never in skills/.

6. What publishing consumes

The official publisher runs the full contract parse dispatched by kind (Path B and Path C both pass; Path A has no contract file and cannot publish), then the catalog and website use: id, version, title, summary, min_app_version (from the manifest or explicit publisher input), plus the computed zip sha256 and sizes. Website preview media is curated on the site side. Publication itself happens through the official publisher pipeline; this guide only defines the package you hand over.

7. Transport zip

words-john-0.1.0.zip
  words-john/
    role.md
    agotalk.role.toml
    avatar.png
    intro.html
    skills/
    contents/
    assets/

Rules:

entries, no .DS_Store.

Import behavior you must expect: Agotalk unpacks the zip into the local Role Library (roles-lib) as a candidate, validates role.md, and shows the candidate. Importing an id that already exists is rejected with ROLE_IMPORT_DUPLICATE — versions never silently overwrite. A candidate never becomes the active workspace role without explicit user action.

8. Hard boundaries

runtime, this contract, and files locked inside contents/ may be assumed.

to publish.** A published package is public — anything inside it is exposed. If you are building a package strictly for your own local use and you choose to embed your own keys, that is your call and your risk; but such a package must not be published through the official pipeline. The official publisher does not scan for secrets, so the discipline is yours.

loudly. Do not guess silently.

9. Generation instruction (hand this to an agent)

Create an Agotalk role package.

1. Read https://roles.agotalk.com/role-package-creator.md fully first.
2. Pick the capability path: launchable web app for the website -> Path B
   (kind = "application" with a static_browser [entry]); conversational role
   for the website -> Path C (kind = "role", role.md as behavior, NO [entry]);
   local-only experiment -> Path A (role.md, NO agotalk.role.toml).
3. Choose <role-id> (lowercase a-z0-9 and hyphens) and create the directory <role-id>/.
4. Write role.md: front matter (title, description), then the role narrative,
   boundaries, and session start guidance. For Path C this file is the whole
   behavior contract.
5. If the role ships an app or content: put real files under contents/,
   agent how-to notes under skills/, presentation media under assets/.
6. Path B/C: add agotalk.role.toml exactly as specified above. Path B needs a
   complete [entry] with an existing HTML target inside the package; Path C
   must not contain any [entry] section.
7. Zip as <role-id>-<version>.zip with the single top-level directory <role-id>/.
8. Run the self-check below. Fix every failure before delivering. If a required
   fact is unknown, report it instead of inventing it.

10. Self-check before delivery

agotalk.role.toml (when present).

tk2332). The avatar, if any, is a package-root avatar.png (recommended 512x512 PNG). An avatar: line inside the role narrative body is fine; only the front matter block is gated.

complete [entry] with kind = "static_browser" and an existing .html target inside the package. Path C packages declare kind = "role" and contain NO [entry] section at all.

environment.manager is exactly "agotalk", and environment.dependency_mode is exactly "none". These four fields are fixed today; any other value fails the contract.

(entry.target, intro_html, cover_image, every screenshot).

512×512 1:1 PNG, cover + screenshots 1280×720 16:9 PNG, each image under ~1 MB. If the website needs WebP, convert curated website media in the website release chain; the role package source contract stays PNG. (Not enforced by the parser.)

package. Local-only packages are your own risk but must not be published.

ROLE_IMPORT_ROLE_MD_MISSING, ROLE_IMPORT_DUPLICATE, ROLE_PACKAGE_LEGACY_AVATAR_FIELD, ROLE_CONTRACT_MISSING, ROLE_CONTRACT_INVALID, ROLE_CONTRACT_FIELD_MISSING, ROLE_CONTRACT_FIELD_INVALID, ROLE_BEHAVIOR_FILE_MISSING, ROLE_ID_DIR_MISMATCH, ROLE_ENTRY_KIND_UNSUPPORTED, ROLE_ENTRY_ADDRESS_BAR_INVALID, ROLE_ENTRY_TARGET_NOT_FOUND, ROLE_ENTRY_TARGET_NOT_FILE, ROLE_ENTRY_TARGET_OUTSIDE_PACKAGE, ROLE_ENVIRONMENT_INVALID, ROLE_PREVIEW_INVALID, ROLE_PREVIEW_NOT_FOUND, ROLE_PREVIEW_NOT_FILE, ROLE_PREVIEW_OUTSIDE_PACKAGE, ROLE_STATE_REMEMBERS_PROGRESS_INVALID, ROLE_IMPORT_ZIP_TOP_LEVEL_INVALID, ROLE_IMPORT_ZIP_ENTRY_INVALID, ROLE_KIND_NOT_LAUNCHABLE.