diff --git a/.gitignore b/.gitignore
index 9012af483..c8059bfec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -24,3 +24,8 @@ __pycache__/
/.agents/
/.issue-agent/
/ISSUE_AGENT.md
+
+# Documentation site build output.
+/apps/docs/.next/
+/apps/docs/.source/
+/apps/docs/tsconfig.tsbuildinfo
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index bf56154e7..b32f49e9c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -3665,3 +3665,12 @@ unchanged. Persisted credential encryption domains and native-session resume key
also remain stable so existing data can be decrypted and Sessions can resume.
Conversion inputs, retirement diagnostics and historical evidence must still name
the identifiers they reject or migrate; current examples use the new names.
+
+
+Core administration error details are scoped by the `/core/v1` router writer mark,
+not a request path test. Use `writeCoreError` with typed `CoreErrorDetails` values
+and document fixed keys in `contracts/agents-api/core-errors.md` when adding a
+code. Include only safe Core-owned facts; never pass submitted values, secrets,
+native text or provider bodies. Invalid/empty details are omitted. Preserve the
+public and machine error serializers, observer callbacks and streaming interfaces.
+The Core client ignores malformed optional details and never retries a mutation.
diff --git a/Makefile b/Makefile
index 6d6b7e414..b1e3112bf 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ SWAG_VERSION ?= v1.16.4
help:
@printf '%s\n' 'make build-agents-api Build standalone Core commands' 'make build-daemon Build the execution daemon' 'make check Run Core, persistence and runtime checks' 'See README.md for runtime prerequisites and deployment.'
-check: check-names check-distribution check-database check-sqlc check-go check-microsandbox-provider check-agents-api check-claude-sdk check-web check-mcode-harness check-agents-executor
+check: check-docs check-names check-distribution check-database check-sqlc check-go check-microsandbox-provider check-agents-api check-claude-sdk check-web check-mcode-harness check-agents-executor
@printf 'OpenAgentCore checks passed.\n'
.PHONY: check-names
@@ -145,3 +145,7 @@ build-e2b-provider:
check-e2b-provider:
PYTHONDONTWRITEBYTECODE=1 $${OAC_TEST_E2B_SDK_PYTHON:-python3} -m unittest discover -s services/agents-api/deploy/e2b -p '*_test.py'
PYTHONDONTWRITEBYTECODE=1 $${OAC_TEST_E2B_SDK_PYTHON:-python3} -m unittest discover -s services/agents-api/tools/e2b-provider -p '*_test.py'
+
+.PHONY: check-docs
+check-docs: node-deps
+ pnpm check:docs
diff --git a/apps/docs/FOLLOW-UP.md b/apps/docs/FOLLOW-UP.md
new file mode 100644
index 000000000..232f913c1
--- /dev/null
+++ b/apps/docs/FOLLOW-UP.md
@@ -0,0 +1,24 @@
+# Feature integration follow-up
+
+This candidate documents the actual maintenance and drain model at source
+`75bf4484ba957344ae374354858bbdfe104f5c4f`. It is not final documentation for design 50.
+The Core error-envelope foundation is included; the later configuration validators and
+diagnostic endpoints are not. Do not replace current procedures with the planned
+behavior before the features land.
+
+| Feature dependency | Site pages to reconcile | Source and verification work |
+| --- | --- | --- |
+| Reset replaces maintenance (design 50 PR-R) | configure, hosted-providers, troubleshooting, admin-api, observability, API reference/core | Regenerate the canonical guides and management contract; describe reset admission, cleanup, failures, archive preconditions and offline-node limits from merged code. Remove retired maintenance instructions only after their replacement exists. |
+| Online generations and E2B changes (design 50 PR-G) | configure, hosted-providers, troubleshooting, execution-model, API reference/core | Document actual generation/rollout states, the supported change matrix and existing-sandbox behavior. Verify PUT examples against the final contract. |
+| Node update protocol (design 50 PR-N) | hosted-providers, troubleshooting, API reference/machine | Explain the implemented update command, old-node handling and ownership preservation. Do not claim an update command now. |
+| Later configuration/error changes (design 40 and remaining installer work) | install, configure, troubleshooting, quickstart | Regenerate current configuration fields and revise diagnostics against merged code; keep secrets out of examples. |
+| Final feature assembly | every Chinese reading note, execution-model diagram, all generated references | Review translations against regenerated English, verify source provenance and links, run contract/route/copy/fact checks, typecheck/build, local browser review and the repository gate. |
+
+English guides are rendered from repository sources. Chinese pages are explicitly
+labelled reading notes, not complete translations; expand them only against the final
+source procedures. API tag pages use the default-language fallback. Release publication,
+remote deployment and preview replacement are outside this reconciliation task.
+
+The source Web guide still carries a pre-rename screenshot. This site copies the
+approved current Web onboarding Monitor image instead, with its own source hash;
+refresh that mapping when the canonical guide image is updated.
diff --git a/apps/docs/README.md b/apps/docs/README.md
new file mode 100644
index 000000000..a526c4fa0
--- /dev/null
+++ b/apps/docs/README.md
@@ -0,0 +1,48 @@
+# OpenAgentCore documentation site
+
+A separate Next.js/Fumadocs documentation app. It starts no Core, database or Runtime.
+English is served at `/`; Chinese reading notes at `/zh`, with English fallback for
+API tag pages. The existing preview is not part of this checkout.
+
+The application reference reads the upstream-constrained public schema. Administration
+and machine references read their own local generated contracts. All three repository
+files currently declare Swagger 2.0; rendering supports Swagger 2 and OpenAPI 3.
+The documentation projection uses OpenAPI 3.1 null unions (including nullable references
+and compositions). Normalization changes documentation servers and bearer presentation, preserving operation
+prose, parameters, response schemas and credential boundaries. Examples use a reserved
+domain. The reference is read-only and must not collect keys or send execution requests.
+
+English guides are generated from the canonical repository docs, not maintained as a
+second installation manual. Edit those sources first, update the Chinese reading notes
+in [guides.json](scripts/guides.json), then regenerate. Source/output hashes fail when
+copies drift; operation and route checks detect missing or mixed API surfaces.
+
+From the repository root:
+
+```sh
+pnpm install --frozen-lockfile
+pnpm --dir apps/docs generate
+pnpm --dir apps/docs verify
+pnpm --dir apps/docs test
+pnpm --dir apps/docs typecheck
+pnpm --dir apps/docs build
+pnpm --dir apps/docs start --port 4275
+```
+
+The route verifier uses the declared `js-yaml` dependency and Python standard library
+only; no ambient PyYAML installation is required. Python checks honor
+`OAC_TEST_OFFICIAL_SDK_PYTHON` when set, otherwise `python3`, and run with `-S`
+to keep site packages out of the gate.
+
+`make check-docs` runs the focused gate and is included in `make check`. After starting
+the built site, run `pnpm --dir apps/docs check:site http://127.0.0.1:4275` and inspect
+both locales in a browser with `pnpm --dir apps/docs check:browser http://127.0.0.1:4275`.
+The check uses installed Playwright Chromium (or `DOCS_BROWSER_EXECUTABLE`), checks
+for credential/request controls and external traffic, and accepts only a local origin.
+Set `DOCS_SITE_ORIGIN` only when preparing actual publication.
+Generation links to the recorded source revision; update that revision after rebasing
+onto later canonical guide changes. Never point samples at an actual deployment by default.
+
+[Feature follow-up inventory](FOLLOW-UP.md) lists the reset and online-generation
+paragraphs that must be reconciled after those implementations land. This candidate
+cannot establish final documentation acceptance for those unfinished features.
diff --git a/apps/docs/app/[lang]/[[...slug]]/page.tsx b/apps/docs/app/[lang]/[[...slug]]/page.tsx
new file mode 100644
index 000000000..941dd8097
--- /dev/null
+++ b/apps/docs/app/[lang]/[[...slug]]/page.tsx
@@ -0,0 +1,78 @@
+import type { Metadata } from "next"
+import { notFound } from "next/navigation"
+import { ImageZoom } from "fumadocs-ui/components/image-zoom"
+import defaultMdxComponents from "fumadocs-ui/mdx"
+import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page"
+import { APIPage } from "@/components/api-page"
+import { LocaleLink } from "@/components/locale-link"
+import { i18n, type Lang } from "@/lib/i18n"
+import { docsAlternates } from "@/lib/site"
+import { source } from "@/lib/source"
+
+function asLang(value: string): Lang {
+ if (!(i18n.languages as readonly string[]).includes(value)) notFound()
+ return value as Lang
+}
+
+function pageSlug(slug?: string[]) {
+ return slug ?? []
+}
+
+export default async function DocsPageRoute({
+ params,
+}: {
+ params: Promise<{ lang: string; slug?: string[] }>
+}) {
+ const { lang: rawLang, slug } = await params
+ const lang = asLang(rawLang)
+ const page = source.getPage(pageSlug(slug), lang)
+ if (!page) notFound()
+
+ const MDX = page.data.body
+ return (
+
+ {page.data.title}
+ {page.data.description}
+
+ ,
+ }}
+ />
+
+
+ )
+}
+
+export function generateStaticParams() {
+ return source.generateParams()
+}
+
+export async function generateMetadata({
+ params,
+}: {
+ params: Promise<{ lang: string; slug?: string[] }>
+}): Promise {
+ const { lang: rawLang, slug } = await params
+ const slugs = pageSlug(slug)
+ const page = source.getPage(slugs, asLang(rawLang))
+ if (!page) return {}
+ return {
+ title: page.data.title,
+ description: page.data.description,
+ // Declare the translations as alternates of one article, and name the
+ // canonical URL, so search engines do not index the English and Chinese
+ // pages as duplicates of each other.
+ alternates: docsAlternates(slugs),
+ }
+}
diff --git a/apps/docs/app/[lang]/docs-providers.tsx b/apps/docs/app/[lang]/docs-providers.tsx
new file mode 100644
index 000000000..f7b40a7e4
--- /dev/null
+++ b/apps/docs/app/[lang]/docs-providers.tsx
@@ -0,0 +1,77 @@
+"use client"
+
+import Image from "next/image"
+import Link from "next/link"
+import { useParams, usePathname, useRouter } from "next/navigation"
+import { FrameworkProvider, type Framework } from "fumadocs-core/framework"
+import { RootProvider } from "fumadocs-ui/provider/base"
+import { useCallback, type ReactNode } from "react"
+import { i18n, type Lang } from "@/lib/i18n"
+
+const locales = [
+ { name: "English", locale: "en" },
+ { name: "中文", locale: "zh" },
+]
+
+const translations = {
+ en: {
+ toc: "On this page",
+ chooseLanguage: "Choose language",
+ nextPage: "Next page",
+ previousPage: "Previous page",
+ },
+ zh: {
+ toc: "本页目录",
+ chooseLanguage: "选择语言",
+ nextPage: "下一页",
+ previousPage: "上一页",
+ },
+} as const
+
+function isLocale(segment: string | undefined): boolean {
+ return Boolean(segment) && (i18n.languages as readonly string[]).includes(segment as string)
+}
+
+function pathForLocale(pathname: string, locale: string) {
+ const segments = pathname.split("/").filter(Boolean)
+ // Drop a leading locale segment, whichever language it names — matching on a
+ // literal "en"/"zh" pair here would silently break when a language is added.
+ if (isLocale(segments[0])) segments.shift()
+ const rest = segments.join("/")
+ if (locale === i18n.defaultLanguage) return rest ? `/${rest}` : "/"
+ return rest ? `/${locale}/${rest}` : `/${locale}`
+}
+
+export function DocsProviders({ lang, children }: { lang: Lang; children: ReactNode }) {
+ const router = useRouter()
+ const pathname = pathForLocale(usePathname(), lang)
+ // Navigation must use the public path during both SSR and hydration.
+ const usePublicPathname = useCallback(() => pathname, [pathname])
+
+ return (
+
+ router.push(pathForLocale(pathname, locale)),
+ }}
+ >
+ {children}
+
+
+ )
+}
diff --git a/apps/docs/app/[lang]/layout.tsx b/apps/docs/app/[lang]/layout.tsx
new file mode 100644
index 000000000..91e648c52
--- /dev/null
+++ b/apps/docs/app/[lang]/layout.tsx
@@ -0,0 +1,54 @@
+import "../global.css"
+import type { Metadata } from "next"
+import { notFound } from "next/navigation"
+import type { ReactNode } from "react"
+import { DocsLayout } from "fumadocs-ui/layouts/docs"
+import { baseOptions } from "../layout.config"
+import { source } from "@/lib/source"
+import { i18n, type Lang } from "@/lib/i18n"
+import { DocsProviders } from "./docs-providers"
+
+export const metadata: Metadata = {
+ title: {
+ template: "%s | OpenAgentCore Docs",
+ default: "OpenAgentCore Docs",
+ },
+ description: "Documentation for OpenAgentCore.",
+}
+
+export function generateStaticParams() {
+ return i18n.languages.map((lang) => ({ lang }))
+}
+
+export default async function LangLayout({
+ params,
+ children,
+}: {
+ params: Promise<{ lang: string }>
+ children: ReactNode
+}) {
+ const { lang: rawLang } = await params
+ if (!(i18n.languages as readonly string[]).includes(rawLang)) notFound()
+ const lang = rawLang as Lang
+
+ return (
+
+ {/* Browser extensions may add attributes before React hydrates the body. */}
+
+
+
+ {children}
+
+
+
+
+ )
+}
diff --git a/apps/docs/app/global.css b/apps/docs/app/global.css
new file mode 100644
index 000000000..40503ed92
--- /dev/null
+++ b/apps/docs/app/global.css
@@ -0,0 +1,103 @@
+@import "tailwindcss";
+@import "fumadocs-ui/css/neutral.css";
+@import "fumadocs-ui/css/preset.css";
+@import "fumadocs-openapi/css/preset.css";
+
+:root {
+ --color-fd-primary: hsl(240 5.9% 10%);
+}
+
+/* Keep the active navigation item readable in dark mode. The neutral theme
+ defines a light primary color for dark surfaces; this override must be more
+ specific than the root token declared above. */
+.dark {
+ --color-fd-primary: hsl(0 0% 98%);
+ --color-fd-primary-foreground: hsl(240 5.9% 10%);
+}
+
+body {
+ font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI",
+ "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
+}
+
+/* Medium weight maps to regular in common CJK fonts. */
+.prose :where(strong):not(:where(.not-prose, .not-prose *)) {
+ font-weight: 700;
+}
+
+.prose pre {
+ min-width: 0;
+ width: 100%;
+}
+
+/* Code blocks wrap instead of scrolling sideways. */
+.prose pre,
+.prose pre code {
+ white-space: pre-wrap;
+ overflow-wrap: anywhere;
+}
+
+/* Inline code wraps only when a token cannot fit on a line of its own.
+ `overflow-wrap: anywhere` also reduces the element's min-content width to
+ roughly one character, and the automatic table layout then treats a column
+ as shrinkable to that width: a five-column table collapsed the first column
+ to 49px and split `environment.type` across five lines. `break-word` breaks
+ a long token as a last resort without affecting intrinsic sizing, so a
+ column keeps at least its longest parameter. */
+.prose :not(pre) > code {
+ white-space: pre-wrap;
+ overflow-wrap: break-word;
+}
+
+/* Give every table column a readable floor. The minimum content width of a
+ column that holds only Chinese text is about one character, while a column
+ holding a parameter such as `OAC_DATABASE_URL` cannot shrink below
+ that parameter. The automatic layout satisfies the wider columns first and
+ can crush the Chinese one to about four characters per line. Five columns of
+ 7rem still fit the narrowest content area the layout produces. */
+.prose table :is(th, td) {
+ min-width: 7rem;
+}
+
+/* Make the page-tree separators read as navigation groups, not page links. */
+#nd-sidebar [data-radix-scroll-area-viewport] p {
+ width: 100%;
+ margin-top: 1.25rem;
+ margin-bottom: 0.5rem;
+ padding: 0.35rem 0.5rem;
+ border-bottom: 1px solid var(--color-fd-border);
+ color: var(--color-fd-muted-foreground);
+ font-size: 0.7rem;
+ font-weight: 700;
+ letter-spacing: 0;
+ line-height: 1.25rem;
+}
+
+#nd-sidebar [data-radix-scroll-area-viewport] p:first-of-type {
+ margin-top: 0;
+}
+
+#nd-sidebar [data-radix-scroll-area-viewport] a[href^="/"] {
+ min-height: 2.25rem;
+ font-size: 0.875rem;
+}
+
+/* Keep the language menu compact inside the sidebar and on narrow screens. */
+[role="dialog"][data-side] {
+ width: min(11rem, calc(100vw - 2rem));
+ min-width: 0 !important;
+ /* Radix anchors this bottom-left menu at x=0; keep it off the viewport edge. */
+ margin-left: 0.75rem;
+}
+
+[role="dialog"][data-side] button {
+ width: 100%;
+}
+
+/* Text selection: tint the background with the theme's primary color and keep
+ the original text color, so contrast is inherited from body text in both
+ themes. Forcing --color-fd-primary-foreground here previously produced white
+ text on a mid-gray highlight in light mode. */
+::selection {
+ background: color-mix(in srgb, var(--color-fd-primary) 22%, transparent);
+}
diff --git a/apps/docs/app/icon.png b/apps/docs/app/icon.png
new file mode 100644
index 000000000..83ad57715
Binary files /dev/null and b/apps/docs/app/icon.png differ
diff --git a/apps/docs/app/layout.config.tsx b/apps/docs/app/layout.config.tsx
new file mode 100644
index 000000000..68f3bf2c9
--- /dev/null
+++ b/apps/docs/app/layout.config.tsx
@@ -0,0 +1,11 @@
+import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"
+
+export const baseOptions: BaseLayoutProps = {
+ i18n: true,
+ nav: {
+ title: "OpenAgentCore Docs",
+ },
+ searchToggle: {
+ enabled: false,
+ },
+}
diff --git a/apps/docs/app/not-found.tsx b/apps/docs/app/not-found.tsx
new file mode 100644
index 000000000..fa0686507
--- /dev/null
+++ b/apps/docs/app/not-found.tsx
@@ -0,0 +1,69 @@
+"use client"
+
+import Link from "next/link"
+import { usePathname } from "next/navigation"
+import { i18n } from "@/lib/i18n"
+
+const COPY = {
+ en: {
+ title: "Page not found",
+ body: "The page you are looking for does not exist, or it moved when the manual was reorganised.",
+ back: "Back to the manual",
+ },
+ zh: {
+ title: "页面不存在",
+ body: "你访问的页面不存在,或已随手册改版移动到别处。",
+ back: "返回手册首页",
+ },
+} as const
+
+/**
+ * 404 for the whole site. Two things about its placement are deliberate.
+ *
+ * It lives at the app root, not at `app/[lang]/`. A `not-found` boundary inside
+ * the segment that also holds the root layout is never compiled as a boundary,
+ * so the version that used to sit there was dead code: every 404 got Next's
+ * built-in notice instead.
+ *
+ * It is a client component that reads the language from the URL. Taking the
+ * language from the request on the server — `headers()`, or any other
+ * request-scoped API — opts *every sibling route* out of static generation,
+ * because the boundary belongs to the `[lang]/[[...slug]]` render tree. That is
+ * not a small regression: it made all 102 docs pages re-render per request, and
+ * the generated API reference pages carry whole OpenAPI surfaces, so a single
+ * navigation took two to three seconds instead of thirty milliseconds.
+ *
+ * The cost of the trade is that this notice arrives with hydration rather than
+ * in the first response, and outside the docs chrome: without a root layout
+ * Next renders a root `not-found` in its own shell. A 404 is `noindex` and a
+ * dead end, so paying there to keep every real page prerendered is the right
+ * way round.
+ *
+ * If this moves back to the server, `pnpm build` must still write prerendered
+ * `.html` files under `.next/server/app/en/` — the build log reporting
+ * `● (SSG)` is not sufficient, it says that either way.
+ */
+export default function NotFound() {
+ const pathname = usePathname()
+ const first = pathname.split("/").filter(Boolean)[0]
+ const locale =
+ first && (i18n.languages as readonly string[]).includes(first)
+ ? first
+ : i18n.defaultLanguage
+
+ const text = COPY[locale as keyof typeof COPY] ?? COPY.en
+ const home = locale === i18n.defaultLanguage ? "/" : `/${locale}`
+
+ return (
+
+ {text.title}
+ {text.body}
+
+ {text.back}
+
+
+ )
+}
diff --git a/apps/docs/app/sitemap.ts b/apps/docs/app/sitemap.ts
new file mode 100644
index 000000000..0600f3c8f
--- /dev/null
+++ b/apps/docs/app/sitemap.ts
@@ -0,0 +1,48 @@
+import type { MetadataRoute } from "next"
+import { i18n } from "@/lib/i18n"
+import { absoluteDocsUrl } from "@/lib/site"
+import { source } from "@/lib/source"
+
+/**
+ * Sitemap for the manual, built from Fumadocs' page list at build time.
+ *
+ * One entry per logical page: every language variant of a page is declared as
+ * an hreflang alternate of the same entry, so a search engine reads the English
+ * and Chinese pages as one article in two languages rather than as duplicates.
+ */
+export default function sitemap(): MetadataRoute.Sitemap {
+ // Collapse the language variants into one group per slug.
+ const bySlug = new Map>()
+
+ for (const { language, pages } of source.getLanguages()) {
+ for (const page of pages) {
+ const slugKey = page.slugs.join("/")
+ const languages = bySlug.get(slugKey) ?? new Map()
+ languages.set(language, page.url)
+ bySlug.set(slugKey, languages)
+ }
+ }
+
+ const entries: MetadataRoute.Sitemap = []
+
+ for (const languages of bySlug.values()) {
+ // Canonical is the default language when it exists, otherwise the first
+ // translation available.
+ const canonicalRelative =
+ languages.get(i18n.defaultLanguage) ?? languages.values().next().value
+ if (!canonicalRelative) continue
+
+ const alternates: Record = {}
+ for (const [lang, relative] of languages) {
+ alternates[lang] = absoluteDocsUrl(relative)
+ }
+ alternates["x-default"] = absoluteDocsUrl(canonicalRelative)
+
+ entries.push({
+ url: absoluteDocsUrl(canonicalRelative),
+ alternates: { languages: alternates },
+ })
+ }
+
+ return entries
+}
\ No newline at end of file
diff --git a/apps/docs/components/api-page.tsx b/apps/docs/components/api-page.tsx
new file mode 100644
index 000000000..3976f2a65
--- /dev/null
+++ b/apps/docs/components/api-page.tsx
@@ -0,0 +1,13 @@
+import { APIPage as OpenAPIPage, type ApiPageProps } from "fumadocs-openapi/ui"
+import { loadSurface } from "@/lib/openapi"
+
+// Registered as `APIPage` for the MDX components map. The generated pages name
+// their API surface by id; the contract itself is loaded through the server
+// instance so the rendered schema and the generated prose stay in step.
+export async function APIPage({
+ document,
+ ...props
+}: Omit & { document: string }) {
+ // References never collect credentials or dispatch requests from the browser.
+ return
+}
diff --git a/apps/docs/components/locale-link.tsx b/apps/docs/components/locale-link.tsx
new file mode 100644
index 000000000..136081f81
--- /dev/null
+++ b/apps/docs/components/locale-link.tsx
@@ -0,0 +1,24 @@
+import type { AnchorHTMLAttributes, ReactNode } from "react"
+import { i18n } from "@/lib/i18n"
+import { prefixLocale } from "@/lib/locale-link"
+
+type LocaleLinkProps = AnchorHTMLAttributes & {
+ lang: string
+ children?: ReactNode
+}
+
+/**
+ * MDX anchor override for the docs routes.
+ *
+ * The active language is passed in by the route, which knows it from the URL,
+ * so no client-side context is required and this stays a server component. It
+ * renders the same plain anchor Fumadocs uses by default, after adding the
+ * locale prefix — see `prefixLocale`.
+ */
+export function LocaleLink({ lang, href, children, ...rest }: LocaleLinkProps) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/apps/docs/content/docs/admin-api.mdx b/apps/docs/content/docs/admin-api.mdx
new file mode 100644
index 000000000..1e9b830cc
--- /dev/null
+++ b/apps/docs/content/docs/admin-api.mdx
@@ -0,0 +1,160 @@
+---
+title: "Core administration API"
+description: "Management operations and their server-side credential boundary."
+---
+
+Core Web is an administrator console over `/core/v1`, authorized by the Core key:
+resource inspection, Project and key management, credential issuance, audit, usage
+and sandbox operations. It never calls `/v1` or `/api/v1`, and has no Agent
+execution, copy or arbitrary asset editing operation.
+
+Core request failures use the [Core administration error envelope](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core-errors.md),
+including optional typed safe details. Console sign-in endpoints retain their
+separate error shape described below.
+
+## Browser to console
+
+The browser uses the console's own origin and signs in with the
+[Core key](/troubleshooting#core-key):
+
+| Method and route | Request | Result |
+| --- | --- | --- |
+| `GET /console/auth` | No body | `200 {"mode":"login"}` or `200 {"mode":"authenticated"}` |
+| `POST /console/auth/login` | `Content-Type: application/json`, body `{"core_key":"…"}`; other members are rejected | `200 {"mode":"authenticated"}` and an HttpOnly, SameSite=Strict session cookie (Secure over HTTPS) |
+| `POST /console/auth/logout` | No credential payload | `200 {"mode":"login"}`; clears the cookie and the server-side session |
+| `GET /console/config` | Signed-in session | `node_installer`, `node_installer_sha256`, `self_hosted_installer`, `self_hosted_installer_sha256`, and `node_artifacts`: the providers (`docker`, `microsandbox`) whose node assets this console holds |
+
+Sign-in errors use the console's `{"error": "…"}` envelope: 400 for a malformed
+body, 401 for a wrong key, 415 for a non-JSON body, 429 with `Retry-After` when
+failed attempts are limited or sign-in is busy, and 503 when the console cannot
+start a session. The console compares the submitted key with its configured Core
+key in constant time and never logs or returns it. Only failed attempts count
+toward the limit; the correct key signs in even while failures are limited. The
+console refuses to start with a Core key shorter than 32 characters. Sessions live only in the console's memory; a console restart
+or Core key rotation requires signing in again. There are no console accounts,
+usernames, passwords, account setup or Basic authentication.
+
+Use same-origin browser requests and cookies. Mutations require the same-origin
+request checks; never put the Core key in JavaScript or browser storage.
+
+## Console to Core
+
+After sign-in and the same-origin checks, the console server forwards every
+`/core/v1/*` request with its private Core key as the Bearer credential; Core
+alone decides whether the route exists. It removes browser Authorization and
+forwarding-sensitive headers, and sets `X-Core-Console-Actor: console`. Core
+records the header as the audit `actor_label`. The label is caller-asserted and
+display-only: direct Core key scripts normally send none, which records an empty
+label, but could set any value. Never use it for authorization or as proof of
+origin.
+
+Web calls Core only through the typed Core clients in `packages/agents-client`:
+[AdminClient](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/packages/agents-client/src/admin-client.ts) (`/core/v1`),
+`SandboxAdminClient` (`/core/v1/sandbox`) and `CoreMetricsClient`
+(`/core/v1/metrics`). See [the complete administrator reference](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md)
+for methods, fields, filters, pagination and response shapes.
+Routes below are relative to `/core/v1`:
+
+| Workflow | Routes |
+| --- | --- |
+| Projects | `GET/POST /projects`, `POST /projects/{id}`, `POST /projects/{id}/archive` |
+| Project keys | `GET/POST /projects/{id}/keys`, `DELETE /projects/{id}/keys/{key_id}` |
+| Resource lists/details/deletion | `/projects/{id}/agents`, `/sessions`, `/environment-templates`, `/skills`, `/files`, `/vaults`, including the documented nested reads |
+| Asset ownership | `GET /projects/{id}/resource-owners` with batched resource IDs |
+| Key operation history | `GET /projects/{id}/write-operations` with key/resource/time filters |
+| Executor credentials | `GET/POST /projects/{id}/environments/{environment_id}/executor-credentials`, `DELETE …/executor-credentials/{key_id}` ([contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-executor-credentials.md)) |
+| Deployment model providers | `GET /harnesses`, `GET/PUT/DELETE /harnesses/{harness}/model-provider`; the key is write-only ([contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/model-execution.md#deployment-defaults)) |
+| Usage and health | `GET /summary`, `/sandbox/runtime-observations`, `/metrics` |
+| Administrator audit | `GET /audit-log`; deployment-wide entries have `project_id: null` |
+
+A Project UUID in a management path selects the target; it is not a credential.
+API-key plaintext is returned only by successful issuance, so display it once and
+never cache it. Reconcile uncertain issuance before explicitly issuing another key.
+Deleted/revoked resources retain their audit records. Historical unknown ownership
+stays null. Session usage grouped by key belongs to the Session's creation key;
+it is operational attribution, not per-key billing.
+
+## Sandbox administration
+
+The [deployment configuration contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/sandbox-deployment.md),
+[nodes and sandbox backends reference](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md)
+and [generated OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core.openapi.yaml)
+define deployment and node operations:
+
+- `GET/POST/PUT /core/v1/sandbox/deployment` and
+ `PATCH /core/v1/sandbox/deployment/maintenance`.
+- `GET /core/v1/sandbox/nodes`, `PATCH/DELETE /core/v1/sandbox/nodes/{node_id}`,
+ and `GET /core/v1/sandbox/nodes/{node_id}/allocations`.
+- `POST /core/v1/sandbox/enrollment-tokens` for a one-time node installation command.
+ Its non-secret `enrollment_id` reappears on the node that command registers.
+
+PostgreSQL owns one provider, per-sandbox resource specification and immutable
+Runtime selection. POST initializes it; PUT replaces the complete selection using
+`expected_generation`. Requests carry `resources` and, for Docker/microsandbox,
+`runtime`; safe responses return `specification` and `specification_digest`.
+Response `resources.allocations` and `resources.pending` are cleanup counts, not
+CPU, memory or disk settings. E2B accepts a write-only key and exact template build
+instead of a node Runtime release, and provisions without a node installation. E2B
+may omit `resources` to adopt the validated build's CPU and memory; responses show
+the build as read at selection time in `e2b.template_build`. Microsandbox responses
+return its idle `suspension` policy; other providers return null.
+
+Provider, resource and Runtime changes all require global maintenance and verified
+cleanup of retained/pending resources. Core validates the candidate before commit;
+a rejection preserves the previous configuration. A changed commit advances the
+generation and retires old nodes and enrollment tokens atomically. Explicitly resume
+after success. Neither switching nor editing configuration deletes resources or
+migrates existing Sessions. During maintenance, administrators can explicitly
+[archive each retained hosted Session](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md#administrative-session-archive)
+through the Core administrator API at the current generation, then read its
+resource disposition and recheck deployment counts. Archive preserves history
+and persisted Files/Artifacts; unpersisted workspace contents are lost and the
+original Session cannot resume. This API does not add a console archive control.
+Public Environment Templates, the `/v1` contract and
+caller-owned `self_hosted` provisioning remain unchanged.
+
+`GET /api/v1/sandbox-node/configuration` uses an enrollment Bearer token, or a
+retained node Bearer credential with `X-OAC-Node-ID`. This read does not consume
+enrollment. Retained matching nodes can read their configuration during maintenance.
+Installers must verify the returned generation, specification digest and Runtime
+before registration; local files cannot override the saved limits. A mismatch
+returns `sandbox_specification_mismatch` without replacing node state.
+
+Node configuration, enrollment, identity and connection routes live under
+`/api/v1/sandbox-node`, beside the daemon's `/api/v1/agent-daemon`. The reverse
+proxy sends `/api/v1` directly to Core; the console returns 404 for it and never
+forwards machine traffic. These routes use their own credentials, do not inherit a
+browser login and gain no management authority. E2B credentials are absent
+from node configuration and safe deployment views.
+
+## Frontend handoff and errors
+
+Frontend screen implementation is owned by the separate frontend task. The
+backend provides the `/core/v1` contract, the typed Core clients and the console
+proxy; backend tests do not qualify the screens.
+
+The console returns 404 for `/v1`, `/api/v1` and old `/console/api-keys` routes,
+even with an explicit application or machine Bearer credential. Invalid
+Origin/Host requests are rejected; unavailable Core or rejected upstream redirects
+return 502. Console authentication uses its own error envelope. Management resource
+errors and deletion constraints are documented in the administrator reference and
+generated schema; do not interpret every empty or failed read as an absent resource.
+
+## Core metrics
+
+`GET /core/v1/metrics?range=1h|6h|24h|7d` returns Core process, execution
+queue/slots, PostgreSQL and background-job measurements. It requires the Core key,
+rejects arbitrary query filters and never grants
+Agent execution access. See the [exact measurement contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core-metrics.md)
+for complete buckets, null values, units and process-local retention. Frontend
+implementation is maintained separately; this backend change does not modify
+Agent metrics or the public Agent API.
+
+## Node host history
+
+The Core key reads a node and its host history through
+`GET /core/v1/sandbox/nodes/{node_id}?range=1h|6h|24h`. See the
+[node host history contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/node-host-history.md)
+for nullable observations, freshness and aggregation. The node list is unchanged.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/api/web-management.md)
diff --git a/apps/docs/content/docs/admin-api.zh.mdx b/apps/docs/content/docs/admin-api.zh.mdx
new file mode 100644
index 000000000..a35483936
--- /dev/null
+++ b/apps/docs/content/docs/admin-api.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Core administration API · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+管理员脚本通过 Core 的回环端口调用 /core/v1,并使用 Core key。公网入口的 /core/v1 由 Web 管理会话处理;不能把它当成应用 API 或机器连接的代理。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/admin-api)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/agents-and-tools.mdx b/apps/docs/content/docs/agents-and-tools.mdx
new file mode 100644
index 000000000..ed238409b
--- /dev/null
+++ b/apps/docs/content/docs/agents-and-tools.mdx
@@ -0,0 +1,124 @@
+---
+title: "Agents, tools and harnesses"
+description: "Native harness tools, shared declarations and the limits of execution support."
+---
+
+Assessed against Core `206474a4c10901442b1faa094281bfb5559082b5` on 2026-09-22.
+The contract remains [OpenAI Python 3.13.0 at `d7c41ef`](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream.json), Beta
+`agents=v1`. This matrix records the bounded execution/tools milestone. It does not
+establish complete protocol compatibility; the remaining limits below stay open.
+
+**Implemented** means current source and controlled checks support the operation.
+**Qualified** additionally identifies a real public Core/PostgreSQL/daemon/native
+model workflow in the evidence register below. **Unsupported** describes a current
+admission/native limitation, not a restriction in the official contract.
+**Unverified** means evidence does not establish the stated semantics or profile.
+Qualification never extends automatically to another model, placement or combination.
+
+## Operation matrix
+
+All function rows refer to application-defined functions. Native workspace tools
+and Environment Plugin MCP have separate inventories and qualification.
+
+| Operation | Implemented behavior and real qualification | Unsupported or unverified boundary |
+| --- | --- | --- |
+| Initial message input, `sessions.create` | String input and ordered user-message arrays share atomic admission. Codex/Claude text and inline image execution: M1/M2; ordinary MiniMax text: M1/P1. [Input contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/message-input.md), [initial parser](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/api/session_initial_input.go). | Whitespace-only text is admitted verbatim for Codex and rejected at admission for Claude SDK and MiniMax Code ([text content](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/message-input.md#text-content)). Empty parts beside text (SES-08) and local size-limit parity need upstream evidence. Images are only qualified for inline PNG/JPEG on `none` and Core-managed Docker `openai_hosted`; MiniMax, self-hosted images and remote URLs remain gaps. |
+| Prepared and active messages, `sessions.events.create` | Ordered `input_text`/`input_image` arrays retain original content and distinct public user Items. HTTP 202 confirms persistence; native receipts establish application. Initial/prepared/active Docker PNG/JPEG: M2; active PNG on `none`: M1. [Shared admission](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/api/inputs.go). | Codex flattens native messages with blank-line separators. Claude can fold or queue native turns; it does not promise Codex's same-native-turn behavior. Public durability does not prove native consumption. Claude SDK and MiniMax Code reject messages without an image or non-whitespace text at admission (`unsupported_or_invalid_configuration`); Codex delivers them unchanged. |
+| Structured output, `agent.text.format` | Save/inherit/freeze `{type:json_schema,schema:...}`. Claude SDK object-root, single Agent, medium verbosity, ordinary functions returning text: S1 (`none`) and S2 (Docker, including prepared/active input and Files/Artifacts). Native final text is retained unchanged; its stream follows the official message sequence with the whole text in one `output_text.delta` ([item serialization](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/history-events-usage.md#item-serialization-2026-09-23)). [Contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/structured-output.md), [profile](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/engine/claude.go). | An explicit non-object root type is a protocol error for every harness ([validation](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/official-semantics-alignment.md#agent-configuration-validation--september-23)). Codex/MiniMax, schemas without an object root, schema numbers changed by binary64, self-hosted, Skills/Plugins, MCP, Subagent and discovery combinations reject execution. No output repair, coercion or extra model loop. Arbitrary schema dialects are unverified. |
+| Function configuration, saved/inline Agents | Required name/description/schema; `defer_loading` defaults false. Protocol errors, repeated names and explicit non-object root types reject with the official fields ([validation](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/official-semantics-alignment.md#agent-configuration-validation--september-23)). Saved references resolve into an immutable Session snapshot. Codex/Claude real calls: F1/F2/M2/S1/S2. [Parser](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/api/function_configuration.go), [saved tools](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/api/saved_tools.go). | MiniMax public functions reject. Claude requires an explicit object root. Local nonblank/512-byte name and 64-definition Session bounds are compatibility gaps. Saving configuration alone does not qualify execution. |
+| Function-result admission, `events.create` | Required `turn_id`, `call_id`, `success`; optional nullable `error` and `output`. Output is string or ordered text/image content. Scoped atomic batches retain field presence, original content and retry identity in storage; public result Items and events always carry `output` and `error`, null when not submitted ([item serialization](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/history-events-usage.md#item-serialization-2026-09-23)). Same result retries are accepted; changed results and results after cancellation return 409 `conflict_error`, including after terminal state. In the caller's Session, an unknown call or a call of another Turn returns 400 `invalid_request_error` without changing the pending action; missing and foreign Sessions stay 404 ([error rows](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/official-semantics-alignment.md#session-input-conflicts-and-result-targets--september-23)). F1/F2/M2 plus [controlled SDK/raw checks](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/tests/official_function_inputs.py). [Parser](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/api/function_inputs.go), [Store](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/store/function_inputs.go). | Admission is separate from application and public Item publication. Invalid or unqualified content cannot consume a pending call. Core messages omit the call and executor IDs that official messages name; hosted defaults and publication timing remain unverified. |
+| Function text results and native application | Codex waits for a matching live root dynamic-tool completion; Claude waits for a matching live root native tool result. Text/error results, retry/conflict, cancellation and cold continuation: F1/F2. [Receipt contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/function-result-images.md). | Transport writes alone do not confirm application. Confirmation is not provider consumption, crash recovery or exactly-once external effects. No automatic result replay. |
+| Function image results | Successful ordered inline PNG/JPEG with text, large PNG and image-only JPEG: Codex/Claude `none` F1/F2; Docker M2. Public Items retain submitted bytes. Codex receipt regression: F2. | Claude rejects failed images and remote references before persistence; native resizing may change its image bytes. MiniMax functions and self-hosted image workflows are unqualified. Other Codex image/error/reference combinations cannot be inferred from successful-inline evidence. |
+| Deferred function discovery | Type-only `tool_search` plus mixed eager/deferred functions: Claude SDK 0.3.269/native 2.1.269, Kimi K3, single Agent, medium, `none`, text results; text and PNG input D1. Native provider observations establish lazy schema loading. [Contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/tool-search.md). | A repeated `tool_search` is a protocol error. Codex/MiniMax discovery, search-only/missing-search, workspace, MCP, structured-output and Subagent combinations remain gaps. Opaque native policy changes lack a reliable pre-input deferral signal. Saved tools include `tool_search`; the pinned Session response union excludes it. Exact hosted projection is unverified. |
+| Explicit disabled search/PTC | Saved and inline `web_search.mode=disabled` and `programmatic_tool_calling.enabled=false`; shared native controls on initial and cold execution. All three harnesses on `none`: P1, including native inventory/control evidence. [Contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/tool-policy.md), [parser](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/api/disabled_tools.go). | Unsupported explicit enablement rejects at Session admission; saved Agents keep every pinned search mode as resource data (TV-05), and Sessions from such Agents reject unless they replace the tools. A repeated `web_search` is a protocol error. Omission retains approved native behavior, which does not establish official default-on PTC parity. Enabled search and default/error parity remain gaps; unrelated native utilities are not implicitly removed. |
+| Agent service-origin MCP | Implemented Codex/Claude HTTP `none` profiles, anonymous/static bearer, scoped Vault selection and native `mcp_call` Items. [Configuration and qualification limits](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/README.md#http-mcp-execution), [profiles](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/engine/profile.go). | This closure batch does not requalify MCP/provider combinations. MiniMax, self-hosted/hosted service-origin MCP, OAuth, nonempty inline headers/metadata and other transports/origins reject. Claude requires a static connected inventory; original MCP-envelope fidelity and continuing server health are unverified. |
+| Environment Plugin MCP/native tools | Separate [Docker Plugin transport matrix](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-templates.md#environment-origin-mcp-plugins) and [V1 deployment evidence](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/user-managed-runtime-v1.md). Native tools stay within the existing colocated Runtime. | Plugin MCP is not Agent service-origin MCP or a public function action. No new optional cross-product is qualified here. Native utility inventories need not be identical. |
+| Required action: `function_call` | Session GET/list and Session SSE expose persisted `{arguments,call_id,name,turn_id,type}`. Actions remain pending until native application or cancellation/terminal settlement. [Projection](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/store/function_state.go), [confirmation](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/store/function_results.go); real handling F1/F2/M2/S1/S2/D1; explicit client disconnect/query/reconnect: F3. | Function-call history is not pending-state authority. Client reconnect does not replay events or redo external application effects. |
+| Required action: `environment_connection` | Offline waiting input exposes `{environment_id,type}` before Turn creation. Connect the exact Session Environment through our scoped daemon enrollment. Three-harness Docker/E2B execution: E1; explicit initial pending-input/query/connection/native completion: E2; expiry: [controlled initial-input test](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/tests/official_self_hosted_initial.py). | Idle offline Sessions without pending input request nothing. Registration alone is not connectivity or native readiness. Stock `exec-server`/Noise transport is outside the approved V1 route. Exact upstream registration/action-removal timing remains unverified. |
+| Stream disconnect and execution loss | SSE is live-only. Reconnect, buffer events, retrieve Session/Turn/Items and deduplicate Item IDs. Worker recovery fails previously claimed work without replay; queued work can remain. [Recovery contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md#public-execution-admission), [connection reconciliation](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/store/environment_connection_recovery.go). | Client disconnect and daemon/Core loss are different cases. F3 qualifies client-stream loss and continued pending function handling; F2 qualifies native process loss with an unapplied saved result. Neither promises replay or recovery of unknown external effects. |
+
+## Required-action recovery contract
+
+The fixed [Session union](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/types/beta/agent_session.py)
+defines both variants. The official [Functions](https://developers.openai.com/api/docs/guides/agents-api/tools/functions)
+and [Manage sessions](https://developers.openai.com/api/docs/guides/agents-api/sessions/manage)
+guides, checked on 2026-09-22, direct clients to retrieve `required_actions` after
+restart or stream loss. A historical `function_call` Item alone cannot establish
+that a result is still pending. The fixed SDK governs field/type compatibility;
+current prose does not silently change that pin.
+
+For a pending function, use the returned Session, Turn and call identity. If the
+application already performed the function, keep and submit that saved result;
+do not run an external effect again merely because an action remains visible.
+Core's acknowledgement boundary is native application. For an environment action,
+connect its exact Environment using the existing enrollment authorization.
+[Connection observations](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/store/environment_connections.go)
+fence stale generations; transport connection can clear pre-Turn activity before
+native preparation. Neither registration nor an action disappearing proves that
+a model ran successfully. Query the matching Turn and Items for its outcome.
+
+Two timing questions remain open. Repeated `requires_action` notification order
+and acknowledgement timing are local implementation choices, not proven upstream
+semantics. Also, an admitted result that is cancelled before native observation
+can remain internally saved without a public output Item or `item.added`.
+[Item publication](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/internal/store/item_projection.go) and
+[the existing coverage record](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md#public-function-configuration) preserve
+this gap. Successful retry cannot manufacture the missing observation or establish
+that the model consumed the result.
+
+## Evidence register
+
+Real records below use fixed SDK 3.13.0 and raw HTTP through Core, dedicated
+PostgreSQL, daemon and native harness. Evidence is private under `~/.parsar/remediation/` on `zju_a100_2` and the
+development host. Paths containing `validated/` or `server-evidence/` identify
+downloaded local mirrors; summary files may also live only on the development
+host. Locators identify retained evidence without publishing credentials or
+private launch configuration. Revisions identify the accepted change,
+not a claim that every historical workflow was rerun at the current baseline.
+
+| ID | Exact evidence and accepted scope |
+| --- | --- |
+| M1 | `20260922/message-image-input/public-codex-reviewed.log` (59.41s), `public-claude-reviewed.log` (79.52s), `public-mcode-network-fixed.log` (48.95s); [PR #12](https://github.com/MiniMax-AI/parsar-core/pull/12), merge `37a2923`. Codex/Claude Kimi K3 PNG initial/active inputs, receipts, retries, cancellation, cold continuation and isolation; MiniMax M2.7 ordinary text regression. |
+| M2 | `20260922/workspace-images/validation-summary.json`; Codex `public-run-_lr5uiy_`, Claude `public-run-sb65p9e9`; candidate `2c295116d66ed9aafc808ec49a8cb6dcb5c674c2`, merge `1adb2489bc3415039440224e9a7905c53e68a557` ([#17](https://github.com/MiniMax-AI/parsar-core/pull/17)). Kimi K3, Codex 0.153.4, Claude SDK 0.3.269/native 2.1.269; Docker seven-Turn image/workspace workflow. Codex's receipt conclusion is superseded by F2. |
+| F1 | `20260922/function-result-images/validation-summary.json`, `validated/function-image-public-2567456666/public.json`; candidate `d3cdb225bc7628b968b76c7e1b400101894ce8cd`, merge `fd23f169e1ede1b2f1c39a1d9dcce71886e3c006` ([#16](https://github.com/MiniMax-AI/parsar-core/pull/16)). Claude SDK 0.3.269/native 2.1.269 with Kimi K3 on `none`; success PNG/JPEG, failed text, retries, cancellation, history continuation. Earlier Codex transport-only evidence does not qualify current receipts. |
+| F2 | `20260922/function-result-receipts/validation-summary.json`, `candidate-public-owner-sdk.log`; hosted `public-run-4fn70foy` (104.13s), `none` `function-image-public-945075091` (83.918s). Candidate `6dceb98e1c56469fcc70cd82025ce77fc870ed3d`, merge `206474a4c10901442b1faa094281bfb5559082b5` ([#20](https://github.com/MiniMax-AI/parsar-core/pull/20)); real Kimi, Codex 0.153.4. Qualifies native receipts, not completed provider consumption. |
+| F3 | `20260922/execution-tools-closure/pending-actions/validation-summary.json`; Codex `codex/public-run-8gexf5a8` (65.58s), Claude `claude_sdk/public-run-5f2tr7e_` (74.33s), production source `206474a`. The same [public verifier](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/tests/official_pending_actions_native.py) checks disconnected-client pending queries, success/error/cancel, original results, target isolation, retry/conflict and no implicit call replay with real Kimi on Docker. No Core restart or native-loss injection is claimed by these runs. |
+| S1 | `20260922/structured-output/acceptance-summary.json`, `structured-public-1905281777/public.json`; real candidate `7f533d46c472f3cdf7c16ce9471c225a2ba7eded`, merge `8716e699dbc34904499c94b6b0b03857bbebfaad` ([#11](https://github.com/MiniMax-AI/parsar-core/pull/11)). Claude/Kimi `none` schema/function execution and cold continuation. Schema-specific active steering was not separately qualified here. |
+| S2 | `20260922/hosted-structured-output/validation-summary.json`, `validated/public-inline.log`, `public-run-qvq3csf1` (186.09s); candidate `f6d2c3f2a1dc2ff5f177213bd2b20d78496011d0`, merge `aa85d8ecc60e0a8b7f2494b73caa81216ee197e2` ([#18](https://github.com/MiniMax-AI/parsar-core/pull/18)). Claude SDK 0.3.269/native 2.1.269, real Kimi; Docker saved/inline schema, prepared/active input, native files and four completed structured answers. |
+| D1 | `20260922/deferred-tools/tool-search-public-2039155387/public.json`, `public-image-isolated-tests.log` (86.71s); merge `178507ae7a63d4068e1e82bef9cd256ba398ae00` ([#13](https://github.com/MiniMax-AI/parsar-core/pull/13)). Claude SDK 0.3.269/native 2.1.269/Kimi K3 `none`; text results with initial/active PNGs, cancellation and cold continuation. `claude-native-1790052750` separately records provider schema inventories and native feasibility. |
+| P1 | `20260922/tool-policy/acceptance.json`, `server-evidence/tool-policy-{codex-2495445746,claude_sdk-2539346823,mcode-3747575401}/public.json`; candidate `eeb432c7495265ae3a9309e32f5b4323611c3245`, merge `4b8754a6eda6696d9b18eb8d583953ac16c6800f` ([#14](https://github.com/MiniMax-AI/parsar-core/pull/14)). Codex/Claude Kimi K3, MiniMax M2.7; four `none` Sessions per harness, native disable controls and cold continuation. |
+| E1 | `20260921/self-hosted-onboarding/{source-candidate.json,source-verified.json,live,live-e2b}`; candidate `8f0cd2530d7b58cb7fb3ea124a1fc43dacecca36`. [Exact Docker/E2B run paths and limits](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/user-managed-runtime-v1.md#evidence-and-verification-boundaries): Codex Kimi K3 Responses, Claude Kimi K3 Anthropic-compatible API, MiniMax M2.7, immutable Linux amd64 Runtime builds. Native execution, restart/history, cancellation, Files/Artifacts and isolation; shared credential lifecycle evidence is not a separate live rotation run for every E2B profile. |
+| E2 | `20260922/execution-tools-closure/environment-actions/result.json` contains Codex/Claude Kimi passes (13 checks each) and the retained MiniMax direct-network failure; `mcode-relay-retry/result.json` separately passes MiniMax M2.7 (13 checks) after correcting provider routing. Source `206474a`; same shared SDK/raw workflow, isolated Core/PostgreSQL and user-managed Docker. Covers initial creation SSE, client disconnect, exact pending Environment/no Turn or Items, scoped enrollment, one actual completed model Turn and creation retries preserving history. This is connection-action qualification, not another full lifecycle or E2B regression. |
+
+Controlled tests establish parser, projection, atomicity and race behavior; native
+feasibility probes establish only feasibility. Neither replaces the real records.
+Failed setup, provider and assertion runs remain failures with their original
+scope. The unchanged gate requirements and independent review apply to this batch.
+
+## Milestone closure and remaining limits
+
+1. **Principal function recovery:** F3 directly verifies live client-stream loss
+ while a function is pending on Codex and Claude, authoritative queries, exact
+ targeting, rejection without mutation, original results, retry/conflict,
+ application, cancellation and isolation. F2 separately establishes truthful
+ native-loss settlement without replay. No active execution recovery is promised.
+2. **Principal environment recovery:** E2 explicitly verifies pending
+ `environment_connection` recovery and single initial execution on all three
+ harnesses. E1 retains broader deployment/history/isolation qualification.
+ Registration, connection and preparation remain distinct; no exact upstream
+ timing or arbitrary-provider qualification is inferred.
+3. **Recorded nonblocking timing:** an accepted result cancelled before native
+ application may lack a public result Item. The submitted data remains durable;
+ F2 confirms that native loss does not turn unknown delivery into Applied. The
+ fixed sources do not establish the publication point for this interleaving.
+ Keep it queued rather than inventing a public state or rewriting safe receipt
+ ownership. This limited timing question is not accepted protocol parity and
+ does not invalidate F3's principal pending-result workflow.
+
+Approved native capability differences (including default PTC), unsupported optional
+combinations, arbitrary provider parity and low-frequency error/default edge cases
+remain separate limitations. They do not authorize silently relabeling an unproved
+principal workflow as deferred. OAuth, full Usage, release publication, new tool
+kinds and new execution loops are outside this closure batch.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/execution-tools.md)
diff --git a/apps/docs/content/docs/agents-and-tools.zh.mdx b/apps/docs/content/docs/agents-and-tools.zh.mdx
new file mode 100644
index 000000000..5ed1f6f95
--- /dev/null
+++ b/apps/docs/content/docs/agents-and-tools.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Agents, tools and harnesses · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+保存 Agent 只保存配置,不启动执行。工具声明由所选原生 harness 执行;Codex、Claude Code 与 MiniMax Code 的能力和限制不同。x_agents_core 保留原有扩展字段名称。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/agents-and-tools)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/api-reference/agents.mdx b/apps/docs/content/docs/api-reference/agents.mdx
new file mode 100644
index 000000000..25bd137d4
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/agents.mdx
@@ -0,0 +1,75 @@
+---
+title: Agents
+description: >-
+ Agents. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists only the authenticated tenant's saved Agents, independently of
+ Sessions. Limit 0 is treated as 1 and larger limits as 100, as
+ observed on the hosted service. The local default is 20; exact
+ upstream default/cap and empty cursor fields remain unverified. An
+ unknown, malformed or foreign after cursor returns not found.
+ - content: >-
+ Persists configuration independently of execution. Names over 128
+ characters and metadata outside 16 string pairs with 64-character keys
+ and 512-character values return invalid_request_error with the
+ official param; U+0000 in stored strings is rejected as a local
+ storage limit. As on every Agents API JSON route, a non-JSON
+ Content-Type, invalid UTF-8, malformed JSON, a repeated key at any
+ depth or a non-object root returns invalid_request_error with a null
+ param and the official message before other checks; an empty or null
+ body is {}. Missing, unknown, wrongly typed or unsupported enum
+ members of the pinned configuration shapes (tools, text, reasoning,
+ service_tier, multi_agent) return invalid_request_error with the JSON
+ path as param; duplicate function names, repeated web_search or
+ tool_search and non-object schema root types return it with a null
+ param. Supports model/name/instructions/metadata, explicit reasoning
+ and service tiers, multi_agent, text/json_schema,
+ function/tool_search/programmatic_tool_calling/web_search and HTTP MCP
+ with nullable credential_id, service origin (omitted or null on HTTP
+ transport is saved as service) and boolean required defaulting to
+ false. Saving credential_id grants no access: Session admission checks
+ attached Vault ownership and destination. MCP allowed_tools preserves
+ null versus empty; saved HTTP transport includes empty headers.
+ Model-derived reasoning defaults, other MCP variants and public retry
+ conformance remain incomplete. web_search saves every pinned mode:
+ omitted or null mode is saved as live and omitted or null context_size
+ as medium; allowed_domains preserves null versus empty and a present
+ location, including {}, includes all four keys with null for omitted
+ ones, as observed officially (req_db41d2f6261b4abfb69465eafe719ab5,
+ req_165d53b88445490b9146d8272c54134d). Session execution accepts only
+ explicit disabled web_search and disabled programmatic_tool_calling
+ through qualified Runtime controls; saved enabled forms reject at
+ Session admission. Session execution admits only its supported
+ configuration subset.
+ - content: >-
+ Reads the saved resource owned by the authenticated tenant,
+ independently of execution Sessions.
+ - content: >-
+ Preserves omitted fields and replaces supplied fields using shared
+ saved-configuration validation. Null name/instructions clear; null or
+ empty metadata clears all pairs. Name, metadata and configuration
+ validation errors return invalid_request_error with the official
+ param, using the Agent create rules before the Agent lookup. Existing
+ Session snapshots are unchanged. Empty updates advance updated_at
+ without changing saved fields. Nested replacement/null defaults,
+ model-derived reasoning and exact hosted error behavior remain
+ incompletely verified.
+ - content: >-
+ Deletes only the authenticated tenant's saved configuration. Existing
+ Session snapshots, history and recorded creation retry identities
+ remain independent. Missing and repeated deletion locally return404;
+ exact hosted error and in-flight creation/deletion semantics remain
+ unverified.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/artifacts.mdx b/apps/docs/content/docs/api-reference/artifacts.mdx
new file mode 100644
index 000000000..012154e5f
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/artifacts.mdx
@@ -0,0 +1,33 @@
+---
+title: Artifacts
+description: >-
+ Artifacts. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists published outputs independently of Environment availability.
+ Sorting uses publication time and ID. A later Turn publishes a path
+ again only when it is new, its bytes changed, or no Artifact remains
+ for it. A malformed environment_id matches nothing. An after value
+ that is not an Artifact of this Session, including a malformed one,
+ returns 400 invalid_request_error with the message "after is not a
+ valid artifact ID". The local default page size is 20; exact upstream
+ defaults remain unverified.
+ - content: >-
+ Deletes the published copy without modifying its original workspace
+ file. Already admitted content reads may finish; later reads reject.
+ - content: >-
+ Streams stored bytes after tenant and Session authorization, including
+ after Environment expiration. Exact upstream headers and Range
+ behavior remain unverified.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/administrator-projects.mdx b/apps/docs/content/docs/api-reference/core/administrator-projects.mdx
new file mode 100644
index 000000000..81b0dd3ca
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/administrator-projects.mdx
@@ -0,0 +1,17 @@
+---
+title: Administrator Projects
+description: >-
+ Administrator Projects. Core administration API: Core key held by Web’s server
+ or an operator script. Generated local management contract. This is not part
+ of the public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents: []
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/agents.mdx b/apps/docs/content/docs/api-reference/core/agents.mdx
new file mode 100644
index 000000000..3efbe8afb
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/agents.mdx
@@ -0,0 +1,29 @@
+---
+title: Agents
+description: >-
+ Agents. Core administration API: Core key held by Web’s server or an operator
+ script. Generated local management contract. This is not part of the public
+ OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/artifacts.mdx b/apps/docs/content/docs/api-reference/core/artifacts.mdx
new file mode 100644
index 000000000..94f4020b7
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/artifacts.mdx
@@ -0,0 +1,33 @@
+---
+title: Artifacts
+description: >-
+ Artifacts. Core administration API: Core key held by Web’s server or an
+ operator script. Generated local management contract. This is not part of the
+ public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/core-administration.mdx b/apps/docs/content/docs/api-reference/core/core-administration.mdx
new file mode 100644
index 000000000..af6d73f49
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/core-administration.mdx
@@ -0,0 +1,56 @@
+---
+title: Core Administration
+description: >-
+ Core Administration. Core administration API: Core key held by Web’s server or
+ an operator script. Generated local management contract. This is not part of
+ the public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Newest-first cursor pagination of safe metadata. Actor
+ labels are unverified console labels, not authorization identities.
+ Request bodies and secrets are never recorded.
+ - content: >-
+ Core key only; available before any sandbox deployment exists. Reports
+ the public URL that applications, nodes, sandboxes and self-hosted
+ executors use, the API base URL, Core's source commit and installation
+ ID, the installer's settings snapshot with where to change it, and
+ what is bound to the current public URL. Sensitive settings report
+ only whether they are configured.
+ - content: >-
+ Core key only. Complete UTC buckets; unknown measurements are null.
+ Samples are process-local and are not backfilled after a restart.
+ - content: >-
+ Core key only. Reports actual resource disposition, including expiry
+ and failure cleanup. This is not archive provenance and does not
+ assert active Turn settlement. Read this after an uncertain archive
+ response; never infer released from a missing sandbox alone.
+ - content: >-
+ Core key only. Requires maintenance and the current deployment
+ generation. Permanently closes execution, requests cancellation and
+ releases sandbox/snapshots through existing cleanup. Session history
+ and persisted files/artifacts remain; unpersisted workspace contents
+ are lost. A cleanup_pending response is not proof of resource release.
+ Does not affect caller-managed Runtime.
+ - content: >-
+ Core key only. Each observation is labelled with its owning Project
+ ID. Uses the existing read-only Runtime sampler, with bounded
+ concurrency and no execution or provisioning. A provider with a batch
+ metrics read, such as E2B, samples the page's running sandboxes in one
+ bounded request.
+ - content: >-
+ Core key only. after/limit/order paginate Projects. Agent grouping
+ returns groups within those spaces. Date bounds filter Session
+ creation, not current asset counts. Usage sums only non-null public
+ Session usage; coverage includes every selected Session. Each Project
+ is read in a consistent database snapshot. Totals are not billing
+ records.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/credentials.mdx b/apps/docs/content/docs/api-reference/core/credentials.mdx
new file mode 100644
index 000000000..f28389c4e
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/credentials.mdx
@@ -0,0 +1,29 @@
+---
+title: Credentials
+description: >-
+ Credentials. Core administration API: Core key held by Web’s server or an
+ operator script. Generated local management contract. This is not part of the
+ public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/deployment-model-providers.mdx b/apps/docs/content/docs/api-reference/core/deployment-model-providers.mdx
new file mode 100644
index 000000000..5f8f685ff
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/deployment-model-providers.mdx
@@ -0,0 +1,43 @@
+---
+title: Deployment Model Providers
+description: >-
+ Deployment Model Providers. Core administration API: Core key held by Web’s
+ server or an operator script. Generated local management contract. This is not
+ part of the public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Returns every harness this build supports, in name
+ order. enabled and default are read-only views of the process
+ configuration (OAC_DEFAULT_HARNESS and OAC_HARNESSES). model_provider
+ is the harness's deployment default, stored in Core, or null. Keys are
+ never returned; api_key_configured reports that one is set.
+ - content: >-
+ Core key only. Returns the safe view; the key is never returned. 404
+ when the harness does not exist or has no deployment default.
+ - content: >-
+ Core key only. Idempotent; each successful request is audited.
+ Sessions that already froze the default keep it. Afterwards new
+ openai_hosted Sessions for this harness need a Session or Agent
+ bundle.
+ - content: >-
+ Core key only. The body is the complete x_agents_core.model_provider
+ bundle, including the write-only api_key; there is no partial update
+ and bundles are never merged. The provider is validated for this
+ harness: an HTTPS base_url without credentials, query or fragment, the
+ harness's protocol (responses for codex, anthropic for claude_sdk and
+ mcode) and, for mcode, positive context_window and max_output_tokens.
+ New openai_hosted and none Sessions that resolve no Session or Agent
+ bundle freeze this default into their encrypted snapshot; existing
+ Sessions never change. self_hosted Sessions never use it. The key is
+ encrypted and never returned. Each write records an administrator
+ audit entry without the key.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/environment-templates.mdx b/apps/docs/content/docs/api-reference/core/environment-templates.mdx
new file mode 100644
index 000000000..1f2754d5f
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/environment-templates.mdx
@@ -0,0 +1,29 @@
+---
+title: Environment Templates
+description: >-
+ Environment Templates. Core administration API: Core key held by Web’s server
+ or an operator script. Generated local management contract. This is not part
+ of the public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/execution-configuration.mdx b/apps/docs/content/docs/api-reference/core/execution-configuration.mdx
new file mode 100644
index 000000000..207d504b0
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/execution-configuration.mdx
@@ -0,0 +1,27 @@
+---
+title: Execution configuration
+description: >-
+ Execution configuration. Core administration API: Core key held by Web’s
+ server or an operator script. Generated local management contract. This is not
+ part of the public OpenAI API.
+full: true
+_openapi:
+ method: GET
+ route: /core/v1/projects/{project_id}/sessions/{session_id}/execution-configuration
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only; the Project ID selects the target space and does not
+ authenticate. Returns the committed model, harness and safe provider
+ selection with recorded sources. This read never decrypts credentials,
+ resolves current defaults or probes execution health. Deployment
+ defaults frozen after they moved into Core show their safe view; older
+ deployment selections remain redacted. Historical provenance and
+ missing provider projections are explicitly unknown/unavailable.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/executor-credentials.mdx b/apps/docs/content/docs/api-reference/core/executor-credentials.mdx
new file mode 100644
index 000000000..326c5bed2
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/executor-credentials.mdx
@@ -0,0 +1,40 @@
+---
+title: Executor Credentials
+description: >-
+ Executor Credentials. Core administration API: Core key held by Web’s server
+ or an operator script. Generated local management contract. This is not part
+ of the public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Returns metadata of the credentials restricted to this
+ Environment, oldest first; secrets are never listed. The Environment
+ must be a self_hosted Environment of the Project whose Session exists;
+ otherwise 404.
+ - content: >-
+ Core key only. Returns a connect-only secret once, restricted to
+ daemon enrollment and connection for this Environment, with the
+ Project's principal as its execution principal. Repeating an issuance
+ key_id returns 409 executor_credential_exists; after an uncertain
+ response, list the credentials and rotate that key_id explicitly.
+ Rotation keeps the key's Environment, invalidates the old secret and
+ restores a revoked key; rotating an unknown key_id returns 404. In an
+ archived Project, issuance and rotation return 409 project_archived.
+ The Environment must be a self_hosted Environment of the Project whose
+ Session exists; otherwise 404. Each write records an administrator
+ audit entry without the secret.
+ - content: >-
+ Core key only. Revokes one credential restricted to this Environment;
+ repeated revocation is safe and it also works in an archived Project.
+ Revocation denies future enrollment and connection but does not stop
+ executor-owned compute. The Environment must be a self_hosted
+ Environment of the Project whose Session exists; otherwise 404.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/files.mdx b/apps/docs/content/docs/api-reference/core/files.mdx
new file mode 100644
index 000000000..186f103bb
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/files.mdx
@@ -0,0 +1,29 @@
+---
+title: Files
+description: >-
+ Files. Core administration API: Core key held by Web’s server or an operator
+ script. Generated local management contract. This is not part of the public
+ OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/index.mdx b/apps/docs/content/docs/api-reference/core/index.mdx
new file mode 100644
index 000000000..a80a06dbc
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/index.mdx
@@ -0,0 +1,32 @@
+---
+title: Core administration API
+description: /core/v1 — Core key held by Web’s server or an operator script.
+---
+
+Generated local management contract. This is not part of the public OpenAI API.
+
+**Credential:** Core key held by Web’s server or an operator script. Examples use reserved `example.com` origins. This reference does not send requests or collect credentials.
+
+Operator scripts use Core’s loopback port. The public entry routes management through Web, which requires its signed-in session and supplies the Core key on the server.
+
+[API namespaces and credentials](/public-api) · [Application reference](/api-reference) · [Administration reference](/api-reference/core) · [Machine reference](/api-reference/machine)
+
+- [core-administration](/api-reference/core/core-administration)
+- [deployment-model-providers](/api-reference/core/deployment-model-providers)
+- [administrator-projects](/api-reference/core/administrator-projects)
+- [agents](/api-reference/core/agents)
+- [environment-templates](/api-reference/core/environment-templates)
+- [executor-credentials](/api-reference/core/executor-credentials)
+- [files](/api-reference/core/files)
+- [write-audit](/api-reference/core/write-audit)
+- [sessions](/api-reference/core/sessions)
+- [artifacts](/api-reference/core/artifacts)
+- [execution-configuration](/api-reference/core/execution-configuration)
+- [items](/api-reference/core/items)
+- [runtime-history](/api-reference/core/runtime-history)
+- [runtime-observations](/api-reference/core/runtime-observations)
+- [turns](/api-reference/core/turns)
+- [skills](/api-reference/core/skills)
+- [vaults](/api-reference/core/vaults)
+- [credentials](/api-reference/core/credentials)
+- [sandbox-manager](/api-reference/core/sandbox-manager)
diff --git a/apps/docs/content/docs/api-reference/core/items.mdx b/apps/docs/content/docs/api-reference/core/items.mdx
new file mode 100644
index 000000000..7589bc49b
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/items.mdx
@@ -0,0 +1,23 @@
+---
+title: Items
+description: >-
+ Items. Core administration API: Core key held by Web’s server or an operator
+ script. Generated local management contract. This is not part of the public
+ OpenAI API.
+full: true
+_openapi:
+ method: GET
+ route: /core/v1/projects/{project_id}/sessions/{session_id}/items
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/meta.json b/apps/docs/content/docs/api-reference/core/meta.json
new file mode 100644
index 000000000..87f295cfb
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/meta.json
@@ -0,0 +1,25 @@
+{
+ "title": "Core administration API",
+ "pages": [
+ "index",
+ "core-administration",
+ "deployment-model-providers",
+ "administrator-projects",
+ "agents",
+ "environment-templates",
+ "executor-credentials",
+ "files",
+ "write-audit",
+ "sessions",
+ "artifacts",
+ "execution-configuration",
+ "items",
+ "runtime-history",
+ "runtime-observations",
+ "turns",
+ "skills",
+ "vaults",
+ "credentials",
+ "sandbox-manager"
+ ]
+}
diff --git a/apps/docs/content/docs/api-reference/core/runtime-history.mdx b/apps/docs/content/docs/api-reference/core/runtime-history.mdx
new file mode 100644
index 000000000..5b7e2da6f
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/runtime-history.mdx
@@ -0,0 +1,26 @@
+---
+title: Runtime history
+description: >-
+ Runtime history. Core administration API: Core key held by Web’s server or an
+ operator script. Generated local management contract. This is not part of the
+ public OpenAI API.
+full: true
+_openapi:
+ method: GET
+ route: /core/v1/projects/{project_id}/sessions/{session_id}/runtime-history
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only; the Project ID selects the target space and does not
+ authenticate. Returns stored Runtime observations for one Session. End
+ is exclusive; the server selects a bounded resolution. Responses
+ contain at most 1,000 series, 10,000 points per coverage/series array,
+ and 100,000 total coverage plus series points. It never reads or
+ changes live compute.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/runtime-observations.mdx b/apps/docs/content/docs/api-reference/core/runtime-observations.mdx
new file mode 100644
index 000000000..b9a2917ae
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/runtime-observations.mdx
@@ -0,0 +1,23 @@
+---
+title: Runtime observations
+description: >-
+ Runtime observations. Core administration API: Core key held by Web’s server
+ or an operator script. Generated local management contract. This is not part
+ of the public OpenAI API.
+full: true
+_openapi:
+ method: GET
+ route: /core/v1/projects/{project_id}/sessions/{session_id}/runtime-observation
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only; the Project ID selects the target space and does not
+ authenticate. Returns one read-only current Runtime observation. It
+ never provisions, renews, restarts, pauses or stops compute.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/sandbox-manager.mdx b/apps/docs/content/docs/api-reference/core/sandbox-manager.mdx
new file mode 100644
index 000000000..f05ee6363
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/sandbox-manager.mdx
@@ -0,0 +1,61 @@
+---
+title: Sandbox Manager
+description: >-
+ Sandbox Manager. Core administration API: Core key held by Web’s server or an
+ operator script. Generated local management contract. This is not part of the
+ public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Does not grant project resource access. Responses
+ contain only explicit safe fields. E2B template_build values are those
+ Core read when the selection was saved; this read does not call E2B.
+ - content: >-
+ Selects a provider, enforced resource limits and pinned Runtime
+ release. Core derives the deployment's core_url from the installation
+ public URL and rejects a core_url member with 400. E2B returns 409
+ sandbox_configuration_error while the public URL is loopback. E2B
+ credentials are write-only. E2B may omit resources to adopt the
+ validated template build's CPU and memory, returned in
+ specification.resources. Exact retries return the existing selection;
+ differing selections and file-managed deployments reject. This does
+ not create compute or execute work.
+ - content: >-
+ Requires maintenance, the current generation and verified cleanup of
+ all old resources. Credentials are write-only. E2B may omit resources
+ to adopt the validated template build's CPU and memory. A core_url
+ member is rejected with 400; the address comes from the installation
+ public URL. Historical records are retained; old node credentials and
+ enrollments are retired. Explicitly resume after success. Never
+ automatically retry an uncertain write.
+ - content: >-
+ Requires the current generation. Maintenance preserves execution,
+ cleanup and history. Resume requires successfully activated provider
+ configuration.
+ - content: >-
+ Core key only. Does not grant project resource access. Responses
+ contain only explicit safe fields.
+ - content: >-
+ Core key only. Does not grant project resource access. Responses
+ contain only explicit safe fields.
+ - content: >-
+ Core key only. Complete UTC buckets. Missing host measurements and
+ offline history are null; reads never sample or backfill.
+ - content: >-
+ Core key only. Does not grant project resource access. Responses
+ contain only explicit safe fields.
+ - content: >-
+ Core key only. Does not grant project resource access. Responses
+ contain only explicit safe fields.
+ - content: >-
+ Core key only. Does not grant project resource access. Responses
+ contain only explicit safe fields.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/sessions.mdx b/apps/docs/content/docs/api-reference/core/sessions.mdx
new file mode 100644
index 000000000..174d997f9
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/sessions.mdx
@@ -0,0 +1,29 @@
+---
+title: Sessions
+description: >-
+ Sessions. Core administration API: Core key held by Web’s server or an
+ operator script. Generated local management contract. This is not part of the
+ public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/skills.mdx b/apps/docs/content/docs/api-reference/core/skills.mdx
new file mode 100644
index 000000000..1521c7fb9
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/skills.mdx
@@ -0,0 +1,49 @@
+---
+title: Skills
+description: >-
+ Skills. Core administration API: Core key held by Web’s server or an operator
+ script. Generated local management contract. This is not part of the public
+ OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/turns.mdx b/apps/docs/content/docs/api-reference/core/turns.mdx
new file mode 100644
index 000000000..d29b6ce94
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/turns.mdx
@@ -0,0 +1,25 @@
+---
+title: Turns
+description: >-
+ Turns. Core administration API: Core key held by Web’s server or an operator
+ script. Generated local management contract. This is not part of the public
+ OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/vaults.mdx b/apps/docs/content/docs/api-reference/core/vaults.mdx
new file mode 100644
index 000000000..552e8e9de
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/vaults.mdx
@@ -0,0 +1,29 @@
+---
+title: Vaults
+description: >-
+ Vaults. Core administration API: Core key held by Web’s server or an operator
+ script. Generated local management contract. This is not part of the public
+ OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+ - content: >-
+ Core key only. Reuses the public resource projection and operation
+ rules; the Project ID selects the target space and does not
+ authenticate.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/core/write-audit.mdx b/apps/docs/content/docs/api-reference/core/write-audit.mdx
new file mode 100644
index 000000000..70eed9da8
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/core/write-audit.mdx
@@ -0,0 +1,26 @@
+---
+title: Write Audit
+description: >-
+ Write Audit. Core administration API: Core key held by Web’s server or an
+ operator script. Generated local management contract. This is not part of the
+ public OpenAI API.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Core key only. The Project ID path selects its space. Returns null for
+ resources without recorded creation provenance, including historical
+ and foreign resources. No key secret is returned.
+ - content: >-
+ Core key only. Reverse chronological keyset pagination over committed
+ writes. Creation records remain; other records follow configured
+ retention. The key path selects its independent space, never a
+ caller-supplied tenant.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/credentials.mdx b/apps/docs/content/docs/api-reference/credentials.mdx
new file mode 100644
index 000000000..b75f46360
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/credentials.mdx
@@ -0,0 +1,74 @@
+---
+title: Credentials
+description: >-
+ Credentials. Application API: Project API key. Public schema constrained by
+ the pinned OpenAI Agents API baseline; documented x_agents_core fields remain
+ Core extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists only metadata from the authenticated project's requested Vault,
+ without decryption or execution. An unknown, malformed or foreign
+ after cursor, including another Vault's Credential, returns not found.
+ Includes active and archived Credentials by default, independently of
+ Vault status. Status accepts a scalar, the SDK status[] array or both,
+ filtering by their union; a repeated scalar is rejected. Limits
+ default to 20 and clamp to 1–100. Equal creation times use ID
+ ordering. Hosted errors, concurrent-page behavior and archive/delete
+ lifecycle remain unverified or unimplemented.
+ - content: >-
+ Stores static_bearer or mcp_oauth secrets as execution-owned
+ authenticated ciphertext without contacting any endpoint. Static
+ bearer and OAuth access tokens must be nonempty strings; their bytes
+ are preserved. OAuth accepts a required access token, nullable RFC3339
+ expiry and optional refresh configuration with none,
+ client_secret_basic or client_secret_post authentication. Required
+ name is trimmed to 1–256 UTF-8 bytes. Credential and token endpoints
+ require HTTPS without userinfo or fragments. Responses contain safe
+ metadata only, including explicit nullable OAuth expiry, refresh,
+ resource and scope. Missing encryption configuration returns local
+ 503. External authorization and provider revocation remain caller
+ responsibilities; exact hosted error/default semantics remain
+ unverified.
+ - content: >-
+ Reads only non-secret metadata scoped to the authenticated project and
+ owning Vault. No token decryption, network request or execution is
+ performed. Unknown, foreign, wrong-Vault and malformed IDs use the
+ same local not-found response; hosted error parity remains unverified.
+ - content: >-
+ Explicitly empty static bearer or OAuth access tokens and OAuth
+ patches without a mutable field are rejected before storage. Omitted
+ OAuth access tokens preserve the existing grant when expiry or refresh
+ fields change. Updates the existing static_bearer or mcp_oauth
+ authentication method without network requests. OAuth access_token
+ omission/null retains the token; a new token clears omitted expiry,
+ explicit null clears expiry, and other omitted fields remain
+ unchanged. OAuth refresh patches cannot add configuration or change
+ client, endpoint, resource or authentication method; nullable
+ token/client-secret values retain stored secrets while explicit null
+ scope clears scope. Whole-null refresh and token_endpoint_auth retain
+ existing configuration under local policy. Identity, destination,
+ creation time and Session bindings remain unchanged. Responses expose
+ safe metadata only. Already-dispatched work is not revoked; provider
+ revocation, storage-key rotation and exact hosted
+ concurrent-update/error semantics remain separate.
+ - content: >-
+ Removes one Credential and its encrypted token within the
+ authenticated project and owning Vault, without an encryption key or
+ secret decryption. Subsequent metadata reads, updates and dispatch
+ lookups cannot use it. Existing Session snapshots and history retain
+ their frozen identities; already-resolved tokens and running Sessions
+ are not revoked or cancelled. This local policy removes the row rather
+ than defining archived lifecycle; missing/repeated deletion returns
+ 404. Exact hosted archive, post-delete visibility and retry/error
+ semantics remain unverified. Provider revocation and physical erasure
+ from native history, WAL or backups are separate concerns.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/environment-templates.mdx b/apps/docs/content/docs/api-reference/environment-templates.mdx
new file mode 100644
index 000000000..5e68bd5c2
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/environment-templates.mdx
@@ -0,0 +1,56 @@
+---
+title: Environment Templates
+description: >-
+ Environment Templates. Application API: Project API key. Public schema
+ constrained by the pinned OpenAI Agents API baseline; documented x_agents_core
+ fields remain Core extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists tenant-owned safe template metadata in creation order with ID
+ tie-breaking. Defaults to limit 20 and descending order; limit 0 is
+ treated as 1 and larger limits as 100. Foreign, missing and malformed
+ cursors return the same not found error. Concurrent-page and exact
+ hosted error behavior remain unverified.
+ - content: >-
+ Saves tenant-owned hosted configuration. Supports nullable name,
+ enabled/disabled or exact-domain restricted network, initial
+ inline/file_id files, confidential env, ordered setup_commands,
+ system/npm/Python packages inline/referenced Skill ZIPs, Plugin ZIPs
+ and workspace-contained capability directories. Omitted/null network
+ defaults to enabled. Restricted network requires 1–100 exact ASCII
+ hostnames; other host forms and populated unsupported installations
+ are rejected before persistence without echoing input. Network policy
+ rejections return invalid_request_error with a null param. No compute
+ is allocated. Exact hosted error/retry semantics remain unverified.
+ - content: >-
+ Returns safe tenant-owned configuration metadata without allocating
+ compute. Missing and foreign resources return the same not-found
+ response.
+ - content: >-
+ Supplied fields replace atomically; omitted fields remain unchanged.
+ Null name clears and null network resets to the pinned enabled
+ default. Existing Session snapshots and creation retries remain
+ unchanged. Initial files replace as a list; null/empty clears. File
+ data is encrypted separately and excluded from response metadata.
+ Skills replace as a list; null/empty clears. Skill archives are
+ encrypted separately and omitted from responses. Plugins and
+ capability directories replace as lists; null/empty clears. Plugin
+ archives are encrypted and omitted from responses. Capability
+ directories are snapshotted after setup. Environment MCP execution
+ requires a qualified native transport and runtime network policy.
+ Empty updates advance updated_at without changing saved fields or
+ confidential contents. Network policy rejections return
+ invalid_request_error with a null param.
+ - content: >-
+ Deletes the tenant-owned reusable configuration without changing or
+ deleting existing Sessions and their frozen configuration.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/environments.mdx b/apps/docs/content/docs/api-reference/environments.mdx
new file mode 100644
index 000000000..287d4db3f
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/environments.mdx
@@ -0,0 +1,63 @@
+---
+title: Environments
+description: >-
+ Environments. Application API: Project API key. Public schema constrained by
+ the pinned OpenAI Agents API baseline; documented x_agents_core fields remain
+ Core extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Returns durable connection status and safe installed metadata for
+ supported self_hosted and basic openai_hosted profiles. Initial files
+ expose frozen safe metadata without content; Plugin/Skill entries
+ expose only safe configured installation metadata.
+ Capability-directory discoveries are not added to those arrays.
+ Unsupported installation configurations remain implementation gaps.
+ This read does not prepare execution, start compute or require an
+ enabled execution worker. Session deletion removes the associated
+ Environment from public reads; project-shared read authorization is
+ unchanged. Connection status does not prove native readiness or
+ process quiescence.
+ - content: >-
+ Lists direct regular files in one authorized self_hosted or qualified
+ local workspace directory. Local paths use the public /workspace root
+ and must be in cleaned form. This partial implementation defaults to
+ the workspace root and limit 20; recursive scope and these defaults
+ are not verified upstream semantics. A missing path, a regular file or
+ a symbolic link returns an empty page; links are never followed.
+ Daemons without a local workspace binding use the Claude SDK adapter
+ reader, which keeps 404 for a missing path and 503 for a regular file
+ or symbolic link. Well-formed unknown query keys are ignored;
+ malformed query encoding and a repeated supported key are rejected.
+ Sorts by case-sensitive path components, descending by default. Keep
+ the same path, order and limit when using page. Each page rereads the
+ complete bounded directory; changed file paths/sizes invalidate
+ continuation locally with 400. There is no snapshot guarantee. An
+ openai_hosted Environment that has not connected yet returns 400.
+ Truncated or uncertain native results fail with 503 without returning
+ a partial page. This read never starts a Turn or admits model input.
+ Actual transport disconnect/reconnect events remain observable.
+ - content: >-
+ Uploads standard Base64 bytes to a file beneath /workspace in a
+ qualified local Environment and returns 201. Accepts inline bytes or a
+ project-owned source file_id through the same write path. Unknown body
+ fields are rejected with their name as param. Basic public hosted
+ creation requires explicit managed Runtime configuration; an
+ openai_hosted Environment that has not connected yet returns 400.
+ Inline data is limited to 5 MiB decoded and a file_id copy to 50 MiB.
+ Missing parent directories are created with mode 0700 and the file
+ with mode 0600. An existing destination is never replaced; a
+ directory, an existing file or a path through a symlink or
+ non-directory returns 400. Idle writes exclude execution. Missing
+ receipts return unavailable and retain a durable mutation gate without
+ automatic replay. Error/timing parity with upstream remains
+ unverified.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/events.mdx b/apps/docs/content/docs/api-reference/events.mdx
new file mode 100644
index 000000000..227e260ff
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/events.mdx
@@ -0,0 +1,33 @@
+---
+title: Events
+description: >-
+ Events. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ method: GET
+ route: /agents/sessions/{session_id}/events
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Live-only events, including command output fragments from capable
+ Codex peers as agent.output.command_execution_output.delta with stable
+ Item/output indexes. Native text conversion and output quotas apply;
+ completion snapshots remain authoritative. Reconnect through Session,
+ Turn and Items reads; missed events are not replayed. A lagging stream
+ closes with an error when its bounded buffer is exceeded. When a
+ hosted Environment fails to provision, the stream sends
+ agent.session.environment.failed, an error event
+ (environment_error/sandbox_error with the safe step and exit-status
+ reason, never command output) and agent.session.failed, then ends.
+ Session activity includes immutable pending-input connection actions
+ before Turn creation; self_hosted environments use the same safe
+ output as Session retrieval.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/files.mdx b/apps/docs/content/docs/api-reference/files.mdx
new file mode 100644
index 000000000..21b1c0709
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/files.mdx
@@ -0,0 +1,48 @@
+---
+title: Files
+description: >-
+ Files. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists project-owned Files without reading their bodies. The limit
+ defaults to 10000 and must be 1–10000. Equal creation times use ID
+ ordering. Purpose validation precedes cursor lookup; current storage
+ contains only user_data. An explicit empty purpose is treated as
+ omitted. Repeated purpose values remain rejected. Hosted positive
+ filtering, default order and concurrent-page behavior remain
+ unverified. No Beta header is required.
+ - content: >-
+ Accepts one multipart file and purpose=user_data in either order, with
+ a private 512 MiB content limit and 64 KiB envelope allowance. Commits
+ only after the entire request validates. The source is project-owned,
+ independent of Sessions and workspace copies. No Beta header is
+ required. Other purposes, expires_after, listing, resumable Uploads,
+ quotas/rate-limit and complete hosted error/status parity remain
+ unsupported or unverified.
+ - content: >-
+ Returns immutable project-owned user_data file metadata. No Beta
+ header is required. Other purposes, expiration and full hosted
+ status/error semantics remain unimplemented or unverified.
+ - content: >-
+ Atomically deletes project-owned metadata and stored bytes.
+ Already-admitted reads or copies may finish. Workspace copies remain
+ independent. Historical WAL/backups are not erased. No Beta header is
+ required; exact hosted concurrent deletion/error semantics remain
+ unverified.
+ - content: >-
+ Resolves project-owned File metadata before enforcing download policy.
+ Public download of user_data Files returns 400; missing and foreign
+ Files return the same 404. Internal initial-file and workspace copies
+ remain available. No Beta header is required.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/index.mdx b/apps/docs/content/docs/api-reference/index.mdx
new file mode 100644
index 000000000..e2bee0bf0
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/index.mdx
@@ -0,0 +1,24 @@
+---
+title: Application API
+description: /v1 — Project API key.
+---
+
+Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.
+
+**Credential:** Project API key. Examples use reserved `example.com` origins. This reference does not send requests or collect credentials.
+
+[API namespaces and credentials](/public-api) · [Application reference](/api-reference) · [Administration reference](/api-reference/core) · [Machine reference](/api-reference/machine)
+
+- [agents](/api-reference/agents)
+- [environments](/api-reference/environments)
+- [environment-templates](/api-reference/environment-templates)
+- [sessions](/api-reference/sessions)
+- [artifacts](/api-reference/artifacts)
+- [events](/api-reference/events)
+- [items](/api-reference/items)
+- [subagents](/api-reference/subagents)
+- [turns](/api-reference/turns)
+- [files](/api-reference/files)
+- [skills](/api-reference/skills)
+- [vaults](/api-reference/vaults)
+- [credentials](/api-reference/credentials)
diff --git a/apps/docs/content/docs/api-reference/items.mdx b/apps/docs/content/docs/api-reference/items.mdx
new file mode 100644
index 000000000..1c010f4b0
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/items.mdx
@@ -0,0 +1,26 @@
+---
+title: Items
+description: >-
+ Items. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ method: GET
+ route: /agents/sessions/{session_id}/items
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Returns supported message and tool Items in first-observation order.
+ Native engine fields are projected explicitly; unfinished Items on
+ terminal Turns are incomplete. Cursors are Items of the same tenant
+ and Session. Any other after value, including a malformed one, returns
+ 400 invalid_request_error with the message "Invalid session item ID in
+ `after`".
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/machine/index.mdx b/apps/docs/content/docs/api-reference/machine/index.mdx
new file mode 100644
index 000000000..db719d415
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/machine/index.mdx
@@ -0,0 +1,14 @@
+---
+title: Machine connection API
+description: /api/v1 — Route-specific node enrollment, node, daemon, or executor credential.
+---
+
+Generated local machine contract. These connections reach Core directly, never through Web.
+
+**Credential:** Route-specific node enrollment, node, daemon, or executor credential. Examples use reserved `example.com` origins. This reference does not send requests or collect credentials.
+
+This schema covers node configuration, enrollment and identity. Daemon WebSockets and executor connection details are described in the [machine overview](/public-api#machine-connection-api).
+
+[API namespaces and credentials](/public-api) · [Application reference](/api-reference) · [Administration reference](/api-reference/core) · [Machine reference](/api-reference/machine)
+
+- [sandbox-node](/api-reference/machine/sandbox-node)
diff --git a/apps/docs/content/docs/api-reference/machine/meta.json b/apps/docs/content/docs/api-reference/machine/meta.json
new file mode 100644
index 000000000..717a2b47d
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/machine/meta.json
@@ -0,0 +1,7 @@
+{
+ "title": "Machine connection API",
+ "pages": [
+ "index",
+ "sandbox-node"
+ ]
+}
diff --git a/apps/docs/content/docs/api-reference/machine/sandbox-node.mdx b/apps/docs/content/docs/api-reference/machine/sandbox-node.mdx
new file mode 100644
index 000000000..7ab0fde2a
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/machine/sandbox-node.mdx
@@ -0,0 +1,31 @@
+---
+title: Sandbox Node
+description: >-
+ Sandbox Node. Machine connection API: Route-specific node enrollment, node,
+ daemon, or executor credential. Generated local machine contract. These
+ connections reach Core directly, never through Web.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Authenticates with an unconsumed enrollment token, or a retained node
+ credential with X-OAC-Node-ID. Does not consume the token or expose
+ E2B credentials. Node files cannot override this specification.
+ - content: >-
+ Node machine connection. Consumes a one-use enrollment token; grants
+ no project or administrator access. Responses contain only explicit
+ safe fields. core_url is required and must equal the installation
+ public URL; a different address gets 409 sandbox_node_address_mismatch
+ and leaves the token unused.
+ - content: >-
+ Node machine connection. Authenticates with the retained node
+ credential; grants no project or administrator access. Responses
+ contain only explicit safe fields.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/meta.json b/apps/docs/content/docs/api-reference/meta.json
new file mode 100644
index 000000000..f96a25397
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/meta.json
@@ -0,0 +1,21 @@
+{
+ "title": "Application API",
+ "pages": [
+ "index",
+ "agents",
+ "environments",
+ "environment-templates",
+ "sessions",
+ "artifacts",
+ "events",
+ "items",
+ "subagents",
+ "turns",
+ "files",
+ "skills",
+ "vaults",
+ "credentials",
+ "core",
+ "machine"
+ ]
+}
diff --git a/apps/docs/content/docs/api-reference/sessions.mdx b/apps/docs/content/docs/api-reference/sessions.mdx
new file mode 100644
index 000000000..2aa51289f
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/sessions.mdx
@@ -0,0 +1,239 @@
+---
+title: Sessions
+description: >-
+ Sessions. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Cursor and results are scoped to the authenticated execution tenant;
+ an unknown, malformed or foreign after cursor returns not found.
+ Optional agent_id matches the immutable root Agent ID, including
+ inline Agents and historical Sessions whose saved source was updated
+ or deleted. Omission lists all Agents. Returns the same Environment
+ and pending-input activity projection as Session retrieval, including
+ self_hosted Sessions.
+ - content: >-
+ The optional Core model_provider bundle resolves from the Session
+ override, saved Agent defaults, then, for openai_hosted and none, the
+ deployment default of the resolved harness; self_hosted never uses the
+ deployment default and none accepts only it. openai_hosted and
+ self_hosted Sessions that resolve no bundle return 400
+ model_provider_required with param x_agents_core.model_provider before
+ any write. Core encrypts and freezes the resolved bundle; later Agent
+ or deployment default edits and same-key retries cannot change it.
+ Keys are never returned. Supports inline configuration or a
+ tenant-owned saved agent_id with per-Session field replacements.
+ Execution supports model/instructions, text verbosity, non-deferred
+ function tools, adapter-qualified multi_agent with persisted Subagent
+ reads, implicit reasoning, service tier auto and environment type
+ none, subject to the configured engine. Codex additionally supports
+ HTTP MCP with service origin (omitted or null on HTTP transport is
+ saved as service), native allowed_tools and boolean required
+ defaulting to false. Session vault_ids attach only project-owned
+ Vaults; credential_id selects an attached static bearer or OAuth
+ credential for the exact HTTPS URL, while null/omission selects a
+ unique match or remains anonymous. Session reads, lists and event
+ snapshots show that implicitly selected credential ID in a null or
+ omitted credential_id, also after the credential is deleted; anonymous
+ selections stay null and the stored caller intent is unchanged. After
+ the input requirement and before any write, a credential_id without
+ vault_ids, one outside the attached Vaults (one message for missing,
+ foreign and unattached IDs) or one for another server_url returns 400
+ invalid_request_error, and several implicit matches return 409
+ conflict_error. Missing decryption configuration fails dispatch
+ without anonymous fallback. Required initialization uses native
+ startup before the first native Turn, including cold resume, and
+ requires a separately advertised capability; exact hosted creation
+ timing and error parity remain unverified. Other MCP origins and
+ native OAuth login remain unsupported. The self_hosted profile
+ requires Codex, an absolute workspace_directory and empty
+ capability_directories, with optional non-deferred function tools and
+ HTTP MCP using service origin, optionally authenticated by the
+ attached Vault rules. Remote MCP and remote Bearer authentication each
+ require separately advertised combination support; old peers cannot
+ receive unsupported work. Omitted/null capability_directories use the
+ empty-list default; self_hosted requires configured execution plus
+ executor registry. Claude SDK currently requires medium verbosity and
+ object-root function schemas. It supports anonymous or attached
+ static-bearer service-origin HTTP MCP on none with boolean required
+ and separately advertised MCP/bearer/required runtime support.
+ Required servers must be connected before the first native input is
+ released; pending or failed startup rejects execution. The shared
+ Vault selection and immutable binding rules apply; unsupported native
+ labels/tool names reject before persistence. An attached Vault with no
+ matching credential may remain anonymous; missing keys or failed
+ credential lookup/decryption never fall back to anonymous execution.
+ Omitted stream defaults to false; stream and agent_id cannot be null.
+ Metadata may be null; non-string values and limit violations return
+ invalid_request_error with a metadata or metadata. param. The
+ inline agent uses the Agent create configuration validation with
+ agent.-prefixed params, reported before the input requirement and
+ saved-Agent lookup; saved configurations with conflicting tools or
+ schema roots reject admission with the same errors, and execution
+ limits keep unsupported_or_invalid_configuration. Hosted network
+ policy rejections return invalid_request_error with a null param.
+ Initial input accepts a string or ordered user-message array. Codex
+ and Claude SDK on none and qualified openai_hosted also accept inline
+ PNG/JPEG image content; other image combinations and remote URLs are
+ unsupported. None initial input atomically starts a Turn; self_hosted
+ initial input is reserved while returning its Environment connection
+ target, with execution deferred to native readiness and Session
+ failure on initial timeout. Initial input is required for none and for
+ streamed creation outside self_hosted. Omitted/null input remains
+ valid for non-streaming hosted and self_hosted creation. With
+ stream=true, returns live Session events starting with the committed
+ creation snapshot and closes right after the first agent.session.idle
+ recorded when a Turn ends or an input reservation stops being pending,
+ or any agent.session.failed, without sending later events. A creation
+ that admitted nothing closes after the snapshot; a settlement that
+ records no event closes after events up to the cursor read with a
+ settled Session projection. Required actions keep it open; disconnect
+ does not cancel execution. The GET events stream remains live-only.
+ New Sessions retain their authenticated creator; all creation retries
+ require the same typed subject, including across key rotation.
+ Saved-Agent retries and inline requests using Vault attachments or
+ credential references retain caller intent independently of later
+ resource changes; new hosted inline requests also freeze caller intent
+ before deployment defaults resolve; unrelated non-hosted inline
+ retries preserve resolved/default equivalences, and their resolved
+ hash leaves out any deployment default. Provider keys enter retry
+ hashes only as fingerprints keyed by the credential key. Unknown
+ historical creators reject retries; known creators without recorded
+ intent retain resolved-snapshot retry rules. These conflict policies
+ are local and not verified hosted parity. A same-key stream=true retry
+ of an existing creation returns 201 with no events and closes at once;
+ retry with stream=false or use the GET events stream to recover.
+ Claude SDK on none and Core-managed Docker openai_hosted supports
+ qualified object-root json_schema output with medium verbosity,
+ single-Agent execution and ordinary functions. Hosted execution reuses
+ native workspace tools and Files/Artifacts; Skills, Plugins,
+ capability directories, HTTP MCP, Subagent and tool_search
+ combinations remain unqualified, including inherited template
+ contents. Other non-text initial input remains unsupported. Basic
+ Codex and Claude SDK openai_hosted creation requires an explicitly
+ configured managed provider. The Claude workspace profile supports
+ non-deferred function tools with text or successful inline PNG/JPEG
+ results alongside native workspace tools; HTTP MCP remains
+ unsupported. Idle Sessions provision automatically; initial
+ provisioning has no caller connection action. Network defaults to
+ enabled; disabled and restricted exact ASCII hostnames are supported.
+ Restricted policy requires 1–100 allowed domains. Unsupported hostname
+ forms and startup installations are rejected. Confidential env,
+ system/npm/Python packages and ordered setup commands use the shared
+ initialization lifecycle; requested network applies after setup.
+ Initial inline and tenant-owned file_id files freeze encrypted bytes
+ before provisioning, then install through the common Core lifecycle
+ before native execution or live Files access. With a template
+ reference, omitted/null files, env, packages and setup_commands
+ inherit. Non-null files and command lists replace; env overlays by
+ key; each package manager inherits on omission/null and otherwise
+ replaces its list. Empty lists clear their selected field.
+ Tenant-owned environment_template_id references inherit omitted/null
+ network and allow only narrowing overrides. Inline hosted network:null
+ retains the enabled default; updating a Template with network:null
+ resets its saved policy to enabled. Core freezes effective
+ configuration; template updates/deletion do not alter Session
+ snapshots or same-intent creation retries. Inline or tenant-owned
+ skill_reference Skills share initialization. Templates preserve
+ default/latest/explicit selectors; Session creation freezes concrete
+ metadata and encrypted content atomically. Skill, Plugin and
+ capability-directory list omission/null inherit; a non-null list
+ replaces, including empty-list clearing. Omitted/null Skill version
+ selectors resolve the default version. Source deletion/default updates
+ cannot change committed Session Skill contents. Deferred function
+ discovery uses type-only tool_search and per-function defer_loading in
+ the qualified single-agent Claude environment:none function profile,
+ including qualified inline image messages and text results. Explicit
+ web_search mode disabled and programmatic_tool_calling enabled false
+ use frozen common Runtime controls. Enabled forms, including those
+ saved on an Agent, remain unqualified and reject before any write
+ unless the Session replaces tools. Omitted programmatic configuration
+ preserves native behavior, a documented difference from the official
+ default-on behavior. Other combinations remain unqualified; see the
+ operation coverage.
+ - content: >-
+ Returns supported none, self_hosted and basic openai_hosted Session
+ environments. Self-hosted pending input can require a caller
+ connection before a Turn exists. Hosted initial provisioning remains
+ idle until a Turn starts; connection observations are not native
+ execution readiness.
+ - content: >-
+ The metadata field is required in an update body. Send null or {} to
+ clear it, or supply an object to replace all pairs. Up to 16 string
+ pairs, with keys at most 64 characters and values at most 512
+ characters; violations and non-string values return
+ invalid_request_error with a metadata or metadata. param. U+0000
+ is rejected as a local storage limit. Malformed, missing and foreign
+ Session IDs share the not-found response. Execution configuration and
+ activity are unchanged. Returns the same safe Environment and
+ pending-input activity projection as Session retrieval.
+ - content: >-
+ Removes a durably idle or failed Session and its history from the
+ public API. A Session whose root Turn is queued, in progress or
+ waiting (including required actions) or whose input reservation is
+ pending returns 409 conflict_error and is left unchanged; cancel it
+ and wait until it is idle before deleting. Subagent child Turns and
+ pending Environment file writes are not checked and do not block
+ deletion. Repeating the deletion of the caller's own deleted Session
+ returns the same confirmation; missing and foreign Sessions return
+ 404. Internal records and native history are retained pending separate
+ physical cleanup; overlapping stream timing remains unverified.
+ - content: >-
+ An empty events array is a resource-authorized no-op; it creates no
+ execution retry identity, Turn, Item or input receipt. For environment
+ none, atomically accepts text messages, cancellation and function
+ results. Messages steer active work or start a queued Turn. The
+ supported self_hosted profile accepts text-only messages; qualified
+ Codex and Claude SDK openai_hosted profiles also accept inline
+ PNG/JPEG. Under the Session lock, matching retries retain their
+ original target; new active messages append to the current Turn, while
+ idle messages reserve work and wait up to the original five-minute
+ connection/admission deadline. Return 202 only after durable
+ admission, without claiming native application; active messages create
+ no Turn or reservation. Cancellation-only prepared-environment batches
+ use existing durable cancellation admission and return 202 without
+ waiting for native exit; a new cancellation conflicts while a pre-Turn
+ reservation is pending. Homogeneous tool_result-only
+ prepared-environment batches reuse existing scoped result admission
+ and application receipts without creating a Turn or bypassing a
+ pending reservation. Mixed prepared-environment batches remain
+ unsupported. HTTP expiry/cancellation use local 409
+ environment_input_expired/environment_input_cancelled errors. New
+ input on a Session whose hosted Environment failed to provision
+ returns the observed 409 conflict_error "the hosted environment failed
+ to provision"; input already waiting when it fails and expired
+ Environments keep the local 409 environment_unavailable. Input the
+ Session cannot accept in its current state, such as a result after
+ cancellation or a batch while earlier input is pending, and a result
+ that differs from the call's saved result return 409 with type and
+ code conflict_error; reusing an Idempotency-Key with a different batch
+ returns the local 409 idempotency_conflict. Inside an owned Session, a
+ result for an unknown call or for a call of another Turn returns 400
+ invalid_request_error and changes nothing; missing and foreign
+ Sessions return 404. Losing execution ownership returns 503. The
+ response write deadline accommodates the admission window for either
+ prepared Environment, independently of new-hosted-admission and
+ executor URL settings. Disconnecting the waiting HTTP request does not
+ cancel retained work or restart its deadline. Retry keys identify the
+ whole ordered batch. Function output accepts text or ordered
+ text/image parts subject to engine support; Claude SDK accepts text
+ results and, on none and qualified openai_hosted, successful inline
+ PNG/JPEG results, preserving ordered content; error images and remote
+ references reject before admission. Native image resizing may change
+ bytes. Runtime image-result support is checked only for image-bearing
+ delivery. Codex and Claude SDK on none and qualified openai_hosted
+ accept ordered inline PNG/JPEG image messages. Self-hosted profiles
+ and other engines remain text-only; remote image URLs are unsupported.
+ Image references are retained unchanged without service-side
+ downloads.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/skills.mdx b/apps/docs/content/docs/api-reference/skills.mdx
new file mode 100644
index 000000000..8757bc526
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/skills.mdx
@@ -0,0 +1,51 @@
+---
+title: Skills
+description: >-
+ Skills. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists tenant-owned metadata in timestamp order. Default page size 20,
+ maximum 100. Limit 0 returns an empty page whose has_more reports
+ whether any Skill follows the cursor; exact hosted defaults remain
+ unverified.
+ - content: >-
+ Accepts one ZIP in files or a directory in files[]. Applies the
+ qualified portable Skill bundle profile. No Beta header is required;
+ full hosted upload limits and activation extensions are not qualified.
+ - content: >-
+ Returns tenant-owned metadata without decrypting contents or starting
+ Runtime. No Beta header is required.
+ - content: >-
+ Changes only the tenant-owned default pointer; immutable versions and
+ existing Session snapshots remain unchanged.
+ - content: >-
+ Deletes tenant-owned source bundles. Existing Session installation
+ snapshots remain independent.
+ - content: >-
+ Downloads an authorized ZIP using the default pointer when no concrete
+ version is supplied. Exact upstream unversioned selection, content
+ headers and range semantics remain unverified.
+ - content: >-
+ Orders by version number; after identifies a version resource, not a
+ version number. An after value that does not begin with skillver, or a
+ version of another Skill, returns 400 invalid_value with param after;
+ a missing version returns not found. No contents are decrypted. Limit
+ 0 returns an empty page whose has_more reports whether any version
+ follows the cursor.
+ - content: >-
+ Deleting the only remaining version also deletes the Skill; existing
+ Session installation snapshots remain independent. The default version
+ cannot be deleted while other versions remain. Version numbers are
+ never reused.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/subagents.mdx b/apps/docs/content/docs/api-reference/subagents.mdx
new file mode 100644
index 000000000..e2f3346ee
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/subagents.mdx
@@ -0,0 +1,49 @@
+---
+title: Subagents
+description: >-
+ Subagents. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Includes nested and closed Subagents. Cursors are Subagents of the
+ same tenant and Session. Any other after value, including a malformed
+ one, returns 400 invalid_request_error with the message "Invalid
+ resource ID in `after`". A limit outside 1–100 is rejected.
+ - content: >-
+ Returns this Session's persisted Subagent. Active includes idle
+ between Turns. Resuming preserves opened_at and clears closed_at.
+ Unknown or inaccessible parent scopes return not found.
+ - content: >-
+ Returns only this Subagent's own Items across all its Turns, not its
+ descendants' Items. Cursors are Items of the same tenant, Session and
+ Subagent. Any other after value, including a malformed one, returns
+ 400 invalid_request_error with the message "Invalid session item ID in
+ `after`".
+ - content: >-
+ Includes this Subagent's Turns after resume, with the Session's Agent
+ ID as agent_id. Cursors are Turns of the same tenant, Session and
+ Subagent. Any other after value, including a malformed one, returns
+ 400 invalid_request_error with the message "Invalid resource ID in
+ `after`". Missing recorded usage remains null. A limit outside 1–100
+ is rejected.
+ - content: >-
+ Returns a Turn owned by this Subagent. Its agent_id is the Session's
+ Agent ID and its subagent_id identifies the Subagent. Session Turn
+ routes do not return child Turns. Unknown or inaccessible parent
+ scopes return not found.
+ - content: >-
+ Returns Items owned by this exact Subagent Turn. Cursors are Items of
+ the same tenant, Session, Subagent and Turn. Any other after value,
+ including a malformed one, returns 400 invalid_request_error with the
+ message "Invalid session item ID in `after`".
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/turns.mdx b/apps/docs/content/docs/api-reference/turns.mdx
new file mode 100644
index 000000000..e3dac307c
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/turns.mdx
@@ -0,0 +1,28 @@
+---
+title: Turns
+description: >-
+ Turns. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Returns the Session's root Turns in creation order; Subagent Turns are
+ listed through the Subagent Turn routes. The cursor belongs to the
+ same Session and tenant; any other after value, including a malformed
+ one or a Subagent Turn ID, returns not found. Usage contains the
+ latest recorded complete token breakdown; missing measurements remain
+ null.
+ - content: >-
+ Returns a root Turn of this Session. A Subagent Turn ID returns the
+ same not found error as a missing Turn; read it through the Subagent
+ Turn routes.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/api-reference/vaults.mdx b/apps/docs/content/docs/api-reference/vaults.mdx
new file mode 100644
index 000000000..3bb1698f4
--- /dev/null
+++ b/apps/docs/content/docs/api-reference/vaults.mdx
@@ -0,0 +1,50 @@
+---
+title: Vaults
+description: >-
+ Vaults. Application API: Project API key. Public schema constrained by the
+ pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core
+ extensions.
+full: true
+_openapi:
+ toc: []
+ structuredData:
+ headings: []
+ contents:
+ - content: >-
+ Lists project-owned Vaults independently of execution. An unknown,
+ malformed or foreign after cursor returns not found. Includes active
+ and archived records by default. Status accepts a scalar, the SDK's
+ status[] array or both, filtering by their union; a repeated scalar is
+ rejected. Limits default to 20 and clamp to 1–100. Equal creation
+ times use ID ordering; exact hosted errors and concurrent-page
+ behavior remain unverified. Archive/delete lifecycle is not
+ implemented.
+ - content: >-
+ Creates a project-owned Vault independently of execution. Omitted name
+ stays null; a supplied string is trimmed and must contain 1–256 UTF-8
+ bytes. Explicit null name is invalid. Omitted/null metadata becomes an
+ empty object; non-string values return invalid_request_error with a
+ metadata. param. Metadata has a local 64 KiB encoded storage
+ bound. U+0000 in stored strings is rejected as a local storage limit.
+ Credentials, Session binding and hosted error/retry parity remain
+ incomplete.
+ - content: >-
+ Reads a Vault owned by the authenticated project without resolving
+ credentials, Sessions or execution devices. Missing and foreign IDs
+ share the same not-found response; exact hosted error semantics remain
+ unverified.
+ - content: >-
+ Atomically removes the authenticated project's Vault and all its
+ stored Credentials without an encryption key, decryption or external
+ requests. Existing Session snapshots, history and recorded retries
+ retain their frozen identities; subsequent credential lookups fail
+ without reselection or anonymous fallback. Already-resolved tokens and
+ running Sessions are not revoked or cancelled. Missing/repeated
+ deletion locally returns 404. Exact hosted archive, post-delete
+ visibility and concurrent/error semantics remain unverified; physical
+ erasure from native history, WAL or backups is not established.
+---
+
+{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */}
+
+
\ No newline at end of file
diff --git a/apps/docs/content/docs/bootstrap-projects-keys.mdx b/apps/docs/content/docs/bootstrap-projects-keys.mdx
new file mode 100644
index 000000000..31aef04ce
--- /dev/null
+++ b/apps/docs/content/docs/bootstrap-projects-keys.mdx
@@ -0,0 +1,101 @@
+---
+title: "Sign in and issue application keys"
+description: "Keep the Core key on the management side and issue Project API keys through Web."
+---
+
+`services/core-console` serves built Web assets, signs administrators in with the
+Core key and forwards every signed-in, same-origin `/core/v1/*` request to Core. The
+backend contract and the React screens that use it are implemented; the console has
+no execution controls.
+
+## Connection model
+
+The browser calls same-origin `/core/v1` through `AdminClient` and
+`CoreMetricsClient`, and the `/core/v1/sandbox` management routes through the
+sandbox client. The console server forwards each signed-in `/core/v1/*` request by
+prefix to its configured Core upstream, with the Core key (`OAC_WEB_CORE_KEY_FILE`)
+as the upstream credential; Core alone decides whether the route exists. Browser
+code must never receive that credential.
+
+Applications call Core's `/v1` directly with their own Project API keys and the
+public API's route-specific headers. Nodes and Runtime daemons call Core's `/api/v1`
+directly with their own machine credentials. The console returns 404 for `/v1` and
+`/api/v1`, even with an explicit Bearer token. Deployment routing must send both to Core.
+The console endpoint and the public application endpoint serve different purposes,
+even if they share a host.
+
+Use the [installation guide](/install) for deployment and the
+[operations guide](/troubleshooting) for storage, upgrades and node
+management. A Core, Web and PostgreSQL installation may have zero execution nodes.
+Opening the console neither allocates compute nor invokes a model. Deployment
+sandbox management selects E2B, Docker or microsandbox independently of an
+application's caller-managed `self_hosted` Runtime, including its own E2B setup.
+
+## Server configuration and login
+
+The installer generates these from its `config.json` and `secrets/`; set them
+yourself only for a Web you run without the installer.
+
+| Setting | Purpose |
+| --- | --- |
+| `OAC_WEB_ADDR` | Console listener address |
+| `OAC_WEB_ORIGIN` | Exact browser-facing origin used for host and origin checks |
+| `OAC_WEB_UPSTREAM` | Core HTTP(S) origin, without credentials, query or resource path |
+| `OAC_WEB_CORE_KEY_FILE` | Absolute path to the private regular file containing the Core key |
+| `OAC_WEB_DIST` | Absolute directory containing the built Web assets |
+
+The console exposes `GET /console/auth` and `POST /console/auth/login` and
+`/logout`. The administrator signs in with the deployment's Core key, which the
+installer writes to `secrets/core.key` under the installation directory (by default
+`~/.oac/core/secrets/core.key`; see [Core key](/troubleshooting#core-key)).
+The server compares it in constant time and answers with a same-origin session
+cookie held only in its memory; the key is never logged or returned, and the
+browser does not store it. A console restart or a Core key rotation requires
+signing in again. There are no console accounts, usernames or setup step, and the
+Core key cannot call `/v1`. `GET /console/config` provides safe console
+configuration to an authenticated browser.
+
+Use TLS for remote browser access and loopback listeners for local development.
+Preserve the host/origin checks and the forwarding rules. The console refuses
+requests with an `Upgrade` header, CONNECT and TRACE, and any path that `safePath`
+rejects: an encoded `%`, dot segments, empty segments or backslashes. Before
+forwarding, it strips the browser's Authorization, Cookie, Origin and Referer headers
+and overwrites the actor header (`X-Core-Console-Actor`) with `console`, so a browser
+cannot choose the audit actor label the service reports. The label is caller-declared
+and display only. Keep deployment, application, node and provider credentials out of
+`VITE_*`, browser storage, source files, URLs and logs.
+
+## Projects and application keys
+
+Installation creates no Project or application key. An administrator creates a
+Project and issues named keys on the console's **Projects and keys** page, which
+uses the [administrator API](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md), or with that
+API directly.
+The Project owns one tenant and one principal; its keys share assets and permissions.
+Writes retain each key's provenance. All Projects and application keys live in
+PostgreSQL, independently of deployment configuration.
+
+Issuance returns plaintext once; Core stores its digest. Deliver the new key to the
+application privately. Rotate by issuing a replacement in the same Project and
+revoking the old key. Archive disables every key while retaining assets and admitted
+work. Ordinary metadata reads cannot recover plaintext.
+
+## Verification and diagnosis
+
+1. Core `/healthz` proves process liveness only.
+2. Console login followed by `GET /core/v1/projects` proves the authenticated
+ browser-to-console and console-to-Core path.
+3. A Project key must work on its public resources and fail on management routes.
+ The Core key must fail on `/v1`; `/v1` through the console stays 404.
+4. Cross-origin management writes must be rejected. Audit actor labels must ignore
+ a forged browser header.
+5. Runtime observations and history report execution state separately from the
+ sandbox deployment read (`GET /core/v1/sandbox/deployment`). Neither a login nor
+ a successful deployment read proves model or sandbox readiness.
+
+A console login failure belongs to console authentication. An upstream 401 on a
+management request points to the console's Core key or Core connection. A resource
+deletion conflict must remain visible; it does not authorize an execution call.
+Node and daemon `/api/v1` routes and native Runtime interfaces retain their own authentication.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/web/core-connection.md)
diff --git a/apps/docs/content/docs/bootstrap-projects-keys.zh.mdx b/apps/docs/content/docs/bootstrap-projects-keys.zh.mdx
new file mode 100644
index 000000000..1c1dd494e
--- /dev/null
+++ b/apps/docs/content/docs/bootstrap-projects-keys.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Sign in and issue application keys · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+使用 Core key 登录 Web。应用开发者在项目中获取 Project API key,设置 OPENAI_BASE_URL 与 OPENAI_API_KEY。Core key 不能用来调用 /v1,应用 key 也不能调用管理或机器接口。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/bootstrap-projects-keys)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/concepts.mdx b/apps/docs/content/docs/concepts.mdx
new file mode 100644
index 000000000..cd2b5bd00
--- /dev/null
+++ b/apps/docs/content/docs/concepts.mdx
@@ -0,0 +1,88 @@
+---
+title: "Concepts and ownership"
+description: "Projects, credentials and the boundary between applications, administration and execution."
+---
+
+OpenAgentCore is an open-source implementation of the OpenAI Agents API. Its public
+contract follows the repository's pinned upstream baseline; documented native
+harness differences remain explicit. Core extensions must not silently change
+upstream resource shapes or execution semantics.
+
+## Three namespaces, three credentials
+
+Core serves three namespaces, each with one kind of caller and credential:
+
+- `/v1`: applications with a Project API key. Exactly the pinned official routes;
+ Core-only fields live only in `x_agents_core`.
+- `/core/v1`: Core Web's server and operator scripts with the Core key. Everything
+ about operating the deployment, including credential issuance.
+- `/api/v1`: nodes, Runtime daemons and self-hosted executors with machine
+ credentials issued through `/core/v1`; each works only on its own routes.
+
+Core enforces the separation: the Core key cannot call `/v1`, API keys cannot call
+`/core/v1`, and neither works on `/api/v1`. Web calls only `/core/v1` and keeps
+the Core key on its server; users sign in to Web with it.
+
+This is a separation of API authority, not a claim that a deployment administrator
+cannot possess application credentials. An administrator can issue an API key and
+use it separately. The console itself has no impersonation or execution operation.
+API-key plaintext is returned once on issuance, never on reads.
+
+## Projects own assets
+
+A Project owns one execution tenant and its assets. Multiple named API keys belong
+to the Project and share its principal, permissions and resources. Each write keeps
+its actual key identity for provenance. There are no Core users, roles, memberships
+or read-only keys. Names are labels; stable IDs identify Projects and keys.
+
+Projects and application API keys live only in PostgreSQL. The console and
+management API use the same records. Configuration contains deployment settings,
+not application credentials or Project definitions. Issue a new key in the same
+Project and revoke the previous one to rotate credentials. Revocation affects only
+that key and preserves assets, provenance and accepted execution. Explicit Project
+archive revokes every key and blocks new issuance in that Project.
+Administrators can still inspect and delete resources in archived Projects.
+
+Store only API-key digests and necessary metadata. Return plaintext once, on
+issuance, and never replay it after an uncertain response. Project/key mutations
+and administrator audit commit together. Database authentication failures fail
+closed. The Core key is managed separately, in deployment configuration.
+
+Parsar product identities, workspaces, business permissions and collaboration
+remain outside Core. Parsar is an ordinary API-key holder in a Project.
+
+## Resource isolation and administrator authority
+
+Agent API reads, writes and references are scoped to the authenticated key's Project.
+A foreign resource remains indistinguishable from an absent resource under the
+existing public operation's rules. Deployment nodes, configured model endpoints
+and startup settings are deployment infrastructure, not shared business assets.
+
+Administrators can inspect resources and execution history, delete resources under
+the same rules as their public deletion operations, manage Projects and API keys,
+issue node enrollment tokens and self-hosted executor credentials, and query
+operational counts and usage. They cannot use management endpoints to
+create, copy or edit arbitrary assets, start a Session, send an event, cancel
+work, or read stored credentials. Project keys share all assets within their
+Project; nothing is shared or copied across Projects.
+
+## Secrets and evidence
+
+Credential values, model credentials and confidential template initialization are
+write-only through resource APIs, including administrator reads. Public metadata
+projections are reused by the management API. This does not redact arbitrary
+user-authored conversation text, Skill source or Artifact content: administrators
+who inspect those records see their recorded contents.
+
+Public writes retain their actual API-key provenance. Administrator writes retain
+a separate audit identity and target Project. The console's fixed actor label is
+display-only; Core trusts the Core key, not that forwarded label. Audit failure rolls
+back the business transaction. Reads are not audited. Resources from the removed
+copy operation keep their `admin_copy` ownership, distinct from historical unknown
+ownership. No request bodies, secrets or file contents enter audit records.
+
+Do not add product users, RBAC, cross-Project shared assets, administrator execution, or old
+private-protocol compatibility to this management model. Existing public
+execution and Runtime ownership rules remain in [CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/CONTRIBUTING.md).
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/design-principles.md)
diff --git a/apps/docs/content/docs/concepts.zh.mdx b/apps/docs/content/docs/concepts.zh.mdx
new file mode 100644
index 000000000..c0fc452ec
--- /dev/null
+++ b/apps/docs/content/docs/concepts.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Concepts and ownership · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+Project 拥有应用资源和执行身份。同一项目的 API keys 共享资源;Core key 只用于管理。Core 负责执行事实,Web 不复制执行所有权。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/concepts)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/configure.mdx b/apps/docs/content/docs/configure.mdx
new file mode 100644
index 000000000..11653093f
--- /dev/null
+++ b/apps/docs/content/docs/configure.mdx
@@ -0,0 +1,253 @@
+---
+title: "Configuration reference"
+description: "The operator config.json, generated files and database-owned runtime settings."
+---
+
+Every setting of a Core installation has exactly one home. There are two kinds:
+
+| Kind | Examples | Home | Change it with | Takes effect |
+| --- | --- | --- | --- | --- |
+| [Process settings](#process-settings-configjson) | Public URL, ports, logging, harnesses, execution concurrency, audit retention, OAuth origins, database pool, Runtime history export | `config.json` in the installation directory (default `~/.oac/core`) | Edit the file, then run `oac apply` | `oac apply` restarts the services that read the changed settings |
+| [Runtime settings](#runtime-settings-web) | Sandbox backend and size, nodes, Projects and keys, default models, executor credentials | Core's PostgreSQL database | Web, or the Core API (`/core/v1`) with the Core key | At once, without a restart |
+
+Web's **System** page shows both: the installation's addresses, the process settings
+read-only under **Startup settings** with the path of `config.json` and the apply
+command, the default models, and the sandbox configuration. Secrets live in
+[`secrets/`](#secrets-and-identity), one copy each. Each setting is set in one place;
+the files in `generated/` are only derived from `config.json`. No configuration file
+defines Projects or API keys.
+
+## Process settings: config.json
+
+The installer writes every setting that applies to the installation's
+[mode](/install#modes), so the file shows each value. Installer flags
+such as `--public-url`, `--core-port` and `--web-port` only seed it. To change a
+setting, edit the file and apply it:
+
+```sh
+~/.oac/core/oac apply --dry-run # show the changed settings, files and restarts
+~/.oac/core/oac apply
+```
+
+### How oac apply works
+
+1. It validates `config.json` and changes nothing if a value is invalid. `mode` and
+ `native_core` are fixed after installation; to change them, install into a new
+ directory.
+2. It writes the files Core, Web and Compose read into `generated/`: `compose.json`,
+ `core.env`, `core-key-digests.json`, `settings.json` and, when used,
+ `runtime-history.json` and the native Core unit. Don't edit them. A generated file
+ edited by hand stops `apply` until you move the change into `config.json` and run
+ `oac apply --discard-edits`, which keeps the edited copy as
+ `generated/.edited-`.
+3. It compares what it wrote with what actually runs. Each container carries a digest
+ of its inputs (the `io.oac.inputs` label; native Core carries `OAC_INPUTS`),
+ and `apply` recreates or restarts exactly the services whose inputs differ: Core
+ first, then Web. The **Restarts** column below says which services a setting
+ affects. Restarting Web signs everyone out of the console.
+4. While any service runs, `apply` also starts the stopped ones. After `oac stop`,
+ it only writes the files and the installation stays stopped.
+5. If Core refuses a value at startup, `apply` prints Core's startup error. If every
+ service was running with the previous files, it restores them and starts the
+ services again; otherwise it reports the failure, and the next `apply` finishes
+ the work.
+
+`oac status` reports changes to `config.json` that are not applied yet and
+generated files edited by hand.
+
+### Changing the public URL
+
+`public_url` is the one origin that applications, nodes, sandboxes and self-hosted
+executors use; Core derives the daemon WebSocket URL, the self-hosted `remote_url` and
+each sandbox's connection address from it. With `null`, Core uses
+`http://127.0.0.1:` and only this host can reach it.
+
+You can set or change it at any time with `oac apply`. When nodes, hosted
+sandboxes or self-hosted executors are bound to the current address, `apply` lists
+them and asks you to type the new URL (`--confirm-public-url-change URL` when not
+interactive). Afterwards, nodes on the old address get no new sandboxes: remove them
+in Web and add them again. Existing sandboxes and executors keep working only while
+the old address still reaches this Core, and `apply` warns that self-hosted executors
+must restart with the new `remote_url`. Their installer refuses to reuse an
+installation made for the old address, and moving an executor isn't supported yet:
+create new self-hosted Sessions and connect their hosts again. Update your reverse
+proxy first.
+
+### Settings
+
+`core.runtime_history.headers` may hold export credentials. They stay in the
+`0600` `config.json` and the generated file Core reads, and never appear in `oac`
+output or in Core's settings snapshot. Model providers are not process settings; see
+[Default models](#default-models).
+
+
+| Key | Type | Default | Modes | Change | Restarts | Meaning |
+| --- | --- | --- | --- | --- | --- | --- |
+| `$schema` | string | none | all | any time | none | Editor hint that points at the installed copy of this schema. Ignored. |
+| `format` | `1` | none | all | fixed | none | Configuration format. Only an upgrade changes it. |
+| `mode` | `"all"` \| `"core-only"` \| `"web-only"` | `"all"` | all | fixed | none | Which services this installation runs. Install flag: `--core-only` or `--web-only`. |
+| `native_core` | boolean | `false` | `all`, `core-only` | fixed | none | Run Core as a systemd user service instead of a container. Install flag: `--native-core`. |
+| `public_url` | string or null (canonical origin; HTTP only on loopback) | `null` | all | `oac apply` | core, web | Public origin of Core and Web behind your TLS reverse proxy, such as https://core.example. Nodes, sandboxes and self-hosted executors use it. null means local access only through http://127.0.0.1. Install flag: `--public-url`. |
+| `ports.core` | integer 1024–65535 | `8091` | `all`, `core-only` | `oac apply` | core (core, web with native Core) | Loopback port of the Core API. With native Core, Web follows it. Install flag: `--core-port`. |
+| `ports.web` | integer 1024–65535 | `8080` | `all`, `web-only` | `oac apply` | web | Loopback port of Web. Install flag: `--web-port`. |
+| `ports.database` | integer 1024–65535 | none | `all`, `core-only` | `oac apply` | database, core | Loopback port of PostgreSQL. Present exactly when native_core is true; the installer picks a free port. |
+| `web.core_url` | string (canonical origin; HTTP only on loopback) | none | `web-only` | `oac apply` | web | Origin of the Core that this Web connects to: HTTPS, or HTTP on a loopback host. Install flag: `--core-url`. |
+| `log.level` | `"debug"` \| `"info"` \| `"warn"` \| `"error"` | `"info"` | all | `oac apply` | core, web | Minimum log level of Core and Web. |
+| `log.format` | `"auto"` \| `"text"` \| `"json"` | `"auto"` | all | `oac apply` | core, web | Log format. auto writes text to a terminal and JSON otherwise. |
+| `log.add_source` | boolean | `false` | all | `oac apply` | core, web | Add the source file and line to each log record. |
+| `core.execution_concurrency` | integer 1–1024 | `4` | `all`, `core-only` | `oac apply` | core | Concurrent execution work units in Core. Unrelated to node sandbox capacity. |
+| `core.harnesses` | array of `"claude_sdk"` \| `"codex"` \| `"mcode"` | `["claude_sdk", "codex", "mcode"]` | `all`, `core-only` | `oac apply` | core | Harnesses that Sessions may select. |
+| `core.default_harness` | `"claude_sdk"` \| `"codex"` \| `"mcode"` | `"codex"` | `all`, `core-only` | `oac apply` | core | Harness used when a Session names none. It must be listed in core.harnesses. |
+| `core.write_audit_retention` | string (Go duration, at least `1h`) | `"2160h"` | `all`, `core-only` | `oac apply` | core | How long non-creation write history is kept, as a Go duration of at least 1h. |
+| `core.oauth_trusted_origins` | array of string (canonical HTTPS origin) | `[]` | `all`, `core-only` | `oac apply` | core | Extra HTTPS origins trusted as private OAuth issuers. |
+| `core.database_pool.max_conns` | integer or null ≥ 1 | `null` | `all`, `core-only` | `oac apply` | core | Maximum database connections. null keeps the driver default, max(4, CPU count). |
+| `core.database_pool.min_conns` | integer or null ≥ 0 | `null` | `all`, `core-only` | `oac apply` | core | Minimum idle database connections. null keeps the driver default, 0. |
+| `core.database_pool.max_conn_lifetime` | string or null (Go duration) | `null` | `all`, `core-only` | `oac apply` | core | Go duration. null keeps the driver default, 1h. |
+| `core.database_pool.max_conn_idle_time` | string or null (Go duration) | `null` | `all`, `core-only` | `oac apply` | core | Go duration. null keeps the driver default, 30m. |
+| `core.database_pool.health_check_period` | string or null (Go duration) | `null` | `all`, `core-only` | `oac apply` | core | Go duration. null keeps the driver default, 1m. |
+| `core.runtime_history` | object or null | `null` | `all`, `core-only` | `oac apply` | core | Runtime history collection and OTLP export. null keeps local collection with Core's defaults. Core checks the values at startup. |
+| `core.runtime_history.transport` | string | none | `all`, `core-only` | `oac apply` | core | OTLP export transport. |
+| `core.runtime_history.endpoint` | string | none | `all`, `core-only` | `oac apply` | core | OTLP collector endpoint. Omit it to keep history local. |
+| `core.runtime_history.insecure` | boolean | none | `all`, `core-only` | `oac apply` | core | Export without TLS. |
+| `core.runtime_history.headers` | object of string values | none | `all`, `core-only` | `oac apply` | core | Headers sent with each export, such as credentials. Never shown by oac or Core. Sensitive. |
+| `core.runtime_history.queue_capacity` | integer | none | `all`, `core-only` | `oac apply` | core | Export queue capacity. |
+| `core.runtime_history.timeout_seconds` | integer | none | `all`, `core-only` | `oac apply` | core | Export and query timeout in seconds. |
+| `core.runtime_history.sample_interval_seconds` | integer | none | `all`, `core-only` | `oac apply` | core | Periodic sampling interval in seconds. |
+
+
+The schema is
+[`deploy/install/config.schema.json`](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/deploy/install/config.schema.json); each
+installation keeps a copy in `generated/config.schema.json` for editors. Core serves
+the non-secret settings snapshot, with the path of `config.json` and the apply
+command, at `GET /core/v1/installation`.
+
+## Runtime settings: Web
+
+Runtime settings live in Core's database. Change them in Web; scripts use the same
+Core API with the Core key.
+
+| Setting | Where in Web | Core API | Notes |
+| --- | --- | --- | --- |
+| Sandbox backend: Docker, microsandbox or E2B | **Nodes** (**Sandbox backend** with E2B): the setup wizard, ending with **Save configuration** | `/core/v1/sandbox/deployment` | One backend per deployment. `install.sh --sandbox` saves the first choice. To change it, see the Nodes page; that flow will change in a coming release |
+| Sandbox size and Runtime release | **Nodes**: the setup wizard | `/core/v1/sandbox/deployment` | Every sandbox gets the same size. See [Sandbox deployment](#sandbox-deployment) |
+| E2B API key and template build | **Nodes**: the setup wizard's **E2B cloud** (the page is then called **Sandbox backend**) | `/core/v1/sandbox/deployment` | The key is write-only and encrypted |
+| Nodes and their capacity | **Nodes**: **Add node**, **Edit node**, **Remove node** on a node's page (**Remove** in its list row) | `/core/v1/sandbox/enrollment-tokens`, `/core/v1/sandbox/nodes` | See [Node capacity](#node-capacity) and the [nodes guide](/hosted-providers) |
+| Projects and API keys | **Projects and keys**: **Create project**, **Rename**, **Issue key**, **Revoke**, **Archive** | `/core/v1/projects` | Keys are shown once; Core stores digests |
+| Default model per harness | **System**: **Default model** | `/core/v1/harnesses/{harness}/model-provider` | See [Default models](#default-models) |
+| Executor credentials of a self-hosted Session | **Session log**, the Session's page: **Executor credentials** | `/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` | See [self-hosted executors](/self-hosted-execution) |
+
+Which harnesses are enabled, and the default one, are process settings
+(`core.harnesses`, `core.default_harness`); System shows them read-only. The
+[API index](/public-api#core-api) lists every Core API route.
+
+### Sandbox deployment
+
+Exactly one provider serves the deployment: Docker, microsandbox or E2B. The
+deployment specification holds the per-sandbox resources and, for Docker and
+microsandbox, the Runtime release. Web's setup proposes the Standard size from
+[`standard-sizes.json`](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/apps/web/src/features/sandbox/standard-sizes.json) and
+derives Small and Large from it; the installer applies the bundle's copy of the same
+file. The database owns the saved values.
+
+| Setting | Standard | Meaning |
+| --- | --- | --- |
+| `resources.cpus` | 2 | Whole vCPUs per sandbox, 1 through 255 |
+| `resources.memory_mib` | Docker 2048, microsandbox 4096 | MiB per sandbox, 512 through 1048576 |
+| `resources.root_disk_mib`, `environment_disk_mib` | microsandbox 8192 each | At least 1024 MiB; microsandbox only |
+| `runtime` | The bundle's Runtime release | Source revision, exact image ID and manifest, and Runtime and firmware hashes; never a mutable tag |
+| Idle suspension, snapshot retention | 300 and 86400 seconds | microsandbox only; fixed |
+
+E2B takes no `runtime`, and Web sends no resources for it: Core adopts the CPU and
+memory of the ready template build `template-id:build-uuid`, and supplied values must
+match it. Docker has no separate disk quota.
+
+Changing the provider, the size or the Runtime applies to the whole deployment; see the
+**Nodes** page, and note that this flow will change in a coming release. A successful
+change retires the old nodes and enrollment commands; history stays, and existing
+Sessions never move between providers. See the
+[operator reference](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-maintenance)
+and the [deployment contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/sandbox-deployment.md).
+
+### Node capacity
+
+Core approves a node's capacity when you generate its Add node command: **Sandboxes at
+once** (`max_active`, default 2) and, for microsandbox only, **Retained sandboxes**
+(`max_retained`, default 8), with `max_retained >= max_active >= 1`. Docker never
+suspends sandboxes, so Web doesn't ask for it and Core keeps `max_retained` equal to
+`max_active`. Change them later with **Edit node**. Reservations and cleanup that is
+not confirmed count against capacity; lowering a limit stops no running sandbox. A
+node's own files can't change its capacity, size or Runtime.
+
+`core.execution_concurrency` is unrelated: it limits concurrent execution work in Core.
+
+### Default models
+
+Each harness has at most one default model provider: `protocol`, HTTPS `base_url`,
+a write-only `api_key` and, for MiniMax Code, `context_window` and
+`max_output_tokens`. It is the same bundle as `x_agents_core.model_provider`. Core
+encrypts it with `secrets/credential.key` and never returns the key.
+
+New Core-hosted (`openai_hosted`) Sessions and Sessions without an environment
+(`none`) use it when neither the request nor the Agent carries a provider; each
+Session freezes the provider it starts with, so a later change affects only new
+Sessions. Self-hosted Sessions never use it. A hosted or self-hosted Session with no
+provider is refused with 400 `model_provider_required`. See
+[model providers](/quickstart#model-providers) and the
+[model execution contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/model-execution.md#deployment-defaults).
+
+The operator file `AGENTS_API_EXECUTION_OPTIONS_FILE` is retired; see
+[Upgrade notes](/troubleshooting#upgrade-notes).
+
+## Secrets and identity
+
+| File | Content | Changed by |
+| --- | --- | --- |
+| `secrets/core.key` | The [Core key](/troubleshooting#core-key) | `oac rotate-core-key` |
+| `secrets/credential.key` | Encryption key for what Core stores sealed in the database: model providers, the E2B key, Vault credentials, Skills, initial files and environment setup | Nothing. Keep it with the database; `oac apply` refuses a changed file |
+| `secrets/database.password` | PostgreSQL password | Nothing. PostgreSQL reads it only when the database is created; `oac apply` refuses a changed file |
+| `state.json` | Installation ID, Compose project, image IDs, source commit and the digests of generated files | The tools only |
+
+A Web-only installation has only `secrets/core.key`, a copy of its Core's key.
+
+## Appendix: Core environment without the installer
+
+Core reads only its environment. The installer renders `generated/core.env` from
+`config.json`; if you run Core yourself (see
+[Maintainers and advanced deployments](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/maintainers.md)), set these variables. Compose
+loads the file with `env_file` and systemd with `EnvironmentFile`, so Compose must be
+2.26.0 or newer.
+
+| Variable | Set from |
+| --- | --- |
+| `OAC_PUBLIC_URL` | `public_url`, or Core's loopback origin. Core derives the daemon WebSocket URL, the self-hosted `remote_url`, the hosted sandbox address and the deployment's read-only `core_url` from it. Required with `OAC_INSTALLATION_ID` |
+| `OAC_ADDR` | `ports.core` (native Core) or the container port |
+| `OAC_DATABASE_URL` | The installation's PostgreSQL without a password, plus `core.database_pool` as `pool_*` query parameters |
+| `OAC_DATABASE_PASSWORD_FILE` | `secrets/database.password`. The URL must then carry no password; migrations and the maintenance commands read the file too |
+| `OAC_CREDENTIAL_KEY_FILE` | `secrets/credential.key` |
+| `OAC_CORE_KEY_DIGESTS_FILE` | `generated/core-key-digests.json`: a JSON array with the SHA-256 of the Core key |
+| `OAC_INSTALLATION_ID` | `state.json`; pinned to the database |
+| `OAC_SETTINGS_FILE` | `generated/settings.json`, the snapshot Core serves at `GET /core/v1/installation`; Core does not act on it |
+| `OAC_EXECUTION_CONCURRENCY`, `OAC_DEFAULT_HARNESS`, `OAC_HARNESSES`, `OAC_WRITE_AUDIT_RETENTION`, `OAC_OAUTH_TRUSTED_ORIGINS` | The matching `core.*` settings |
+| `OAC_HISTORY_SETTINGS_FILE` | `generated/runtime-history.json` when `core.runtime_history` is set; see the [history contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime-history-api.md) |
+| `OAC_LOG_LEVEL`, `OAC_LOG_FORMAT`, `OAC_LOG_ADD_SOURCE` | `log.*`; Web reads the same three |
+| `OAC_E2B_STATE_DIR`, `OAC_E2B_PROVIDER_BIN` | The E2B receipt directory and, for native Core, the bundled helper. The E2B key and template live in the database |
+
+A Web you run without the installer reads the variables in
+[Connecting the administrator console to Core](/bootstrap-projects-keys#server-configuration-and-login),
+plus `OAC_WEB_NODE_PAYLOAD_DIR`: the absolute path of the matched distribution's
+node payload (the installer's `node-payload/`). Without it, Add node and the
+self-hosted install command are unavailable.
+
+Core fails at startup, naming the replacement, while a retired variable is set:
+`AGENTS_API_DAEMON_WS_URL` (use `OAC_PUBLIC_URL`), `AGENTS_API_CONFIG_FILE` (no
+replacement), `AGENTS_API_EXECUTION_OPTIONS_FILE` (use
+[default models](#default-models)), `AGENTS_API_SANDBOX_ADMIN_DIGESTS_FILE` (use
+`OAC_CORE_KEY_DIGESTS_FILE`), and `AGENTS_API_MANAGED_RUNTIMES_FILE`,
+`AGENTS_API_SANDBOX_NODE_STATE_DIR` and `AGENTS_API_SANDBOX_NODE_CORE_URL` (the
+sandbox deployment lives in the database and nodes enroll separately). Core logs the
+file paths it loads, never environment values or file contents. Native installation
+paths must be canonical absolute paths without control characters, quotes, backslashes
+or wildcards. Keep the installation ID and the database together; Core refuses a
+missing installation ID when its database already has a deployment.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/configuration.md)
diff --git a/apps/docs/content/docs/configure.zh.mdx b/apps/docs/content/docs/configure.zh.mdx
new file mode 100644
index 000000000..8f0c525b7
--- /dev/null
+++ b/apps/docs/content/docs/configure.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Configuration reference · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+安装配置的唯一编辑入口是 config.json;运行 oac apply 应用变更。generated 目录由安装器生成。运行时后端、节点和默认模型由 Web 管理并保存在 PostgreSQL 中。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/configure)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/console.mdx b/apps/docs/content/docs/console.mdx
new file mode 100644
index 000000000..de30947cf
--- /dev/null
+++ b/apps/docs/content/docs/console.mdx
@@ -0,0 +1,74 @@
+---
+title: "Administrator console"
+description: "Monitor Core, inspect resources and manage deployment settings through Web."
+---
+
+Core Web is the administrator console for a Core deployment. Its Go service
+provides Core key login and forwards signed-in, same-origin `/core/v1` requests to
+Core. Applications use Core's public Agents API directly with their own Project API
+keys.
+
+The React console (`apps/web`) uses this contract: every browser request goes through
+the console's same-origin management routes with `AdminClient` and the sandbox
+management client, and it sends nothing to `/v1`.
+
+
+
+## Console pages
+
+| Group | Page | Purpose |
+| --- | --- | --- |
+| Monitor | Overview | Service status, running Sessions, sandbox slots and work needing attention; 24-hour Session activity; Core and its nodes as a topology, each with a popover glance; Sessions needing attention; usage by Project |
+| Monitor | Core metrics | The Core process: execution slots, the Turn queue, connected daemons, database latency and pool, background jobs |
+| Monitor | Agent metrics | Requests, errors, duration, tokens, models, tools, Agents and API keys over 1 h, 6 h, 24 h or 7 d |
+| Monitor | Sandbox metrics | Node capacity and hosted Runtime CPU and memory across Projects |
+| Monitor | Session log | Every Session, opening one Session's read-only conversation, trace and Turns; a self-hosted Session's page also manages its executor credentials and gives the command that connects a host |
+| Resources | Agents, Environment templates, Skills, Files, Vaults | Inspection and permitted deletion |
+| Platform | Projects and keys, Nodes, System | Project and key lifecycle; sandbox deployment and nodes; System: the installation's public address, API base URL, ID and source commit (read-only), each harness's default model provider (write-only key) beside its read-only startup state, the sandbox configuration every Project shares: provider, sandbox size, Runtime or E2B template build, idle suspension and maintenance, and Core's config.json startup settings with where to change them |
+
+Missing data is shown as missing (—), never as zero. How each figure is read and
+bounded is recorded in [management interface coverage](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/web/protocol-coverage.md).
+
+## Management scope
+
+Administrators can create, rename and archive Projects; issue and revoke their
+keys; inspect resources and execution history; delete supported resources; and
+issue, rotate and revoke the executor credentials of a self-hosted Session's
+environment on its Session page.
+They can also read summaries, Runtime observations and audit history, and manage
+deployment sandbox nodes. Deployment sandbox management selects E2B, Docker or
+microsandbox; caller-managed `self_hosted` Runtimes remain a separate application
+path.
+
+A Project owns one tenant and one principal. All its keys share assets and
+permissions; writes record the individual key as provenance. Projects and keys
+live only in PostgreSQL. Key issuance returns plaintext once and stores its digest.
+Rotate by issuing another key and revoking the old one. Archive disables every key
+in the Project while retaining assets and admitted work.
+
+The management API cannot create or edit arbitrary application resources, start
+Sessions, submit input or cancel execution. Applications perform those operations
+through `/v1`. Core has no API users, roles or memberships.
+
+## Connect and develop
+
+Follow the [installation guide](/install) for Core, Web and
+PostgreSQL with zero execution nodes. Installation creates no Project or application
+key; an administrator creates them on the console's **Projects and keys** page or
+through the management API. The browser signs in to the console with the Core key;
+only the console server sends it to Core.
+
+- [Connection and authentication](/bootstrap-projects-keys)
+- [Architecture and ownership](/execution-model)
+- [Management interface coverage](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/web/protocol-coverage.md)
+- [Frontend handoff and acceptance](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/web/roadmap.md)
+- [React application](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/apps/web/README.md)
+
+The [administrator API contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md) defines
+management routes and resource behavior. The [public API contracts](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md)
+define the separate application interface. See the [design principles](/concepts)
+for ownership and [contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/CONTRIBUTING.md) for required checks.
+
+OpenAgentCore Web is available under the [MIT License](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/LICENSE).
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/web/README.md)
diff --git a/apps/docs/content/docs/console.zh.mdx b/apps/docs/content/docs/console.zh.mdx
new file mode 100644
index 000000000..3f91dbe76
--- /dev/null
+++ b/apps/docs/content/docs/console.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Administrator console · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+Web 是管理员控制台,提供 Monitor、Resources 和 Platform 操作。浏览器使用管理会话;Core key 作为上游凭据保留在 Web 服务端。应用 API 的执行请求不经过 Web。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/console)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/environments-and-files.mdx b/apps/docs/content/docs/environments-and-files.mdx
new file mode 100644
index 000000000..ab045960d
--- /dev/null
+++ b/apps/docs/content/docs/environments-and-files.mdx
@@ -0,0 +1,218 @@
+---
+title: "Environments and files"
+description: "Execution placement, Environment resources and workspace ownership."
+---
+
+This assessment covers the fixed [Python SDK contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream.json), with partial
+coverage. Core-managed Docker runs the three qualified native harnesses. V1
+user-managed `self_hosted` enrollment uses the same colocated Runtime for Codex,
+Claude SDK and MiniMax at `/workspace`; see its [real deployment qualification](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/user-managed-runtime-v1.md).
+For hosted compute, the deployment selects E2B, Docker or microsandbox through
+[Hosted Sandbox Manager](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md).
+For the separate caller-managed E2B path, the user owns allocation, renewal and
+cleanup through the official SDK and [Runtime packaging](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/deploy/e2b/README.md).
+[Templates](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-templates.md) remain a hosted-only resource path;
+[Files](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-files.md) reuse the exact authorized local workspace.
+
+The internal Store now owns a durable Environment association for newly created
+`self_hosted` and `openai_hosted` snapshots, atomically with Session creation.
+It derives configuration and tenant ownership from the Session; retries preserve
+the existing identity. Scoped reads hide associations after Session deletion while
+retaining the underlying record. The public text profile reuses this association
+and the preparation/admission path below; additional provider profiles remain open.
+Missing/`none` configurations and historical internal snapshots gain no backfill.
+
+
+The private daemon gateway authenticates the enrolled Environment executor key and
+exact dedicated device. Existing Worker connection observations retain generation
+and revision fencing; registration and connectivity do not establish native
+readiness. Rotation/revocation, Session deletion and ownership loss deny further
+access without promising immediate cessation of native effects. Native history
+remains local to the bound Runtime and cannot be replaced on retry. There is no
+registry/Noise relay or transient service-side harness credential.
+See the [enrollment guide](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/README.md#user-managed-runtime-enrollment).
+
+## Basic public Docker-hosted profile
+
+An explicitly configured default managed Docker provider enables `type=openai_hosted`
+for the qualified Codex, Claude Code and MiniMax Code profiles. Each uses the same
+Runtime lifecycle and workspace interfaces with its own native adapter/isolation.
+See the [engine profile guides](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md#public-engine-profiles) for setup and limits.
+The standalone [operator configuration](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/deploy/codex/README.md#standalone-operator-configuration)
+selects the qualified immutable Runtime image; advertised capabilities alone do
+not enable admission. An idle or initial-text creation commits Session, Environment
+and retry identity before the existing leased Worker provisions its allocation.
+A committed creation interrupted before bootstrap is recovered without replaying
+an existing allocation's Create.
+
+Omitted/null network defaults to enabled. Enabled, disabled and exact-host restricted
+policies use the same qualified image with adapter-selected immutable native policy.
+Templates and inline configuration share initial files, env, packages, ordered setup
+and inline or tenant-owned referenced Skills through the hosted initializer.
+Unsupported hostname forms and installation combinations reject explicitly; see
+the [Template coverage and limits](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-templates.md). Empty/null installation
+defaults produce safe empty metadata, not a live workspace inventory. Service-origin
+hosted MCP remains unsupported; Environment Plugin MCP has its
+own qualified transport matrix.
+
+Initial provisioning leaves a Session idle until a Turn starts, with no caller
+connection action. The managed scan records authenticated, exactly bound daemon
+connections through existing fenced generations. Native preparation remains
+separate. Core restart preserves allocation/workspace/native identity; it does not
+blindly replay uncertain work. Terminal cleanup revokes authority and settles
+pending input atomically before external reclamation. Matching retries preserve
+outcomes; new inputs reject terminal Environments. Expiry has no invented SSE
+variant. Local failure codes and exact event ordering remain unverified upstream
+semantics; this profile does not establish complete Environment compatibility.
+
+## User-managed E2B profile
+
+The application creates, renews and destroys its E2B sandbox through the official
+SDK. It deploys the shared Runtime, then enrolls that Runtime into a `self_hosted`
+Session. Core neither keeps an E2B allocation nor issues Provider renew/kill calls.
+The [E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/deploy/e2b/README.md) owns packaging and
+user-side lifecycle instructions. Expiry or lost workspace/history must not trigger
+transparent replacement or replay. The [new enrollment qualification](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/user-managed-runtime-v1.md)
+records its own real deployment evidence.
+The [prior E2B qualification](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md#e2b-v1-qualification) records a historical
+Core-managed route; it does not establish this caller-managed enrollment result.
+Current deployment-managed E2B is a separate hosted configuration in the manager guide.
+
+## Initial public self-hosted profile
+
+Create a Session with `environment.type=self_hosted`,
+`workspace_directory: "/workspace"` and omitted/null/empty `capability_directories`. Creation
+accepts initial text as a string or ordered user-message array. Omitted/null input
+creates no Turn or connection action. Configured execution, an enabled harness and
+the exact local profile are validated
+before persistence. Supported optional functions remain engine-specific.
+
+Initial text commits a reservation and connection action, then returns the Session
+and Environment connection target while offline. Streamed creation sends the same
+committed projection as its `created` snapshot, already showing `requires_action`
+and the connection action, then the committed `requires_action` event. Like
+every fresh creation stream it ends right after the idle recorded when the
+admitted Turn ends or the reservation stops being pending, or after a failure;
+the connection alone clearing the action does not end it. A creation without
+input ends right after its created snapshot, and a same-key stream retry ends at
+once without events. The existing Worker prepares and admits the input; closing the stream
+leaves committed work intact.
+Initial expiry leaves a failed Session, safe error and empty actions without a
+Turn or an Environment failure. Creation retries preserve the original identity,
+deadline and input. Later live observers do not replay creation events.
+
+Later text-only batches recover their original reservation or direct receipt under
+the Session lock. New active messages append to the current Turn through existing
+ordered admission and native delivery; they create no Turn, preparation or reservation.
+If no Turn is active, reserve and wait for the existing Worker to retain native
+preparation, admit and claim. Return 204 only after that
+transaction commits. Connection actions precede a Turn and clear on connection;
+connection alone is not readiness. Retries preserve identity and the five-minute
+database deadline. HTTP disconnect retains the reservation. Local expired/cancelled
+outcomes return 409, lost ownership 503, and deletion 404; exact hosted error
+status/body and pending-input crash recovery are unverified. See the
+[canonical wait rules](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/CONTRIBUTING.md#environment-ownership-and-placement).
+
+Cancellation-only batches use the existing locked admission and native delivery.
+An idle cancellation creates no Turn, and retry identity preserves the original
+target during later work. Pending pre-Turn reservations still block new cancellation.
+HTTP 204 confirms admission, not native completion or OS quiescence. A cancellation
+before native Session transfer can still lack a final Outcome and conservatively
+fail; complete cancellation settlement remains open.
+Homogeneous function-result batches reuse scoped locked admission and native
+application receipts, without creating a Turn or preparation. Definitions use the
+existing function parser and remain fixed through native preparation and continuation;
+output/error field presence and ordered content keep their existing semantics.
+Retries retain the original call, including during later work. New results cannot
+bypass pending input. Function callbacks do not populate Environment installations.
+Mixed events, non-text input and nonempty capability directories remain unsupported.
+The public field types are unchanged; `/workspace` is the V1 deployment limit, not
+an upstream schema change. `remote_url` is the configured daemon WebSocket URL,
+returned unchanged. This is our private connection contract and does not claim
+stock `exec-server` compatibility. Service-origin HTTP MCP is explicitly rejected
+on `self_hosted`; `none` MCP and hosted Template Plugin MCP retain their own scope.
+
+Mechanism tests cover enrollment, credential checks and exact-device dispatch. They
+do not establish real public qualification. Before claiming that qualification,
+exercise fixed SDK/raw HTTP creation/input, actual native tools, Files/Artifacts,
+second-Turn history, Core/Runtime restart, cancellation and credential rotation/
+revocation/deletion on each declared deployment.
+
+## Contract inventory
+
+Paths below follow the SDK resource methods, before the service's `/v1` prefix.
+The authoritative fields and unions are linked to the pinned source; this table
+is an inventory, not a replacement schema.
+
+| Resource | Operations | Contract distinctions |
+| --- | --- | --- |
+| [Environment](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/resources/beta/agents/environments/environments.py) | `GET /agents/environments/{id}` | Created through Session configuration, with no standalone create/list/update/delete method in this resource. Safe metadata includes files, plugins, skills, type and status. |
+| [Template](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/resources/beta/agents/environments/templates.py) | `POST`, `GET /agents/environments/templates`; `GET`, `POST`, `DELETE /agents/environments/templates/{id}` | Reusable hosted configuration, resolved for each Session. List uses `after`, `limit` and `order`. Supplied update fields replace their value; omitted fields stay unchanged. Deletion includes confidential inputs. |
+| [Files](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/resources/beta/agents/environments/files.py) | `POST`, `GET /agents/environments/{id}/files` | Create accepts `file_id` or inline base64 data with an absolute path inside `/workspace`. List uses opaque `page`, not `after`, with stable path/order/limit across pages. |
+
+These are eight operations, separate from Session creation and live events.
+Templates use an `after` cursor and limit default 20, clamped to 1–100; file listing has nullable
+limit/path, non-null order/page when supplied, and case-sensitive path-component
+ordering. Both default to descending order. Do not reuse cursor decoding merely
+because both endpoints paginate.
+
+[Session environment input](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/types/beta/environment_param.py)
+and [output](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/types/beta/environment.py)
+have different shapes:
+
+- `none` selects no execution environment.
+- `self_hosted` input requires `type` and `workspace_directory`; its optional
+ nullable `capability_directories` defaults to an empty list. `remote_url` is
+ output-only. The output also includes the Environment ID and capability paths.
+ The output description's `/workspace` default does not make the input field
+ optional.
+- `openai_hosted` can reference a template and supply capability paths, network,
+ packages, files, plugins, skills, environment variables and setup commands.
+ Omitted template-backed values inherit; Session overrides cannot broaden the
+ template network policy. The service implementing this discriminator owns
+ provisioning; it does not rename the public discriminator for its provider.
+
+[Template responses](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/types/beta/agents/environments/environment_template.py)
+expose safe metadata, retaining unresolved skill version selectors and file
+references. They do not return inline file/archive contents, environment variables
+or setup command bodies. Nullability and replacement behavior must be checked
+against each request type, not inferred from these response models.
+
+| Projection | State vocabulary |
+| --- | --- |
+| [Environment resource](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/types/beta/agents/environment_info.py) | `pending`, `connected`, `disconnected`, `expired`, `failed` |
+| [Session environment event state](https://github.com/openai/openai-python/blob/d7c41efee1b0802b79f3f88a678ef2052b06e9ce/src/openai/types/beta/agent_session_environment_state.py) | `pending`, `ready`, `connected`, `disconnected`, `failed`; nullable error |
+
+These projections cannot share an unchecked string cast. Session environment
+notifications carry Session/Environment identity and optional Turn identity.
+The Session's `required_actions` union includes `environment_connection` with an
+Environment ID, separately from function calls. Environment readiness is distinct
+from Session and Turn status.
+
+## Ownership and placement decision
+
+The canonical [architecture rules](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/CONTRIBUTING.md#environment-ownership-and-placement)
+keep Environment lifecycle common while leaving process placement and native
+transport to adapters. Logical ownership does not require a machine per object.
+
+| Object | Responsibility |
+| --- | --- |
+| API Session and Environment | Durable tenant ownership, configuration, pending interaction and connection observations. |
+| Provider allocation | Compute and filesystem lifetime; caller-owned for `self_hosted`, service-owned for hosted provisioning. |
+| Device and daemon connection | Authenticated engine-host identity and replaceable internal dispatch transport. |
+| Harness process and native Session | Native model/tool loop, execution state and proven history/continuation path. |
+| Runtime enrollment | Exact Environment/device/key binding for user-managed compute; no service-owned allocation. |
+
+Daemon, harness, tools and workspace are colocated in V1. Our daemon fills the
+executor role; a separate native executor and service-side harness are retired.
+The pinned public resources remain the target, while stock executor wire
+interoperability is explicitly outside this implementation.
+
+Co-location needs a real credential and isolation design: generated code must not
+gain the broader application credential or cross-tenant secrets through a shared
+unrestricted process account. A directory binding alone is not isolation. Retain
+native history independently of disposable compute, or prove native restoration;
+never treat an Environment ID as a filesystem or history backup. Do not silently
+move an existing Session away from its bound device.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environments.md)
diff --git a/apps/docs/content/docs/environments-and-files.zh.mdx b/apps/docs/content/docs/environments-and-files.zh.mdx
new file mode 100644
index 000000000..26bfc7865
--- /dev/null
+++ b/apps/docs/content/docs/environments-and-files.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Environments and files · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+Environment 与 Session 一起创建。none、self_hosted、openai_hosted 是协议中的执行位置名称;openai_hosted 在这里表示 Core 管理的计算资源。文件和工作区仍由对应资源及执行所有者管理。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/environments-and-files)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/execution-model.mdx b/apps/docs/content/docs/execution-model.mdx
new file mode 100644
index 000000000..07b150217
--- /dev/null
+++ b/apps/docs/content/docs/execution-model.mdx
@@ -0,0 +1,111 @@
+---
+title: "Execution and API architecture"
+description: "Applications call the public API; the administrator browser calls Web; Runtime connects to Core."
+---
+
+
+
+Core Web manages a Core deployment. Applications, including Parsar, use the public
+Agents API independently with their own Project keys. The management backend,
+`AdminClient` and the React console built on them are implemented.
+
+The [design principles](/concepts) define identity and authority.
+The [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md) defines exact
+routes, payloads, pagination and audit records.
+
+## Request boundaries
+
+```mermaid
+flowchart LR
+ browser["Administrator browser"]
+ console["Core console service"]
+ core["Core API"]
+ database[("PostgreSQL")]
+ application["Application / official SDK"]
+ runtime["Runtime and native adapters"]
+
+ browser -->|"Same-origin management requests; console login"| console
+ console -->|"/core/v1/* by prefix, including sandbox; Core key"| core
+ application -->|"/v1; Project API key"| core
+ core <--> database
+ core <--> runtime
+```
+
+React management code must use the Core clients from `packages/agents-client`:
+`AdminClient` for `/core/v1`, `CoreMetricsClient` for `/core/v1/metrics` and the
+sandbox management client for `/core/v1/sandbox`. The console service
+returns 404 for `/v1` and `/api/v1`, including requests with an explicit Bearer
+token. It has no application key and does not impersonate the selected Project.
+
+The console signs the browser in with the Core key, checks the host and origin, and
+forwards every signed-in `/core/v1/*` request to Core by prefix; Core alone decides
+whether the route exists. It strips the browser's Authorization, Cookie, Origin and
+Referer headers and supplies the Core key as its private upstream credential. Core
+rejects application keys on management routes and the Core key on `/v1`.
+The audit actor label is declared by the caller and is display only, never Core authorization: the console server declares `console`, and operator scripts calling Core with the Core key directly leave it empty.
+
+Node and daemon connections use `/api/v1` with their own credentials. The reverse
+proxy sends them directly to Core; the console never forwards them, and they do not
+grant a browser execution authority.
+
+## Ownership
+
+| Component | Responsibility |
+| --- | --- |
+| React frontend | Project selection, permitted management actions and operational views; cached reads (TanStack Query) that keep the last data on screen while refreshing |
+| `AdminClient` | Typed management requests and validation, sharing resource parsers with the public client |
+| `services/core-console` | Core key login, host/origin checks, and prefix forwarding of `/core/v1/*` with the Core key as the private upstream credential |
+| Core API and PostgreSQL | Project isolation, resource state, deletion preconditions, audit and scheduling |
+| Runtime and native adapters | Existing allocation, process lifecycle and execution protocols |
+
+A Project owns one tenant and one principal; its keys have equal access to its
+assets. Projects and keys are database records. Configuration contains deployment
+settings, not business identities. Core has no separate API-user or role model.
+
+Revoking one key prevents new authentication without removing assets or admitted
+work. Archiving a Project disables all its keys and retains resources for
+administrator inspection and deletion.
+
+Management adds no execution path. It can inspect metadata and history and apply
+existing deletion rules. It cannot edit arbitrary resources, create Sessions, send
+input or cancel work. A deletion conflict cannot be resolved by an implicit
+cancellation from the console.
+
+Secret fields remain write-only; Skill source and Artifact content have explicit
+read routes, while Source File content does not have an administrator download
+route.
+
+## Deployment and application Runtime paths
+
+Deployment sandbox management selects one provider at a time: E2B, Docker or
+microsandbox. E2B uses the deployment's provider integration; Docker and microsandbox
+use operator-managed machines. Provider setup, maintenance and node administration
+belong to the existing sandbox management surface.
+
+An application's `self_hosted` Runtime, including one it provisions in its own E2B
+account, is a separate caller-managed path. It does not choose or reconfigure the
+deployment provider. This console contract changes neither native Runtime protocols
+nor application Session creation semantics.
+
+## Frontend state and validation
+
+Session inspection uses paginated durable history and bounded polling. There is no
+management Session SSE endpoint. Project changes must discard stale reads and
+pending operation state before displaying results in another Project.
+
+The client sends each write once per explicit action. An uncertain result stays
+visible until the administrator checks state and decides how to proceed. Issued
+key plaintext must not enter browser storage or logs. Key issuance recovery
+follows the administrator contract.
+
+The sandbox deployment read (`GET /core/v1/sandbox/deployment`) describes the saved
+selection. It does not prove a reachable model, valid provider credentials or
+execution readiness. Runtime observations, usage coverage and audit history must
+retain the distinctions defined by Core. In E2B views, the running sandbox count
+comes from the deployment's allocations; the hosted Runtime total counts hosted
+observation records across projects and reported lifecycle states. These sources
+have different coverage and refresh independently, so the console does not infer
+resource retention or cleanup from their difference.
+Native execution ownership remains governed by [CONTRIBUTING.md](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/CONTRIBUTING.md).
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/web/architecture.md)
diff --git a/apps/docs/content/docs/execution-model.zh.mdx b/apps/docs/content/docs/execution-model.zh.mdx
new file mode 100644
index 000000000..d331de11c
--- /dev/null
+++ b/apps/docs/content/docs/execution-model.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Execution and API architecture · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+应用直接用 Project API key 调用 Core 的 /v1。管理员浏览器通过 Web 会话操作管理界面,Web 服务端持有 Core key。节点和 Runtime 用专属凭据直接连接 Core。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/execution-model)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/hosted-providers.mdx b/apps/docs/content/docs/hosted-providers.mdx
new file mode 100644
index 000000000..7b8638a06
--- /dev/null
+++ b/apps/docs/content/docs/hosted-providers.mdx
@@ -0,0 +1,309 @@
+---
+title: "Nodes and hosted providers"
+description: "Add and remove Docker or microsandbox nodes; diagnose connection and readiness."
+---
+
+A node is a Linux machine that runs sandboxes for Core-hosted Sessions when the
+sandbox backend is Docker or microsandbox. Core places each new Session on a node with
+free capacity; the node creates the sandbox, and the sandbox connects back to Core.
+With E2B you need no nodes. Self-hosted executors, which applications run for their
+own Sessions, are a different thing; see [Self-hosted executors](/self-hosted-execution).
+
+You add a node by generating a command in Web and pasting it on the host. The
+[operator reference](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md) covers the
+node protocol, manual registration, placement and failure handling.
+
+## Before you add a node
+
+- **Core has an HTTPS public URL** that the host and its sandboxes can reach: each
+ sandbox calls Core at `public_url`. A loopback installation can't have nodes, and
+ Web says so at Add node. See
+ [HTTPS and the reverse proxy](/install#https-and-the-reverse-proxy).
+- **Web holds the node files.** Install from the offline bundle, or add the release's
+ node files to the smaller bundle; see
+ [Download a release](/install#download-a-release). Otherwise Add node says that
+ the console has no node files for the provider.
+- **A sandbox backend is chosen.** The installer selects microsandbox unless you chose
+ otherwise. After `--sandbox none`, the **Nodes** page first asks you to choose
+ **Own machines**, then microsandbox (preselected as recommended) or Docker, which it
+ asks you to confirm, and a sandbox size, then **Save configuration**. Every node of a
+ deployment uses that provider.
+
+## Add a node
+
+1. In Web, open **Nodes** and select **Add node**.
+2. Set **Sandboxes at once** (default 2): how many sandboxes Core may run on this node.
+ With microsandbox, also set **Retained sandboxes** (default 8): how many it may keep,
+ suspended ones included. With Docker, Core keeps retained equal to at once. You can
+ change them later with **Edit node**.
+3. Select **Generate command** and copy the command. It registers one node, once, and
+ only if it runs within 10 minutes; Web counts down and offers
+ **Generate new command** when it expires.
+4. Run it on the host. Web follows the node from registered to connected to ready.
+
+The command downloads the node installer from your console, checks its SHA-256, and
+runs it with a one-time enrollment token. The installer downloads the node files from
+the same console and checks each against the release manifest, imports the Runtime
+image, registers the node, starts its service and waits until Core reports the node
+connected and ready. It never installs software, and it stops with a one-line hint
+before changing anything when a prerequisite is missing.
+
+Web's command runs the installer with sudo: it prepares the host itself, creating a
+`oac-node` service user and a system service. On a host where you have no sudo, the
+dialog's **No sudo on this host?** section gives the same command without sudo; the
+node then runs as a user service of a user that an administrator prepared.
+
+### The command
+
+This is the command Web generates, with this installation's values:
+
+```sh
+ (umask 077; d=$(mktemp -d) || exit; trap 'rm -rf "$d"' EXIT; s=; [ "$(id -u)" -eq 0 ] || s=sudo
+curl -fsS --max-time 30 --max-filesize 1048576 'https://core.example/node-install/node-install.pyz' -o "$d/node-install.pyz" &&
+printf '%s %s\n' '' "$d/node-install.pyz" | sha256sum -c --status &&
+printf '%s\n' '' | $s python3 "$d/node-install.pyz" --enrollment-token-stdin --source-url 'https://core.example' --core-url 'https://core.example' --provider 'docker' --installation-id '')
+```
+
+- It downloads the installer into a private temporary directory, checks its SHA-256,
+ and runs it with `sudo`, or directly in a root shell.
+- The token reaches the installer on standard input, so it never appears in a process
+ argument, an environment variable or sudo's log.
+- The leading space keeps the command out of the shell history where `HISTCONTROL`
+ ignores such lines (the Debian and Ubuntu default).
+- If the command is interrupted or the download stalls, run the same command again:
+ the download resumes.
+
+The host needs:
+
+- Linux amd64 with systemd; Python 3.9+, `curl` and `sha256sum`; root or sudo.
+- SELinux not enforcing. Sudo mode refuses an enforcing host; use the no-sudo command
+ with a prepared user there.
+- One Core per host: a host already running a sudo-mode node for another Core is
+ refused.
+- Docker: rootful Docker Engine running, its socket `/var/run/docker.sock` owned by the
+ `docker` group with mode `0660`, enforcing CPU and memory limits (cgroup v2).
+- microsandbox: `/dev/kvm` in the `kvm` group (hardware or nested virtualization), and
+ the libraries microsandbox links (glibc).
+- CPUs and memory for at least one sandbox of the deployment's size, and about 2 GB of
+ disk for the Runtime image.
+- HTTPS access to the console and Core at the public URL; sandboxes reach Core too.
+
+### Without sudo
+
+The no-sudo command is the same without `sudo`:
+
+```sh
+ (umask 077; d=$(mktemp -d) || exit; trap 'rm -rf "$d"' EXIT
+curl -fsS --max-time 30 --max-filesize 1048576 'https://core.example/node-install/node-install.pyz' -o "$d/node-install.pyz" &&
+printf '%s %s\n' '' "$d/node-install.pyz" | sha256sum -c --status &&
+printf '%s\n' '' | python3 "$d/node-install.pyz" --enrollment-token-stdin --source-url 'https://core.example' --core-url 'https://core.example' --provider 'docker' --installation-id '')
+```
+
+Run it as the non-root user that will run the node; run by root, it installs the
+sudo-mode service instead. An administrator prepares that user once:
+
+- Docker: `sudo usermod -aG docker NODE_USER`, with Docker enforcing CPU and memory
+ limits. microsandbox: `sudo usermod -aG kvm NODE_USER` for read and write access to
+ `/dev/kvm`.
+- Lingering, after the group change: `sudo loginctl enable-linger NODE_USER`. After a
+ group change, sign in again as that user; if the user's systemd manager was already
+ running, restart it (`sudo systemctl restart user@$(id -u NODE_USER).service`) or
+ reboot.
+- microsandbox only: a home directory of at most 28 bytes, such as `/home/oac`,
+ because microsandbox's socket paths are short.
+- The host requirements above, except root, SELinux and one Core per host.
+
+`NODE_USER` stands for that user, as in the dialog's commands. Run the command as that
+user over SSH, or from a root shell with `su - NODE_USER`. The
+node's state then lives in `~/.oac/nodes//` of that user. The
+Docker group makes this user root-equivalent on the host too.
+
+## What sudo mode sets up
+
+| Item | Detail |
+| --- | --- |
+| Service user | System user `oac-node`, home `/var/lib/oac-node`, no login shell. An existing account with that home and a nologin shell is adopted; any other account named `oac-node` is refused |
+| Group | The group that owns `/var/run/docker.sock` (Docker) or `/dev/kvm` (microsandbox): `docker` or `kvm`, nothing else |
+| Service | `/etc/systemd/system/oac-node-.service`, a root-owned system unit with `User=oac-node`, enabled at boot. It restarts every 5 seconds while Core is unreachable and stops for good once Core no longer accepts the node |
+| Node state | `/var/lib/oac-node/.oac/nodes//`: identity, configuration, node files. microsandbox keeps its images and sandboxes under `/var/lib/oac-node/.oac/m/` |
+| Records | `/etc/oac-node/`: what the installer created or changed, used by reruns and uninstall |
+| Docker | The Runtime image, imported once, and a network `oac-node-` |
+
+Root only prepares the account, the group and the unit; everything else, the Docker
+network included, runs as `oac-node`, in its own session without a terminal. The
+installer never installs Docker, KVM or packages, never starts Docker, never changes
+device permissions, sudoers, firewall or SELinux settings, and never touches other
+accounts.
+
+**Docker mode is root-equivalent.** Membership in the `docker` group lets
+`oac-node`, and so anything that controls the node, act as root on the host. This
+is inherent to running sandboxes on Docker and equally true without sudo. Add Docker
+nodes only on hosts dedicated to sandboxes. microsandbox nodes need only the `kvm`
+group.
+
+**One Core per host.** All sudo-mode nodes share the `oac-node` account, so a host
+serves one Core in sudo mode; a command from a second Core is refused. A host also
+can't run the same installation's node both with and without sudo.
+
+**The token.** It is single-use, expires after 10 minutes and only registers the node.
+The installer takes it only on standard input and refuses it in the environment,
+where `sudo VAR=… python3` would record it in sudo's log. A sudoers policy with
+`log_input` records standard input, and so the token.
+
+## Rerun, expiry and slow links
+
+- Rerunning the same command is safe. Once the node is registered, a rerun uses the
+ node's own credential, changes nothing that already matches and needs no token.
+- A command that already expired, or was used on another host, fails at once with
+ `Core rejected the node configuration read (HTTP 401)`: generate a new command and
+ run it within 10 minutes.
+- If the command expires during a slow download, registration fails with
+ `The enrollment command expired or was already used`. The downloaded files are kept:
+ generate a new command in Web and run it.
+- Downloads resume where they stopped. A download that brings less than 64 KiB in a
+ minute stops, keeping what it has; run the command again.
+- The Docker Runtime image is about 500 MB. On a slow link, load it first: copy the
+ release's `oac--linux-amd64-runtime.tar.gz` asset to the host and run
+ `sudo docker load -i` on it. The installer then finds the exact image and skips the
+ download.
+- Interrupting the installer, or closing its terminal, stops it; run the command again
+ to continue.
+
+## Logs
+
+The installer prints the node's log command (`Logs: …`) when it finishes. The Add node
+dialog shows it too when something needs attention: when the node reports a problem, or
+when it hasn't become connected and ready about a minute after registering.
+
+| Node installed | Command |
+| --- | --- |
+| With sudo, or by root | `sudo journalctl -u oac-node-.service` |
+| Without sudo, as the node's user | `journalctl --user -u oac-node-.service` |
+
+The installation ID is in the command (`--installation-id`) and on the **System** page.
+
+## Remove a node
+
+1. In Web, open **Nodes** and choose **Remove node** on the node's page, or
+ **Remove** in its list row, then **Confirm removal**. Core refuses while the node
+ still holds sandboxes, snapshots or pending cleanup; let them finish, or
+ [archive their Sessions](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-maintenance)
+ through the Core API. Removal is permanent: the host can come back only as a new
+ node.
+2. Web then shows **Clean up the host** with the uninstall command. Run it on the host:
+
+ ```sh
+ (umask 077; d=$(mktemp -d) || exit; trap 'rm -rf "$d"' EXIT; s=; [ "$(id -u)" -eq 0 ] || s=sudo
+ curl -fsS --max-time 30 --max-filesize 1048576 'https://core.example/node-install/node-install.pyz' -o "$d/node-install.pyz" &&
+ printf '%s %s\n' '' "$d/node-install.pyz" | sha256sum -c --status &&
+ $s python3 "$d/node-install.pyz" --uninstall --installation-id '')
+ ```
+
+ For a node installed without sudo, **Installed without sudo?** gives the same command
+ without `sudo`; run it as that user.
+
+Uninstall first asks Core, at the address the node enrolled with, whether the node was
+removed, and refuses while Core still lists it. When the node enrolled with an address
+other than the current public URL, the dialog also shows **Old Core address gone?**:
+if that address no longer responds, it gives the command with `--force`, which skips
+the check; remove the node on the Nodes page first. Without the dialog, take the
+installer's SHA-256 from the `node-install.pyz` line of
+`https://core.example/node-install/SHA256SUMS`. Uninstall
+stops and removes the service, the node state, the records and the Docker network. It
+deletes the `oac-node` account only if the installer created it and no node remains;
+an adopted account only loses the groups the installer added.
+
+It never deletes sandboxes, volumes or images. It keeps the Runtime image and prints
+the `docker image rm` command. For microsandbox it keeps the store under
+`/var/lib/oac-node/.oac/m/`, prints how to delete it
+(`sudo -u oac-node rm -rf `), and keeps a created account until the store is
+gone; rerun uninstall afterwards. With `--force`, microVMs may still use the store, so
+check `pgrep -u oac-node` first. Uninstall can be rerun until it completes.
+
+## Troubleshooting
+
+### Readiness codes
+
+When a node is online but its provider is not ready, **Nodes** shows its status as
+**Provider unavailable**, with the reason in a help tip; **Overview** counts it as a
+**Provider issue**. The API reports the reason as a fixed code, the `diagnostic` field
+of `GET /core/v1/sandbox/nodes`. A node reports only its first failed check, and the
+next heartbeat, about ten seconds after a fix, clears or replaces it. The node's log has
+the local error behind the code.
+
+| Code | Web shows | Cause | Fix |
+| --- | --- | --- | --- |
+| `docker_unavailable` | Docker unavailable | The Docker socket is unreachable or not accessible, or Docker fails its info or image request | Start Docker and give the node's user access to `/var/run/docker.sock` |
+| `docker_limits_unsupported` | Docker limits unsupported | Docker reports no CPU quota or memory limit support | Use a host whose cgroups enforce CPU and memory limits (cgroup v2) |
+| `runtime_image_unavailable` | Runtime image missing | Docker does not have the pinned Runtime image | Rerun the add command, or load the image from the matching release |
+| `kvm_unavailable` | KVM unavailable | The node can't open `/dev/kvm` for reading and writing | Enable hardware virtualization and give the node's user KVM access, through the `kvm` group |
+| `microsandbox_artifacts_unavailable` | microsandbox components missing | The Runtime or firmware is missing or fails its SHA-256 check, or the helper is missing | Rerun the add command |
+| `capacity_insufficient` | Host too small | The host has fewer CPUs or less memory than one sandbox | Use a larger host, or change the sandbox size |
+| `provider_unavailable` | Sandbox provider unavailable | Any other failure, and every failure an older node reports | Read the node's log |
+
+A new group membership applies only to a new process. In sudo mode, restart the node
+service: `sudo systemctl restart oac-node-.service`. Without sudo,
+the user's systemd manager keeps the groups it started with, so restart that manager:
+`sudo systemctl restart user@$(id -u NODE_USER).service`. A node that is registered
+but never connects usually can't reach Core at the public URL, or its `/api/v1`
+WebSocket doesn't pass the reverse proxy.
+
+### Installer messages
+
+| Message | Fix |
+| --- | --- |
+| `Core rejected the node configuration read (HTTP 401)` before anything downloads, or `The enrollment command expired or was already used` | Generate a new command in Web and run it within 10 minutes |
+| Core's public URL changed after this command was generated | Generate a new command in Web and run it |
+| This host's node uses ``, but this command uses `` | The node was added under an older public URL. Remove it in Web, uninstall it, then add it again |
+| Docker Engine is not installed, or Docker is not running | Install Docker Engine, or `sudo systemctl enable --now docker`, then rerun |
+| Docker on this host does not enforce CPU and memory limits | Use cgroup v2, then rerun |
+| KVM is unavailable, or `/dev/kvm` must be group-accessible | Enable virtualization; your distribution's KVM package sets `root:kvm 0660` |
+| This host has N CPUs and M MiB of memory; each sandbox needs … | Use a larger host, or change the sandbox size |
+| SELinux is enforcing on this host | Use the no-sudo command as a prepared user |
+| This host already runs a sudo-mode node for another Core | One host serves one Core in sudo mode. Remove that node and uninstall it first |
+| Ask the host administrator to enable user lingering, or No systemd user manager is running | No-sudo mode: `sudo loginctl enable-linger NODE_USER`, or use sudo |
+| Core still lists this node | Remove it on the Nodes page first |
+
+## Change the sandbox backend or size
+
+To change the sandbox backend, the sandbox size or the Runtime, see the **Nodes** page;
+this flow will change in a coming release. A change applies to the whole deployment
+and retires every node. Retired nodes drop out of the list, so there is nothing to
+remove in Web: on each host, run the [uninstall command](#remove-a-node) (Core no longer
+accepts the node, so it needs no `--force`), then add the host again with a new command.
+The [operator reference](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-maintenance)
+describes today's procedure and API.
+
+
+## Remove a node added before the rename
+
+A new installer refuses retained state for the same installation under the old
+`parsar-node` names. It does not convert that state or remove nodes belonging to
+another installation. Remove the node on its old Core's Nodes page, then run the
+**previous release's** verified `node-install.pyz --uninstall --installation-id
+<installation-id>`, with `sudo` only if that node was installed with sudo. Use
+`--force` only when that Core no longer exists.
+
+If the previous installer is unavailable, inspect and remove only that
+installation's resources:
+
+1. Stop and disable `parsar-node-.service` with `systemctl`, using
+ `sudo` for a system service or `--user` as the original installing user.
+2. Remove its exact unit file or enablement link. System units live in
+ `/etc/systemd/system/`; user links live in `~/.config/systemd/user/`. Reload the
+ same systemd manager after removing the unit.
+3. After stopping the node, remove its state directory as its owner:
+ `/var/lib/parsar-node/.parsar/nodes/` as `parsar-node` in sudo
+ mode, or `~/.parsar/nodes/` as the original user. Do not run
+ root cleanup through files or links controlled by the service account.
+4. In sudo mode, remove only `/etc/parsar-node/.json`. Keep the
+ account record, the `parsar-node` user and group memberships while any other
+ installation or retained microsandbox store uses them.
+5. For Docker, remove `parsar-node-` only after confirming that
+ its network has no attached containers. Keep Runtime images, volumes and
+ retained microsandbox stores until their owners have explicitly retired them.
+
+Then use the new Core's Add node command. The new account, service and state use
+`oac-node` and `~/.oac/nodes`; historical Core Session records remain in Core.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/getting-started/nodes.md)
diff --git a/apps/docs/content/docs/hosted-providers.zh.mdx b/apps/docs/content/docs/hosted-providers.zh.mdx
new file mode 100644
index 000000000..6479922c6
--- /dev/null
+++ b/apps/docs/content/docs/hosted-providers.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Nodes and hosted providers · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+在 Web 的 Nodes 页面添加节点,并在目标主机执行经过校验的命令。sudo 模式使用 oac-node 系统账户;无 sudo 模式使用用户服务。旧名称的同一安装实例必须先按旧版本流程移除,再添加。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/hosted-providers)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/index.mdx b/apps/docs/content/docs/index.mdx
new file mode 100644
index 000000000..1be8be55b
--- /dev/null
+++ b/apps/docs/content/docs/index.mdx
@@ -0,0 +1,28 @@
+---
+title: "OpenAgentCore documentation"
+description: "Install Core and Web, create a Project API key, and add execution capacity."
+---
+
+OpenAgentCore is self-hosted, open-source Agents API infrastructure: Core serves the
+OpenAI Agents API and runs native harnesses (Codex, Claude Code and MiniMax Code) in
+sandboxes on your machines. Web is the administrator console; it issues the keys that
+applications call Core with.
+
+| Page | For | What it covers |
+| --- | --- | --- |
+| [Install Core and Web](/install) | Administrators | Prerequisites, download, the installer and every option, HTTPS and the reverse proxy, a local trial, first sign-in, what is created on disk |
+| [Nodes](/hosted-providers) | Administrators | Adding a node with one command, sudo and no-sudo modes, removal, logs and troubleshooting |
+| [Self-hosted executors](/self-hosted-execution) | Administrators and application owners | Connecting an application's own machine to a `self_hosted` Session, rotating and revoking its credential |
+| [Operations](/troubleshooting) | Administrators | The `oac` command, the Core key, backups, upgrades and troubleshooting |
+| [Configuration reference](/configure) | Administrators | Every `config.json` setting and every runtime setting in Web |
+| [Call the API](/quickstart) | Application developers | `OPENAI_BASE_URL` and `OPENAI_API_KEY`, running a Session, model providers and `x_agents_core` |
+| [API reference](/public-api) | Developers | The `/v1`, `/core/v1` and `/api/v1` namespaces and their contracts |
+
+Deeper references: the
+[nodes and sandbox backends](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md)
+reference for operators, the
+[protocol coverage and native differences](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md), and
+[maintainers and advanced deployments](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/maintainers.md) for building distributions
+and running Core without the installer.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/getting-started/README.md)
diff --git a/apps/docs/content/docs/index.zh.mdx b/apps/docs/content/docs/index.zh.mdx
new file mode 100644
index 000000000..f1cb5461f
--- /dev/null
+++ b/apps/docs/content/docs/index.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "OpenAgentCore documentation · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+安装 Core 与 Web,用 Core key 登录 Web,创建项目并签发应用 API key,然后添加执行节点。应用使用 /v1;Web 的服务端使用 /core/v1;机器连接使用 /api/v1。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/install.mdx b/apps/docs/content/docs/install.mdx
new file mode 100644
index 000000000..4778a096b
--- /dev/null
+++ b/apps/docs/content/docs/install.mdx
@@ -0,0 +1,533 @@
+---
+title: "Install Core and Web"
+description: "Current distribution, host prerequisites, HTTPS, first sign-in and installation options."
+---
+
+One command installs Core, the Web console and PostgreSQL on a Linux host. Web is
+the administrator console: you sign in with the Core key, issue Project API keys and
+add nodes. Applications call Core's API with those keys.
+
+1. [Check the prerequisites](#prerequisites).
+2. [Download a release bundle](#download-a-release).
+3. [Run the installer](#install).
+4. [Put Core and Web behind HTTPS](#https-and-the-reverse-proxy), or
+ [try it locally first](#try-it-locally-with-a-quick-tunnel).
+5. [Sign in to Web and issue a key](#sign-in-to-web).
+6. [Add a node](/hosted-providers).
+
+These pages describe the current source. Every bundle carries the docs that match it
+under `docs/`; if you install an older bundle, follow those.
+
+## Prerequisites
+
+**The Core host**
+
+- Linux amd64 with Python 3.9 or newer.
+- Docker Engine with Docker Compose 2.26.0 or newer (`docker compose version`).
+- A non-root user who can run `docker`. The installer refuses root.
+- Free loopback ports 8091 (Core) and 8080 (Web), or
+ [other ports](#ports-and-directory).
+- The GitHub CLI, `gh`, to download the bundle.
+
+The Core host needs no KVM and no systemd user services, unless you choose
+[native Core](#native-core).
+
+**Network, for nodes and remote access**
+
+- A DNS name for Core and Web, such as `core.example`, with a TLS certificate.
+- A reverse proxy on the Core host that sends three routes to the right service; see
+ [HTTPS and the reverse proxy](#https-and-the-reverse-proxy).
+
+Node hosts and their sandboxes, E2B sandboxes and self-hosted executors all reach
+Core at this HTTPS address. A local trial needs none of this: install without a
+public URL and set it later.
+
+## Download a release
+
+The repository is internal for now, so GitHub asks you to sign in first:
+
+```sh
+gh auth login
+```
+
+Pick a release, download its offline bundle and check it:
+
+```sh
+gh release list --repo MiniMax-AI/parsar-core
+mkdir -p "$HOME/.oac/releases" && cd "$HOME/.oac/releases"
+gh release download --repo MiniMax-AI/parsar-core --pattern '*-linux-amd64-offline.tar.gz*'
+sha256sum -c oac--linux-amd64-offline.tar.gz.sha256
+tar -xzf oac--linux-amd64-offline.tar.gz
+cd oac--linux-amd64
+```
+
+`` is the release tag from the list, and `` is the source commit in the
+asset names. The installer also checks every file in the bundle against its
+`SHA256SUMS` before it changes anything.
+
+Each release has two bundles:
+
+| Bundle | Contains | Use it for |
+| --- | --- | --- |
+| `oac--linux-amd64-offline.tar.gz` | Core, Web and PostgreSQL images, the installers and every node and Runtime file | Any installation. Web serves the node files to your nodes and self-hosted executors |
+| `oac--linux-amd64.tar.gz` | The same without the node and Runtime files | Core-only hosts, and installations that use only E2B. Web can't add nodes or connect self-hosted executors until the files are present |
+
+To add the node files to the smaller bundle, create an `artifacts/` directory in the
+extracted bundle, download the release's other `oac--linux-amd64-*`
+assets (not the two bundles) into it, then run `./install.sh`, or rerun it if the
+installation already exists. Nodes download these files only from your Web console,
+never from GitHub, so node hosts need no GitHub access.
+
+## Install
+
+From the extracted bundle:
+
+```sh
+./install.sh --public-url https://core.example
+```
+
+The installer:
+
+1. checks the host and the bundle, and loads the Core, Web and PostgreSQL images;
+2. creates private `~/.oac` (mode `0700`) and the installation directory `~/.oac/core` with the Core key, `config.json`
+ and the `oac` command ([what it creates](#what-the-installer-creates));
+3. starts PostgreSQL, Core and Web with Docker Compose, on loopback ports only;
+4. selects microsandbox at Web's Standard size (2 CPUs, 4 GiB memory and two 8 GiB
+ disks each). It adds no node, so this host needs no KVM; you add nodes in Web.
+
+It creates no Project or API key, makes no model request and runs no sample task.
+It ends by printing the console address, the API base URL and the next steps.
+
+Without `--public-url`, the installation serves only this host
+(`http://127.0.0.1:8080` for Web, `http://127.0.0.1:8091/v1` for the API). Set
+`public_url` in `config.json` later and run `oac apply`; you don't reinstall. Nodes,
+E2B and self-hosted executors need the public URL.
+
+### Installer options
+
+Flags only seed `config.json` for a new installation. Afterwards you change settings in
+`config.json` with [`oac apply`](/configure#process-settings-configjson),
+and rerunning `install.sh` accepts no flag except `--install-dir`.
+
+| Option | Default | Meaning |
+| --- | --- | --- |
+| `--public-url URL` | none: local access only | Public origin of Core and Web behind your reverse proxy, such as `https://core.example`: HTTPS, no path. Plain HTTP only for a loopback host. Seeds `public_url` |
+| `--sandbox docker\|microsandbox\|e2b\|none` | `microsandbox` (`none` with `--web-only`) | [Sandbox backend](#sandbox-backend) to start with. A one-time choice saved in the database; change it later in Web |
+| `--accept-docker-risks` | | With `--sandbox docker`: accept Docker's [weaker isolation](#sandbox-backend) without the prompt; required without a terminal |
+| `--e2b-api-key-file FILE` | | With `--sandbox e2b`: absolute path of a private file (no group or other access, at most 4 KiB) holding the E2B API key |
+| `--e2b-template ID:BUILD` | | With `--sandbox e2b`: the ready template build, `template-id:build-uuid` |
+| `--core-only` | | [Mode](#modes): Core and PostgreSQL, without Web |
+| `--web-only` | | [Mode](#modes): Web only, connected to an existing Core. Needs `--core-url` and `--core-key-file` |
+| `--core-url URL` | | With `--web-only`: origin of the existing Core, HTTPS or loopback HTTP. Seeds `web.core_url` |
+| `--core-key-file FILE` | | With `--web-only`: absolute path of a private file holding that Core's Core key (at least 32 characters) |
+| `--native-core` | off | Run [Core as a systemd user service](#native-core) instead of a container |
+| `--core-port PORT` | `8091` | Loopback port of Core. Seeds `ports.core` |
+| `--web-port PORT` | `8080` | Loopback port of Web. Seeds `ports.web` |
+| `--install-dir DIR` | `~/.oac/core` | Absolute installation directory. It must be empty or missing |
+| `--config FILE` | | Seed `config.json` from a prepared file instead of the setting flags above (`--public-url`, the mode flags, `--native-core`, ports, `--core-url`) |
+| `--convert`, `--yes` | | [Convert an installation](/troubleshooting#convert-an-earlier-installation) made before the OpenAgentCore rename; `--yes` skips the confirmation |
+
+#### Sandbox backend
+
+A deployment runs its sandboxes on exactly one backend:
+
+| `--sandbox` | Sandboxes run on | You then |
+| --- | --- | --- |
+| `microsandbox` (default) | microVMs on your nodes (KVM), 2 CPUs, 4 GiB memory and two 8 GiB disks each | [Add nodes](/hosted-providers) in Web |
+| `docker` | Docker on your nodes, 2 CPUs and 2 GiB each; weaker isolation, confirmed at install | [Add nodes](/hosted-providers) in Web |
+| `e2b` | E2B's cloud, with the CPU and memory of your template build | Nothing: E2B needs no nodes |
+| `none` | Nothing yet | Choose the backend on the **Nodes** page in Web |
+
+The Standard sizes come from the bundle's copy of Web's
+[`standard-sizes.json`](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/apps/web/src/features/sandbox/standard-sizes.json). The
+installer saves the choice through the same administrator API that Web uses once the
+services are healthy. If Core refuses it, for example because E2B rejects the key,
+the installer prints Core's message and exits with an error; the services keep
+running, and you choose the backend on the Nodes page.
+
+`--sandbox docker` first prints Docker's drawbacks. Containers share the host kernel,
+so a container escape reaches the node host, while microsandbox runs each sandbox in
+its own microVM. The node service account in the `docker` group is root-equivalent
+on each node host. Choose Docker only for trusted workloads or hosts without KVM.
+The installer then asks for confirmation, defaulting to No; without a terminal, pass
+`--accept-docker-risks`. Unconfirmed, it exits with an error before it installs
+anything.
+
+E2B also needs an HTTPS public URL that is not loopback, because E2B's sandboxes
+call Core from E2B's cloud. The installer refuses `--sandbox e2b` without one, before
+it installs anything:
+
+```sh
+./install.sh --public-url https://core.example --sandbox e2b \
+ --e2b-api-key-file "$HOME/.oac/e2b-api-key" --e2b-template ':'
+```
+
+Prepare the template build with the
+[E2B guide](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/deploy/e2b/README.md). To change the backend or
+the sandbox size later, use the Nodes page in Web; see
+[Nodes](/hosted-providers#change-the-sandbox-backend-or-size).
+
+#### Modes
+
+| Mode | Runs | Use it for |
+| --- | --- | --- |
+| all (default) | PostgreSQL, Core and Web | Most installations |
+| `--core-only` | PostgreSQL and Core | A Core whose Web runs on another host, or scripts only. Without Web there is no Add node command |
+| `--web-only` | Web | A second host for the console, paired with an existing Core |
+
+A Web-only console forwards every signed-in `/core/v1` request to its Core with the
+Core key, so `--core-url` must reach Core's `/core/v1` directly. The default reverse
+proxy sends `/core/v1` to Web, so the Core's public URL doesn't work here:
+
+- **Web on the Core host:** use Core's loopback port,
+ `--core-url http://127.0.0.1:8091`.
+- **Web on another host:** give Core a second HTTPS name whose proxy sends every path to
+ Core, such as `https://core-api.example` to `127.0.0.1:8091` on the Core host, and use
+ it as `--core-url`. Allow only the Web host's address on that name; anyone else who
+ reaches it still needs the Core key.
+
+##### Split deployment
+
+| Host | Install | Reverse proxy |
+| --- | --- | --- |
+| Core | `./install.sh --core-only --public-url https://core.example` | `core.example`: `/v1`, `/v1/*` and `/api/v1/*` to Core; `/node-install/*` to the Web host, with Host rewritten to `console.example`; nothing else. `core-api.example`: every path to Core, for the Web host's address only |
+| Web | `./install.sh --web-only …`, below | `console.example`: every path to Web |
+
+```sh
+./install.sh --web-only --install-dir "$HOME/.oac/web" \
+ --public-url https://console.example \
+ --core-url https://core-api.example \
+ --core-key-file "$HOME/core.key"
+```
+
+Browsers use `console.example`, and nodes download their installer from it.
+Applications, nodes and sandboxes call Core at `core.example`, and self-hosted
+executors download their installer from there too, which is why `core.example` sends
+`/node-install/*` to the Web host. Web serves those files only for its own name, so the
+proxy rewrites Host. The Web host serves every node and executor file, so install it
+from the offline bundle (or add `artifacts/`) when the deployment has nodes or
+self-hosted executors.
+
+Caddy on the Core host, where `203.0.113.10` stands for the Web host's address:
+
+```text
+core.example {
+ @core path /v1 /v1/* /api/v1/*
+ handle @core {
+ reverse_proxy 127.0.0.1:8091
+ }
+ handle /node-install/* {
+ reverse_proxy https://console.example {
+ header_up Host console.example
+ }
+ }
+ handle {
+ respond 404
+ }
+}
+
+core-api.example {
+ @web remote_ip 203.0.113.10
+ handle @web {
+ reverse_proxy 127.0.0.1:8091
+ }
+ handle {
+ respond 403
+ }
+}
+```
+
+nginx on the Core host, with the `map` from the
+[main example](#https-and-the-reverse-proxy):
+
+```nginx
+server {
+ listen 443 ssl;
+ server_name core.example;
+ ssl_certificate /etc/letsencrypt/live/core.example/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/core.example/privkey.pem;
+
+ client_max_body_size 0;
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_buffering off;
+ proxy_request_buffering off;
+ proxy_read_timeout 1h;
+ proxy_send_timeout 1h;
+
+ location = /v1 { proxy_pass http://127.0.0.1:8091; }
+ location /v1/ { proxy_pass http://127.0.0.1:8091; }
+ location /api/v1/ { proxy_pass http://127.0.0.1:8091; }
+ location /node-install/ {
+ proxy_pass https://console.example;
+ proxy_set_header Host console.example; # Web serves node files only for its own name
+ proxy_ssl_server_name on;
+ proxy_ssl_name console.example;
+ proxy_ssl_verify on;
+ proxy_ssl_verify_depth 2;
+ proxy_ssl_trusted_certificate /etc/ssl/certs/ca-certificates.crt;
+ }
+ location / { return 404; }
+}
+
+server {
+ listen 443 ssl;
+ server_name core-api.example;
+ ssl_certificate /etc/letsencrypt/live/core-api.example/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/core-api.example/privkey.pem;
+
+ allow 203.0.113.10; # the Web host
+ deny all;
+ client_max_body_size 0;
+ proxy_http_version 1.1;
+ proxy_buffering off;
+ proxy_read_timeout 1h;
+ location / { proxy_pass http://127.0.0.1:8091; }
+}
+```
+
+The Web host's proxy sends every path to Web, for example
+`console.example { reverse_proxy 127.0.0.1:8080 }` in Caddy.
+
+Copy the key file from the Core host's `secrets/core.key` with mode `0600`; the
+installer keeps its own copy in `secrets/core.key`, so delete `$HOME/core.key`
+afterwards. After the Core key is rotated, copy the new key again; see
+[Rotate the Core key](/troubleshooting#rotate-the-core-key). A Web-only install selects no
+sandbox backend: the Core host's `--sandbox` does, or later the paired console's
+**Nodes** page.
+
+The mode and native Core are fixed once installed. To change them, install into a
+new directory.
+
+#### Native Core
+
+`--native-core` runs Core as a systemd user service of the installing user;
+PostgreSQL and Web stay in containers. It needs a running systemd user manager and
+lingering for that user (`sudo loginctl enable-linger "$USER"`), and the bundle's
+native binaries must load on the host. PostgreSQL then listens on a loopback port
+the installer picks (`ports.database`), and Web uses the host network. Native Core
+needs no KVM and is unrelated to the sandbox backend.
+
+#### Ports and directory
+
+Core and Web listen on `127.0.0.1` only; the reverse proxy reaches them there. The
+installer refuses a port that is in use. Several installations can share a host with
+their own `--install-dir` and ports, for example
+`--install-dir "$HOME/.oac/core-2" --core-port 8092 --web-port 8081`. Each has its
+own database, Core key and nodes.
+
+## HTTPS and the reverse proxy
+
+By default, Core and Web share one public origin; for Web on its own host, see the
+[split deployment](#split-deployment). Your reverse proxy terminates TLS and routes by
+path:
+
+| Path | Goes to | Callers |
+| --- | --- | --- |
+| `/v1`, `/v1/*` | Core, `127.0.0.1:8091` | Applications, with a Project API key |
+| `/api/v1/*` | Core, `127.0.0.1:8091` | Nodes, sandboxes and self-hosted executors, with their own machine credentials. Uses WebSockets |
+| Everything else | Web, `127.0.0.1:8080` | Browsers: the console, its sign-in and `/core/v1/*`. Node and executor hosts download their installers from `/node-install/*` |
+
+The proxy must:
+
+- **Preserve Host.** Web accepts only requests for the host of its public URL.
+- **Pass WebSocket upgrades** on `/api/v1`. Nodes and Runtime daemons hold long-lived
+ WebSocket connections.
+- **Not buffer or time out streams.** `/v1` streams Session events (server-sent events).
+- **Accept large uploads.** Core enforces its own limits; source files may reach
+ 512 MiB.
+
+Web answers 404 on `/v1` and `/api/v1` and never forwards them. If those paths reach
+Web, application calls, node enrollment and every sandbox connection fail. Run the
+proxy on the Core host, since Core and Web listen on loopback. `oac status` prints
+the routes with this installation's ports.
+
+**Caddy** (obtains and renews the certificate itself; passes Host and WebSockets by
+default):
+
+```text
+core.example {
+ @core path /v1 /v1/* /api/v1/*
+ handle @core {
+ reverse_proxy 127.0.0.1:8091
+ }
+ handle {
+ reverse_proxy 127.0.0.1:8080
+ }
+}
+```
+
+**nginx** (for example `/etc/nginx/conf.d/oac.conf`, inside the `http` block;
+use your certificate paths):
+
+```nginx
+map $http_upgrade $connection_upgrade {
+ default upgrade;
+ '' close;
+}
+
+server {
+ listen 80;
+ server_name core.example;
+ return 301 https://$host$request_uri;
+}
+
+server {
+ listen 443 ssl;
+ server_name core.example;
+ ssl_certificate /etc/letsencrypt/live/core.example/fullchain.pem;
+ ssl_certificate_key /etc/letsencrypt/live/core.example/privkey.pem;
+
+ client_max_body_size 0; # Core enforces its own upload limits
+ proxy_http_version 1.1;
+ proxy_set_header Host $http_host;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ proxy_buffering off; # server-sent events on /v1
+ proxy_request_buffering off;
+ proxy_read_timeout 1h; # long-lived WebSockets and streams
+ proxy_send_timeout 1h;
+
+ location = /v1 { proxy_pass http://127.0.0.1:8091; }
+ location /v1/ { proxy_pass http://127.0.0.1:8091; }
+ location /api/v1/ { proxy_pass http://127.0.0.1:8091; }
+ location / { proxy_pass http://127.0.0.1:8080; }
+}
+```
+
+Replace `core.example` and, if you changed them, the ports. To check the routing:
+
+```sh
+curl -s -o /dev/null -w '%{http_code}\n' -H 'OpenAI-Beta: agents=v1' https://core.example/v1/agents
+```
+
+`401` means `/v1` reached Core, which asks for a key; `404` means it reached Web.
+
+Install with `--public-url https://core.example`, or, for an existing installation,
+set `public_url` in `~/.oac/core/config.json` and run `~/.oac/core/oac apply`.
+Core derives every address it hands out from this one setting. If you change it while
+nodes, sandboxes or self-hosted executors use the old address, `oac apply` lists
+them and asks you to confirm; those nodes must then be added again. See
+[Changing the public URL](/configure#changing-the-public-url).
+
+TLS verification stays on everywhere. With a private certificate authority, node
+hosts, executor hosts and the Runtime image must trust it.
+
+## Try it locally with a quick tunnel
+
+For a trial without DNS or certificates, a
+[Cloudflare quick tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/do-more-with-tunnels/trycloudflare/)
+gives the installation a temporary public HTTPS address. It forwards to one local
+port, so put a plain-HTTP proxy with the same three routes in front, for example this
+Caddyfile on `127.0.0.1:8443`:
+
+```text
+http://:8443 {
+ bind 127.0.0.1
+ @core path /v1 /v1/* /api/v1/*
+ handle @core {
+ reverse_proxy 127.0.0.1:8091
+ }
+ handle {
+ reverse_proxy 127.0.0.1:8080
+ }
+}
+```
+
+1. Install, with or without a public URL (`./install.sh`).
+2. Start the local proxy: `caddy run --config Caddyfile`.
+3. Start the tunnel: `cloudflared tunnel --url http://127.0.0.1:8443`. It prints an
+ address such as `https://random-words.trycloudflare.com`.
+4. Set `"public_url": "https://random-words.trycloudflare.com"` in
+ `~/.oac/core/config.json` and run `~/.oac/core/oac apply`.
+5. Open that address and [sign in](#sign-in-to-web).
+
+A quick tunnel is for trials only. Its address changes whenever `cloudflared`
+restarts; nodes and executors bound to the old address must then be added again.
+Its throughput is low, so the first node download of the Runtime (about 500 MB) can
+be slow; see [slow links](/hosted-providers#rerun-expiry-and-slow-links). Nodes and E2B need a
+reachable HTTPS address, so a loopback-only installation can't have nodes: Web says so
+at Add node.
+
+## Sign in to Web
+
+1. Open the console address the installer printed: your public URL, or
+ `http://127.0.0.1:8080` on the Core host. Use exactly that address; Web refuses
+ other host names. For a loopback installation on a remote machine, forward the port
+ first: `ssh -L 8080:127.0.0.1:8080 `.
+2. Sign in with the Core key:
+
+ ```sh
+ cat ~/.oac/core/secrets/core.key
+ ```
+
+ The Core key is the installation's administrator credential. Keep it private; see
+ [Core key](/troubleshooting#core-key). Web has no user accounts.
+3. **Set a default model.** Open **System** and find **Default model**. On the harness
+ card marked **Default** (Codex unless you changed `core.default_harness`), choose
+ **Set**; once a provider is configured, the button reads **Replace**. Enter the
+ model provider's base URL and API key; MiniMax Code also needs the context window
+ and max output tokens. Core encrypts the key and never shows it again. The default
+ applies to Core-hosted Sessions and Sessions without an environment when the request
+ and the Agent carry no model provider; self-hosted Sessions never use it. See
+ [model providers](/quickstart#model-providers).
+4. **Create a project and issue a key.** Open **Projects and keys**, choose
+ **Create project** and give it a **Name**, then **Issue key** in it with a
+ **Key name**. The **Key issued** dialog shows the key once: choose **Copy key**, keep
+ it somewhere safe, then **I've saved this key**. Core keeps only a digest. The
+ dialog's **How to call** card shows the API base URL and sample requests.
+5. Give the application developer the key, the API base URL and the model ID to use
+ with the default model provider (Sessions must name `agent.model`). They follow the
+ [API quickstart](/quickstart).
+
+**Overview** tracks the setup in a **Getting started** checklist, in any order:
+**Get sandboxes ready** ([add a node](/hosted-providers)), **Set a default model**,
+**Create a project and issue a key**, and **Run the first Session**. After you hide it,
+**Show Getting started** in the sidebar brings it back.
+
+An existing pre-rename installation at `~/.parsar/core` blocks a new default install.
+Use `./install.sh --convert` after draining it with the old release, or explicitly
+choose another `--install-dir`. See [conversion and retained backups](/troubleshooting#convert-an-earlier-installation).
+
+## What the installer creates
+
+The installation directory, `~/.oac/core` by default, mode `0700`:
+
+| Path | Content |
+| --- | --- |
+| `config.json` | Process settings. The only file you edit; see the [configuration reference](/configure) |
+| `oac` | The [management command](/troubleshooting#the-oac-command) |
+| `secrets/core.key` | The Core key |
+| `secrets/credential.key` | Encryption key for credentials stored in the database. Back it up with the database; never replace it |
+| `secrets/database.password` | PostgreSQL password |
+| `state.json` | Format 2, installation ID, Compose project name, image IDs and source commit. Written by the tools only |
+| `generated/` | Files derived from `config.json`: `compose.json`, `core.env`, `core-key-digests.json`, `settings.json`, `config.schema.json`, and `runtime-history.json` or the native Core unit when used. `oac apply` rewrites them; don't edit them |
+| `node-payload/` | The node and self-hosted installers, the manifest and the node files that Web serves at `/node-install/` |
+| `state/e2b/` | Private E2B receipts |
+| `native/` | Core binaries, with `--native-core` only |
+
+Web-only installations have only `secrets/core.key` among the secrets, and no
+`state/e2b/`; Core-only installations have no `node-payload/`.
+
+In Docker, the installer creates a Compose project named `oac-<10 hex digits>`
+(`project` in `state.json`) with the containers `database`, `migrate` (runs the
+migrations, then exits), `core` and `web`, and the volume `_database` that
+holds all data. Core and Web publish only `127.0.0.1:8091` and `127.0.0.1:8080`. The
+database publishes no port unless Core is native.
+
+The database holds the sandbox backend selection, and later your Projects, keys,
+nodes, default models and all execution history. Apart from Docker's own storage,
+nothing is written outside your home directory: no system service and no file under
+`/etc`. Native Core adds a systemd user unit, enabled from `generated/`.
+
+## Rerun the installer
+
+Rerunning `./install.sh` from the same bundle repairs an installation: it reloads
+missing images, restores a missing `oac` command, copies node files newly placed in
+`artifacts/`, applies `config.json` and starts the services. It accepts only
+`--install-dir`. It refuses a bundle from another release; see
+[Upgrades](/troubleshooting#upgrade-an-installation).
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/getting-started/install.md)
diff --git a/apps/docs/content/docs/install.zh.mdx b/apps/docs/content/docs/install.zh.mdx
new file mode 100644
index 000000000..c8cc6d490
--- /dev/null
+++ b/apps/docs/content/docs/install.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Install Core and Web · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+安装器默认在 ~/.oac/core 安装 Core、Web 和专用 PostgreSQL,初始没有执行节点。先确定公网 HTTPS 地址,按当前版本的校验步骤下载并安装,再用 Core key 登录 Web。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/install)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/meta.json b/apps/docs/content/docs/meta.json
new file mode 100644
index 000000000..73b131510
--- /dev/null
+++ b/apps/docs/content/docs/meta.json
@@ -0,0 +1,27 @@
+{
+ "title": "OpenAgentCore documentation",
+ "pages": [
+ "---Start here---",
+ "index",
+ "concepts",
+ "execution-model",
+ "install",
+ "configure",
+ "---Application access---",
+ "bootstrap-projects-keys",
+ "quickstart",
+ "public-api",
+ "agents-and-tools",
+ "sessions",
+ "---Execution---",
+ "environments-and-files",
+ "hosted-providers",
+ "self-hosted-execution",
+ "---Administration---",
+ "console",
+ "admin-api",
+ "observability",
+ "troubleshooting",
+ "api-reference"
+ ]
+}
diff --git a/apps/docs/content/docs/meta.zh.json b/apps/docs/content/docs/meta.zh.json
new file mode 100644
index 000000000..e1c42ae20
--- /dev/null
+++ b/apps/docs/content/docs/meta.zh.json
@@ -0,0 +1,27 @@
+{
+ "title": "OpenAgentCore 文档",
+ "pages": [
+ "---从这里开始---",
+ "index",
+ "concepts",
+ "execution-model",
+ "install",
+ "configure",
+ "---应用接入---",
+ "bootstrap-projects-keys",
+ "quickstart",
+ "public-api",
+ "agents-and-tools",
+ "sessions",
+ "---执行环境---",
+ "environments-and-files",
+ "hosted-providers",
+ "self-hosted-execution",
+ "---管理---",
+ "console",
+ "admin-api",
+ "observability",
+ "troubleshooting",
+ "api-reference"
+ ]
+}
diff --git a/apps/docs/content/docs/observability.mdx b/apps/docs/content/docs/observability.mdx
new file mode 100644
index 000000000..91d7f2d57
--- /dev/null
+++ b/apps/docs/content/docs/observability.mdx
@@ -0,0 +1,282 @@
+---
+title: "Runtime observations"
+description: "Current Runtime observations, recorded history and their availability limits."
+---
+
+Status: Phase 2 and initial Core Web consumption implemented. The current-snapshot routes, strict
+`packages/agents-client` projection, and generated `core.openapi.yaml` contract are
+implemented and consumed by the Dashboard through complete Session/observation
+identity joins. Durable history uses the separate optional
+[Runtime history API](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime-history-api.md); lifecycle controls remain outside
+this phase.
+
+These are administrator reads under `/core/v1`, authenticated by the Core key,
+not upstream OpenAI Agents resources. The
+former project routes `GET /v1/agents/runtime-observations` and
+`GET /v1/agents/sessions/{session_id}/runtime-observation` are removed.
+
+## Routes
+
+### List current Runtime observations
+
+```http
+GET /core/v1/sandbox/runtime-observations?after={session_id}&limit=20&order=desc
+Authorization: Bearer ...
+```
+
+| Field | Rules |
+| --- | --- |
+| `after` | Observation ID from the previous page. Optional, supplied once. |
+| `limit` | Integer 1–100, default 20. |
+| `order` | `asc` or `desc`, default `desc`. |
+
+The list contains one current Runtime context for every Session of every managed
+Project, labelled with its owning `project_id`, including explicit `none`,
+unsupported `self_hosted`, and released managed contexts. Ordering uses the same Session creation-time and ID
+keyset as the Session list. An observation ID is the Session UUID, so pagination
+does not change when the underlying Runtime incarnation changes. Pages are not an
+atomic telemetry snapshot; every row has its own `resolved_at`, and a successful
+provider sample has its own `observed_at`. A client completes the entire page chain
+before publishing a new Dashboard snapshot.
+
+```json
+{
+ "object": "list",
+ "data": [
+ {
+ "project_id": "3f0c2a9e-2b7d-4d0f-9a51-1c8e4b6d7a20",
+ "observation": {
+ "id": "6c77d3a2-71d6-4ed5-884f-687aecda02a3",
+ "object": "agent.runtime_observation",
+ "session_id": "6c77d3a2-71d6-4ed5-884f-687aecda02a3",
+ "environment_id": "6c02fb71-5fa8-4298-93e8-57c6625a3fc2",
+ "mode": "openai_hosted",
+ "provider_type": "docker",
+ "instance": {
+ "kind": "managed_allocation",
+ "allocation_id": "d23ab94e-e40b-45bd-93a2-444f1f74642b",
+ "device_id": "2e434f4f-76aa-4e54-a707-4757036d90ef",
+ "connection_generation": null
+ },
+ "status": "observed",
+ "reason": null,
+ "allocation_created_at": 1789951200,
+ "resolved_at": 1789953021,
+ "observed_at": 1789953020,
+ "started_at": 1789951220,
+ "cpu": {
+ "usage_seconds_total": 482.75,
+ "capacity_cores": 2.0,
+ "usage_cores": null,
+ "utilization_ratio": null
+ },
+ "memory": {
+ "usage_bytes": 805306368,
+ "limit_bytes": 2147483648
+ },
+ "disk": null
+ }
+ }
+ ],
+ "has_more": false,
+ "first_id": "6c77d3a2-71d6-4ed5-884f-687aecda02a3",
+ "last_id": "6c77d3a2-71d6-4ed5-884f-687aecda02a3"
+}
+```
+
+### Retrieve one Session's current Runtime observation
+
+```http
+GET /core/v1/projects/{project_id}/sessions/{session_id}/runtime-observation
+Authorization: Bearer ...
+```
+
+This returns the same object shape as a list item's `observation`, without the
+administrator list's `disk` (see the [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md)). It never starts a Turn, creates
+an Environment, provisions compute, renews a lease, or changes lifecycle state.
+
+A valid `environment:none` Session returns `200` with status `unsupported`; the
+Session exists but has no attributable Runtime instance. A missing Session, or one
+outside the Project, returns the existing indistinguishable not-found error.
+
+## Resource schema
+
+### `RuntimeObservation`
+
+| Field | Type | Required | Semantics |
+| --- | --- | --- | --- |
+| `id` | string | yes | Session UUID; stable identity of this current-observation resource and its list cursor. |
+| `object` | literal | yes | `agent.runtime_observation`. |
+| `session_id` | string | yes | Authorized Core Session. |
+| `environment_id` | string or null | yes | Null only for mode `none`. |
+| `mode` | enum | yes | `none`, `self_hosted`, `openai_hosted`. |
+| `provider_type` | string or null | yes | Forward-compatible safe source kind such as `docker` or `microsandbox`; null when no provider applies. Clients must not treat an unknown nonempty value as an error. |
+| `instance` | object | yes | Provider-neutral current incarnation identity; explicit `kind=none` when no compute applies. |
+| `status` | enum | yes | `observed`, `unsupported`, `unavailable`. |
+| `reason` | enum or null | yes | Safe reason when status is not `observed`. |
+| `allocation_created_at` | integer or null | yes | Unix seconds for managed allocation age. |
+| `resolved_at` | integer | yes | Unix seconds when Core resolved identity and status for this row. |
+| `observed_at` | integer or null | yes | Provider sample time; null without a sample. |
+| `started_at` | integer or null | yes | Current compute incarnation start time. |
+| `cpu` | object or null | yes | Null when no CPU fields were observed. |
+| `memory` | object or null | yes | Null when no memory fields were observed. |
+
+### `RuntimeInstance`
+
+```json
+{
+ "kind": "managed_allocation",
+ "allocation_id": "alloc_...",
+ "device_id": "device_...",
+ "connection_generation": null
+}
+```
+
+`kind` is `managed_allocation`, `self_hosted_connection`, or `none`. For a managed
+context, `allocation_id` is the incarnation key; for self-hosted, the current
+`connection_generation` is the incarnation key. Fields that do not apply are
+explicit nulls. Provider-native container IDs, pod names, host paths, credentials,
+and raw labels are not public fields.
+
+### `RuntimeCPUObservation`
+
+```json
+{
+ "usage_seconds_total": 482.75,
+ "capacity_cores": 2.0,
+ "usage_cores": 1.42,
+ "utilization_ratio": 0.71
+}
+```
+
+All fields are `number | null`. Values are finite and nonnegative;
+`capacity_cores`, when present, is greater than zero. Numeric zero is observed
+zero. Null is unavailable. `usage_cores` is the cumulative CPU delta divided by
+the observation-time delta for two ordered samples of the same incarnation.
+`utilization_ratio` is `usage_cores / capacity_cores`. It is not clamped: a value
+above 1 is retained as provider/accounting evidence and is not interpreted as a
+lifecycle signal. Both derived fields are null after a cache restart or whenever
+either source sample is absent or invalid. The API never derives CPU rate from a
+single sample of cumulative time. A provider that reports only a current share
+of its CPU capacity (E2B) fills `utilization_ratio` with that report and leaves
+`usage_seconds_total` and `usage_cores` null.
+
+### `RuntimeMemoryObservation`
+
+```json
+{
+ "usage_bytes": 805306368,
+ "limit_bytes": 2147483648
+}
+```
+
+Both fields are `integer | null`. Values are nonnegative and safe JSON integers.
+Zero usage is observed zero. A missing or unlimited provider limit is null.
+
+## Status and reason matrix
+
+| Status | Allowed reason |
+| --- | --- |
+| `observed` | null |
+| `unsupported` | `runtime_mode_not_observable` |
+| `unavailable` | `allocation_pending`, `runtime_not_running`, `source_not_configured`, `sample_timeout`, `sample_unavailable` |
+
+Ownership mismatch, malformed durable identity, corrupt provider evidence, and
+authorization failure are not downgraded to unavailable rows.
+
+## Error responses
+
+Use the existing Agents API error envelope.
+
+| HTTP | Type / code | When |
+| --- | --- | --- |
+| 400 | `invalid_request_error` / `invalid_request_error` | List: a repeated supported query key, or an empty or invalid limit or order, with the shared Beta list messages. Unknown list query keys are ignored. |
+| 400 | `invalid_request_error` / `unsupported_parameter` | Single-Session retrieval with any query parameter. |
+| 401 | `invalid_request_error` / `invalid_admin_key` | Missing or invalid Core key. |
+| 404 | `not_found_error` / `not_found_error` | Missing, malformed or foreign Session/cursor, indistinguishably, as for the [Session list cursor](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/list-query-semantics.md#list-cursor-errors--september-23-2026). |
+| 500 | `server_error` / `internal_error` | Integrity, ownership, or invalid provider evidence. |
+| 503 | `server_error` / `execution_unavailable` | Required Runtime observation service is not configured, or list collection exceeded its request budget. |
+
+Errors never include provider raw responses or credentials.
+
+## Freshness and caching
+
+- Return `Cache-Control: no-store`.
+- The Phase 2 implementation performs bounded direct reads and has no observation
+ cache. A later internal cache may coalesce reads for at most five seconds.
+- `observed_at` is authoritative for freshness; HTTP response time is not.
+- Clients mark samples stale according to their own explicit threshold.
+- `ETag` is not proposed because observations change independently.
+
+## Client contract
+
+`packages/agents-client` exposes:
+
+```ts
+type RuntimeObservationStatus = "observed" | "unsupported" | "unavailable";
+type RuntimeObservationReason =
+ | "runtime_mode_not_observable"
+ | "allocation_pending"
+ | "runtime_not_running"
+ | "source_not_configured"
+ | "sample_timeout"
+ | "sample_unavailable";
+
+type RuntimeObservation =
+ | RuntimeObservedObservation
+ | RuntimeUnavailableObservation
+ | RuntimeNoneObservation
+ | RuntimeSelfHostedObservation;
+
+interface AdminRuntimeObservation {
+ project_id: string;
+ observation: RuntimeObservation & { disk: RuntimeDiskObservation | null };
+}
+
+class AdminClient {
+ listRuntimeObservations(options?: {
+ after?: string;
+ limit?: number;
+ order?: "asc" | "desc";
+ }): Promise>;
+
+ retrieveRuntimeObservation(projectId: string, sessionId: string): Promise;
+}
+```
+
+These exported variants discriminate on `status` and `mode`; their instance,
+reason, timestamps, CPU, and memory fields narrow accordingly. The exact variant
+definitions live in `packages/agents-client/src/types.ts` and mirror the status
+and reason matrix above.
+
+The client validates every required field, enum, nullability rule, timestamp, and
+finite number. The current pinned contract rejects unknown additive fields so an
+unreviewed server expansion cannot silently cross the browser boundary. Malformed
+data rejects the whole page; Web does not publish a partial snapshot.
+
+The generated OpenAPI 2 schema records field-level required/nullability rules,
+UUID formats, reason enums, and numeric minima. OpenAPI 2
+cannot encode the complete cross-field discriminated union. The matrix above is
+normative for wire consumers; the server projection and strict TypeScript
+projector enforce it, and the exported TypeScript type prevents invalid
+status/mode combinations in typed consumers.
+
+Web also applies a configured whole-refresh budget. If `has_more` remains true
+when that budget is exhausted, it retains the prior complete snapshot and marks
+the refresh incomplete; it does not publish partial values as global totals.
+After both Runtime-observation and Session traversals complete, Web also requires
+their Session ID sets to be identical. A mismatch caused by concurrent creation or
+deletion makes the candidate incomplete and prevents publication.
+
+## Deliberately excluded
+
+- Token usage: use existing Session/Turn Usage.
+- Billing and cost: product/backend concern.
+- Historical series in these routes: the optional capability uses the separate
+ [Runtime history API](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime-history-api.md).
+- Container logs and command output.
+- Provider credentials or native configuration.
+- Start, stop, pause, resume, restart, renew, or delete operations.
+- Idle classification and automatic shutdown.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime-observability-api.md)
diff --git a/apps/docs/content/docs/observability.zh.mdx b/apps/docs/content/docs/observability.zh.mdx
new file mode 100644
index 000000000..981a98f6c
--- /dev/null
+++ b/apps/docs/content/docs/observability.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Runtime observations · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+当前 Runtime 观测与已保存历史是不同的数据。离线或陈旧数据不代表当前健康状态;查看来源、时间和可用性,执行事实仍以 Core 为准。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/observability)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/public-api.mdx b/apps/docs/content/docs/public-api.mdx
new file mode 100644
index 000000000..13cec4d9c
--- /dev/null
+++ b/apps/docs/content/docs/public-api.mdx
@@ -0,0 +1,105 @@
+---
+title: "API namespaces and credentials"
+description: "The public application API, private administration API and machine connection interface."
+---
+
+Core serves three namespaces. Each has one kind of caller and its own credential;
+no credential works in another namespace.
+
+| Namespace | Caller | Credential | Contents | Reference |
+| --- | --- | --- | --- | --- |
+| `/v1` | Applications (business systems, SDKs) | Project API key | Exactly the pinned official Agents API routes. Core-only fields live only in `x_agents_core` (`harness`, `model_provider`) | [Public API](/sessions) |
+| `/core/v1` | Core Web's server and operator scripts | [Core key](/troubleshooting#core-key) | Installation facts, Projects and keys, resource reads and deletion, Session archive, credential issuance, metrics, audit, sandbox deployment and nodes, deployment model providers | [Core API](#core-api), [Web API](/admin-api), [Core OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core.openapi.yaml) |
+| `/api/v1` | Nodes, Runtime daemons, self-hosted executors | Machine credentials: node enrollment tokens and executor credentials issued through `/core/v1`, node credentials registered with an enrollment token, and daemon credentials Core writes into hosted sandboxes | Machine connections only: `/api/v1/sandbox-node/*` and `/api/v1/agent-daemon/*`, including WebSockets; each credential works only on its own routes | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host), [executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-executor-credentials.md), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime.openapi.yaml) |
+
+A Project API key gets 401 on `/core/v1` and `/api/v1`; the Core key gets 401 on
+`/v1` and `/api/v1`. Projects own assets. Multiple equally privileged keys share
+the Project's assets and execution principal; provenance retains the key that
+performed each write. Projects and application keys live in PostgreSQL, never
+deployment configuration.
+
+The browser talks only to Web's server: `/console/auth` and `/console/config`,
+`/node-install/*`, the static pages, and `/core/v1/*`, which the server forwards
+with the Core key after sign-in and same-origin checks. Users sign in to Web with
+the Core key and get an HttpOnly session cookie; the browser never receives the
+key. Core decides which `/core/v1` routes
+exist; Web returns 404 for `/v1` and `/api/v1`, whatever credential a request
+carries.
+
+The deployment's reverse proxy sends `/v1`, `/v1/*` and `/api/v1/*` to Core and
+every other path, including `/core/v1/*`, to Web. Operator scripts call `/core/v1`
+with the Core key on Core's loopback port, not through the public entry.
+
+## Public API
+
+Applications call every `/v1` route with a Project API key. The routes are exactly
+the 58 method and path pairs in
+[upstream-routes.json](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream-routes.json); the
+[Public API guide](/sessions) groups them by resource. `/v1` has no
+Core-only route.
+
+## Core API
+
+All routes are under `/core/v1`. Core Web's server and operator scripts call them
+with the Core key.
+
+| Routes | Contents | Contract |
+| --- | --- | --- |
+| `projects`, `projects/{project_id}[/archive]`, `projects/{project_id}/keys[/{key_id}]` | Projects and their API keys | [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md) |
+| `projects/{project_id}/{agents,environment-templates,skills,files,vaults,sessions}/**` | Resource reads and deletion, Session history, artifacts and archive | [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md) |
+| `projects/{project_id}/sessions/{session_id}/execution-configuration` | Committed harness and model selection | [Execution configuration](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/execution-configuration.md) |
+| `projects/{project_id}/sessions/{session_id}/runtime-observation`, `sandbox/runtime-observations` | Current Runtime observations | [Runtime observations](/observability) |
+| `projects/{project_id}/sessions/{session_id}/runtime-history` | Stored Runtime history | [Runtime history](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime-history-api.md) |
+| `projects/{project_id}/{resource-owners,write-operations}`, `audit-log`, `summary` | Provenance, write history, administrator audit and usage summary | [Write audit](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/write-audit.md), [administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md) |
+| `projects/{project_id}/environments/{environment_id}/executor-credentials[/{key_id}]` | Executor credentials for a self-hosted Environment | [Executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-executor-credentials.md) |
+| `installation` | Public URL, API base URL, source commit, the installer's process settings and what is bound to the public URL; available before any deployment | [Installation](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/installation.md) |
+| `metrics` | Core's own process metrics | [Core metrics](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core-metrics.md) |
+| `sandbox/deployment[/maintenance]`, `sandbox/enrollment-tokens`, `sandbox/nodes[/{node_id}[/allocations]]` | Sandbox deployment, node enrollment tokens and nodes | [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/sandbox-deployment.md), [node operations](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md), [node host history](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/node-host-history.md) |
+| `harnesses`, `harnesses/{harness}/model-provider` | Supported harnesses and each harness's deployment default model provider (write-only key) | [Model execution](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/model-execution.md#deployment-defaults) |
+
+## Machine connection API
+
+These routes are under `/api/v1`. Each accepts only the credential listed, never
+the Core key or a Project API key.
+
+| Routes | Caller | Credential | Contract |
+| --- | --- | --- | --- |
+| `POST sandbox-node/enroll` | Node installer | One-use enrollment token from `POST /core/v1/sandbox/enrollment-tokens` | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime.openapi.yaml) |
+| `GET sandbox-node/configuration` | Node installer and node | Enrollment token, or node credential with `X-OAC-Node-ID` | [Sandbox deployment](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/sandbox-deployment.md), [machine OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime.openapi.yaml) |
+| `GET sandbox-node/identity`, WebSocket `GET sandbox-node/connect` | Node | Node credential registered at enrollment | [Node operations](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#register-a-host) |
+| `POST agent-daemon/enroll`, `GET agent-daemon/connection` | Self-hosted executor and its installer | Executor credential from `/core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials` | [Executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-executor-credentials.md) |
+| WebSocket `GET agent-daemon/ws`, `POST agent-daemon/bootstrap`, `GET agent-daemon/device-status` | Runtime daemons | Daemon credential: Core writes one into each hosted sandbox it prepares; a self-hosted executor uses its executor credential | [Runtime enrollment](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/README.md#user-managed-runtime-enrollment) |
+
+## Contract sources
+
+- [Pinned upstream baseline](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream.json): OpenAI
+ Python SDK 3.13.0, exact upstream commit and `agents=v1`.
+- [Pinned routes](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream-routes.json) and
+ [fields](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream-fields.json): extracted from the
+ pinned SDK by `scripts/extract-agents-api-upstream.py`. Contract tests require the
+ public OpenAPI to have exactly these routes and to keep every other field inside
+ `x_agents_core`.
+- [Public OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/openapi.yaml): public schema snapshot;
+ combine it with the fixed SDK and [operation evidence](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/operation-evidence.md).
+- [Core OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core.openapi.yaml): generated `/core/v1`
+ routes, all authenticated by the Core key.
+- [Machine connection OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/runtime.openapi.yaml):
+ generated `/api/v1` sandbox node routes. The node WebSocket and the private
+ daemon transport are described in the node and Runtime credential guides.
+- [Administrator contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md): Project/key
+ lifecycle, resources, explicit hosted Session archive, summary,
+ errors/deletion preconditions, audit and historical copy provenance.
+- [Web integration](/admin-api): browser, console and Core boundaries and frontend handoff.
+- [Design rules](/concepts) and [contributor guide](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/CONTRIBUTING.md):
+ ownership, security and change requirements.
+
+Generated schemas do not establish complete compatibility or real execution
+support. The [coverage record](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md) identifies
+qualified workflows, native differences and unresolved behavior. Update the
+relevant contract and this index when adding or moving an API surface.
+
+Core administration failures use the [Core error envelope](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/core-errors.md),
+including typed optional safe details and distinct console proxy rejection codes.
+The public and machine error contracts remain unchanged.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/api/README.md)
diff --git a/apps/docs/content/docs/public-api.zh.mdx b/apps/docs/content/docs/public-api.zh.mdx
new file mode 100644
index 000000000..7d2fc3a97
--- /dev/null
+++ b/apps/docs/content/docs/public-api.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "API namespaces and credentials · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+/v1 使用 Project API key;/core/v1 使用 Core key,由 Web 服务端或管理员脚本调用;/api/v1 只接受对应的节点、注册、daemon 或 executor 凭据。这三类凭据不能互换。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/public-api)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/quickstart.mdx b/apps/docs/content/docs/quickstart.mdx
new file mode 100644
index 000000000..b6ebfacd8
--- /dev/null
+++ b/apps/docs/content/docs/quickstart.mdx
@@ -0,0 +1,149 @@
+---
+title: "Call the Agent API"
+description: "Use the pinned SDK, a Project API key and an explicit model provider to run a Session."
+---
+
+Core serves the OpenAI Agents API. Applications use the official OpenAI SDK with two
+environment variables, which the SDK reads by default:
+
+| Variable | Value | From |
+| --- | --- | --- |
+| `OPENAI_BASE_URL` | The installation's API base URL: its public URL followed by `/v1`, such as `https://core.example/v1` | The administrator; Web's **System** page shows it as **API base URL** |
+| `OPENAI_API_KEY` | A Project API key | The administrator issues it on **Projects and keys**; see [Sign in to Web](/install#sign-in-to-web) |
+
+The Project API key authenticates your application to this Core. It is separate from
+the model provider's key that a harness uses, and neither belongs in source control.
+The API base URL reaches Core directly; Web does not serve `/v1`.
+
+## First request
+
+Use the pinned client version:
+
+```sh
+python3 -m venv .venv
+. .venv/bin/activate
+pip install openai==3.13.0
+export OPENAI_BASE_URL=https://core.example/v1
+read -rs OPENAI_API_KEY && export OPENAI_API_KEY # paste the Project API key; it isn't echoed
+```
+
+```python
+from openai import OpenAI
+
+client = OpenAI() # reads OPENAI_BASE_URL and OPENAI_API_KEY
+print(client.beta.agents.list().data)
+```
+
+`read -rs` keeps the key out of your shell history. This read checks access; it runs no
+model and creates no sandbox. The same request with curl, which keeps the key off its
+command line:
+
+```sh
+curl "$OPENAI_BASE_URL/agents" -H "OpenAI-Beta: agents=v1" \
+ -H @<(printf 'Authorization: Bearer %s\n' "$OPENAI_API_KEY")
+```
+
+All keys of a Project share its assets and execution principal; other Projects are
+isolated. When the administrator rotates your key, they issue a new one in the same
+Project and revoke the old one; your assets stay.
+
+## Run a Session
+
+A Core-hosted Session (`openai_hosted`) runs in a sandbox that Core creates: on a node
+with free capacity, or on E2B. Core prepares the daemon, native harness and workspace
+inside it. The installation needs a ready node or an E2B backend; see
+[Nodes](/hosted-providers).
+
+```python
+import os
+
+session = client.beta.agents.sessions.create(
+ environment={"type": "openai_hosted"},
+ input="Create /workspace/hello.txt with a short greeting, then describe it.",
+ extra_body={
+ "agent": {
+ "model": os.environ["MODEL_NAME"],
+ "x_agents_core": {"harness": "codex"},
+ },
+ "x_agents_core": {
+ "model_provider": {
+ "protocol": "responses",
+ "base_url": os.environ["MODEL_BASE_URL"],
+ "api_key": os.environ["MODEL_API_KEY"],
+ }
+ },
+ },
+)
+print(session.id)
+```
+
+This makes a real model request and may incur charges. If the administrator set a
+default model for the harness, omit `x_agents_core.model_provider`; `agent.model` is
+still required, so ask the administrator which model ID the default provider serves.
+SDK 3.13.0 replaces an ordinary body field with the matching `extra_body` field
+instead of merging nested fields, so keep the whole `agent` object in `extra_body`.
+
+## Core extensions: x_agents_core
+
+`/v1` has exactly the official routes. Core's additions are fields inside
+`x_agents_core`, passed through `extra_body`; any other member is rejected with 400.
+
+| Field | Where | Meaning |
+| --- | --- | --- |
+| `x_agents_core.harness` | A saved Agent, or the Session's inline `agent` | Which native harness runs the Agent: `codex` (Codex), `claude_sdk` (Claude Code) or `mcode` (MiniMax Code). Omitted: the installation's default harness (`core.default_harness`, Codex unless changed) |
+| `x_agents_core.model_provider` | A saved Agent, or Session creation | The model provider bundle: `protocol`, HTTPS `base_url`, write-only `api_key`, and for `mcode` also `context_window` and `max_output_tokens`. Reads return `api_key_configured` instead of the key |
+
+The provider's protocol must match the harness:
+
+| Harness | `protocol` | Also required |
+| --- | --- | --- |
+| Codex (`codex`) | `responses` | The provider's exact model ID in `agent.model` |
+| Claude Code (`claude_sdk`) | `anthropic` | The provider's exact model ID |
+| MiniMax Code (`mcode`) | `anthropic` | `context_window` and `max_output_tokens` |
+
+See [harness selection](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/harness-selection.md) and
+[model execution](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/model-execution.md) for the full contract.
+Selecting a harness doesn't make an unsupported model or operation work.
+
+## Model providers
+
+A Session takes its model provider from the first of these that has one; bundles are
+never merged:
+
+1. `x_agents_core.model_provider` in the Session creation request;
+2. the saved Agent's `x_agents_core.model_provider`;
+3. the installation's default model for the harness, set in Web on **System**,
+ **Default model**.
+
+| Environment | Request or saved Agent | Default model | None of them |
+| --- | --- | --- | --- |
+| `openai_hosted` | Used | Used | 400 `model_provider_required` |
+| `self_hosted` | Used | Never | 400 `model_provider_required` |
+| `none` | Rejected with 400 `unsupported_or_invalid_configuration` | Used | Allowed: the device's own environment supplies the model |
+
+A Session freezes its provider when it is created; later changes to the Agent or the
+default affect only new Sessions. Core encrypts the key with the Session and never
+returns it. Self-hosted Sessions always bring their own provider, because the default
+holds the operator's key and the executor host belongs to the application; see
+[Self-hosted executors](/self-hosted-execution).
+
+## Observe and recover
+
+```python
+current = client.beta.agents.sessions.retrieve(session.id)
+turns = client.beta.agents.sessions.turns.list(session.id)
+items = client.beta.agents.sessions.items.list(session.id)
+print(current.id, turns.data, items.data)
+```
+
+Wait for the Turn's final state before treating a task as done. After a disconnect,
+recover through Session, Turn and Items reads: the event stream is live only and does
+not replay history. Don't submit the same work as a new Session when a response is
+lost.
+
+Files, Artifacts, cancellation and their current limits are in the
+[coverage ledger](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md). MCP credentials use the
+separate Vault API; they are neither Project API keys nor model provider keys. The
+[API index](/public-api) lists every route.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/getting-started/quickstart.md)
diff --git a/apps/docs/content/docs/quickstart.zh.mdx b/apps/docs/content/docs/quickstart.zh.mdx
new file mode 100644
index 000000000..e652c1784
--- /dev/null
+++ b/apps/docs/content/docs/quickstart.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Call the Agent API · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+应用设置 OPENAI_BASE_URL=https://core.example.com/v1 和 OPENAI_API_KEY=<project-api-key>,使用文档固定的 SDK 版本。先配置执行后端和模型,再创建 Session 并读取 Turn 结果。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/quickstart)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/self-hosted-execution.mdx b/apps/docs/content/docs/self-hosted-execution.mdx
new file mode 100644
index 000000000..3de987fef
--- /dev/null
+++ b/apps/docs/content/docs/self-hosted-execution.mdx
@@ -0,0 +1,169 @@
+---
+title: "Self-hosted executors"
+description: "Connect a user-owned Runtime to its Environment with a restricted executor credential."
+---
+
+A `self_hosted` Session runs on a machine the application owns, the executor host,
+instead of a node. The application creates the Session through `/v1`; the
+administrator issues an executor credential for it in Web; one command on the host
+starts a Runtime container that connects to Core. The Runtime is the same one nodes
+use: daemon, native harness and workspace.
+
+An executor credential works for one Session's environment only. It can't call the
+Agent API, the Core API or node enrollment.
+
+## Before you start
+
+- **Core has an HTTPS public URL.** The Session's `remote_url` is
+ `wss:///api/v1/agent-daemon/ws`, and the installer connects only over
+ `wss://`. Web shows no command until Core has a reachable public URL.
+- **Web holds the installer and the Runtime files.** The command downloads the
+ installer, the Runtime launcher and the image from Core's public URL, which the
+ reverse proxy sends to Web (in a [split deployment](/install#split-deployment), to
+ the Web host); install from the offline bundle. A console without the self-hosted
+ installer shows no **Connect a host** section at all.
+- **The host** runs Linux amd64 with Python 3.9+, `curl`, `sha256sum` and the Docker
+ CLI, with Docker usable through `/var/run/docker.sock` by the non-root user that runs
+ the installer; the installer refuses root. It reaches the public URL over HTTPS.
+- **The Session carries its model provider**, in the request or saved on its Agent.
+ Default models never apply to self-hosted Sessions, and creating one without a
+ provider fails with 400 `model_provider_required`.
+
+## Connect a host
+
+1. **The application creates the Session**, with its Project API key and its own model
+ provider:
+
+ ```python
+ import os
+ from openai import OpenAI
+
+ client = OpenAI() # reads OPENAI_BASE_URL and OPENAI_API_KEY
+ session = client.beta.agents.sessions.create(
+ environment={"type": "self_hosted", "workspace_directory": "/workspace"},
+ extra_body={
+ "agent": {"model": os.environ["MODEL_NAME"], "x_agents_core": {"harness": "codex"}},
+ "x_agents_core": {"model_provider": {
+ "protocol": "responses",
+ "base_url": os.environ["MODEL_BASE_URL"],
+ "api_key": os.environ["MODEL_API_KEY"],
+ }},
+ },
+ )
+ print(session.id, session.environment.id, session.environment.remote_url)
+ ```
+
+2. **In Web**, open **Session log**, then the Session's page. In its
+ **Executor credentials** section, **Connect a host** shows a command in a
+ **Terminal** block; copy it and run it on the executor host:
+
+ ```sh
+ (umask 077; d=$(mktemp -d) || exit; trap 'rm -rf "$d"' EXIT
+ curl -fsS --max-time 30 --max-filesize 1048576 'https://core.example/node-install/self-hosted-install.pyz' -o "$d/install.pyz" &&
+ printf '%s %s\n' '' "$d/install.pyz" | sha256sum -c --status &&
+ python3 "$d/install.pyz" --source-url 'https://core.example' --environment-id '' --remote 'wss://core.example/api/v1/agent-daemon/ws')
+ ```
+
+ The command contains no secret. It downloads the installer from your console and
+ checks its SHA-256 before running it.
+3. **Issue the credential.** Select **Issue credential**. The **Executor credential**
+ dialog shows it once: choose **Copy credential**.
+4. **Paste it at the installer's hidden prompt** and press Enter. The prompt accepts the
+ compact or the pretty-printed credential and never echoes it. Then choose **Done** in
+ the dialog; after that the credential can't be shown again. Closing the dialog with
+ × keeps the credential on the page until you choose **Done**.
+
+The installer downloads the Runtime from the console and checks it, starts a container
+named `oac-selfhost-<32 hex digits>`, and waits until Core confirms that the
+Environment is connected. The Session then runs its Turns there. Rerunning the same
+command resumes the same installation. It refuses, with "This installation belongs to
+another Environment or distribution", once Core runs another release or its public URL
+changed: the host's installation is tied to both. Moving an executor to a new release
+or address isn't supported yet. A fresh installation would start a new container
+without the old workspace and native history, so the Session can't continue there:
+stop the old container, create a new self-hosted Session and connect a host for it.
+
+For automation without a terminal, choose **Download credential file**, which saves
+`executor-credential-.json`, make it private
+(`chmod 600`), and add `--credential-file /absolute/path/to/that-file.json` to the
+installer line. The path must not go through a symbolic link.
+
+The host keeps its state in `~/.oac/self-hosted//`
+(`--install-dir` chooses another absolute directory). Keep it with the container's
+volumes: they hold the credential, workspace and native history. The Runtime's log is
+`docker logs `. A connected Environment proves the connection, not that the
+model works; send the Session a Turn to test execution.
+
+Core delivers the Session's model provider only over this Environment's executor
+connection. The host keeps it in the Runtime's harness home, where the model's tools
+and the Files API can't read it but the host's owner can.
+
+## Rotate or revoke
+
+| Action in Web | Effect |
+| --- | --- |
+| **Rotate** | The credential gets a new secret; the old secret stops working at once. Rotating a revoked credential restores it |
+| **Revoke** | The credential stops working at once |
+
+After either, the Runtime disconnects and stops retrying: its log shows one message
+that names the fix, and the container keeps running without a restart loop. To
+reconnect the host:
+
+1. **Rotate** the same credential in Web and copy the new secret.
+2. Rerun the same install command on the host and paste it.
+
+The installer puts the new credential into the same container, which reconnects with
+its workspace and history. Issuing a *new* credential doesn't reconnect an Environment
+that has already connected: it stays bound to its first credential, and the installer
+says to rotate that one.
+
+To remove the Runtime instead, stop its container:
+`docker --host unix:///var/run/docker.sock stop `. Deleting the Session does
+not remove containers or volumes on the executor host.
+
+In an archived project, Web shows "This project is archived, so executor credentials
+can't be issued or rotated." Only **Revoke** remains; a host that already holds a
+credential keeps it when you rerun the command.
+
+## Without Web
+
+Scripts on the Core host can issue credentials with the Core key through Core's
+loopback port (`ports.core` in `config.json`, 8091 by default). Choose a new UUID for
+the credential and keep it:
+
+```sh
+key_id=$(python3 -c 'import uuid; print(uuid.uuid4())'); echo "credential ID: $key_id"
+(umask 077; curl -fsS -X POST \
+ -H @<(printf 'Authorization: Bearer %s\n' "$(cat "$HOME/.oac/core/secrets/core.key")") \
+ -H 'Content-Type: application/json' -d "{\"key_id\":\"$key_id\"}" \
+ "http://127.0.0.1:8091/core/v1/projects/$PROJECT_ID/environments/$ENVIRONMENT_ID/executor-credentials" \
+ -o executor-key.json)
+```
+
+Rotate with `{"key_id":"…","rotate":true}` on the same route; revoke with
+`DELETE …/executor-credentials/`. After an uncertain response, list the
+credentials with `GET` before trying again. The
+[credential contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-executor-credentials.md)
+has every rule and error.
+
+
+## Executors installed before the rename
+
+An existing `parsar-selfhost-*` container keeps its own image, daemon and
+`~/.parsar/self-hosted/` directory. It can keep serving its
+Environment after Core is renamed; Core does not upgrade or manage this container.
+
+The new installer refuses when that exact Environment still has a directory under
+`~/.parsar/self-hosted`, including when a different `--install-dir` is supplied.
+Executors for other Environments are unaffected. To replace it, inspect the old
+installation's `started.json` to identify its container, stop and remove that
+exact container, and preserve any history you need before removing the old
+Environment's installation directory. Then rerun the new verified installer. It
+starts `oac-selfhost` with state under `~/.oac/self-hosted/`;
+it does not reuse the old executor's volumes or native history.
+
+Credential replacement for executors created by the new installer still updates
+and restarts the same owned container. The legacy refusal does not authorize
+adopting an old container to replace its credential.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/getting-started/self-hosted.md)
diff --git a/apps/docs/content/docs/self-hosted-execution.zh.mdx b/apps/docs/content/docs/self-hosted-execution.zh.mdx
new file mode 100644
index 000000000..27012a4b9
--- /dev/null
+++ b/apps/docs/content/docs/self-hosted-execution.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Self-hosted executors · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+self_hosted 执行器由用户拥有,Core 不管理它的容器。通过 Web 为对应 Environment 签发 executor 凭据,安装器通过隐藏输入或私有文件读取凭据;不要把凭据放入命令行。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/self-hosted-execution)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/sessions.mdx b/apps/docs/content/docs/sessions.mdx
new file mode 100644
index 000000000..c70cbe237
--- /dev/null
+++ b/apps/docs/content/docs/sessions.mdx
@@ -0,0 +1,97 @@
+---
+title: "Sessions and public resources"
+description: "Agents, Sessions, Turns, events and resources in the pinned public contract."
+---
+
+Applications call Core directly with an API key issued inside a Project. They use
+the public API to create and execute Agents, query history, and manage their assets.
+They never use the Core key or the console's browser session cookie.
+
+## Authentication and base URL
+
+Set `OPENAI_BASE_URL` to the installation's API base URL (`api_base_url` in
+`GET /core/v1/installation`: the public URL followed by `/v1`) and `OPENAI_API_KEY`
+to a Project API key; the official SDK reads both. Raw HTTP callers send
+`Authorization: Bearer `. The fixed SDK supplies
+`OpenAI-Beta: agents=v1` for the Beta Agents resources; raw HTTP callers must add
+it. General Files and Skills endpoints do not require that Beta header.
+
+```python
+from openai import OpenAI
+
+client = OpenAI() # Reads OPENAI_API_KEY and OPENAI_BASE_URL.
+page = client.beta.agents.list()
+```
+
+This read does not execute a model. See the [real execution quickstart](/quickstart)
+for separate model access configuration and a complete Session example.
+
+All keys in a Project share assets and Session creator identity. Revoking one key
+leaves the others valid; archiving the Project revokes all its keys. The optional
+`OpenAI-Organization` and `OpenAI-Project` headers must match `core` and
+`proj_` respectively. Do not send a key's UUID as the project scope.
+
+## Public resources
+
+Paths are relative to `/v1`. Detailed fields, union types and response shapes are
+in the [public OpenAPI](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/openapi.yaml), fixed SDK source
+and linked semantic records. The [resource inventory](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/README.md#upstream-resource-inventory)
+lists each operation and its coverage.
+
+| Resource | Route family | Semantics |
+| --- | --- | --- |
+| Agents | `/agents` | Reusable configuration, CRUD and lists |
+| Sessions and Turns | `/agents/sessions`, nested `/turns` | Admission, metadata, state and recovery queries |
+| Items and events | Session `/items`, `/events` | Persisted history and live SSE |
+| Artifacts | Session `/artifacts` | Captured output metadata, immutable content and deletion |
+| Subagents | Session `/subagents`, nested `/turns` and `/items` | Scoped child execution reads; [native limits](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/subagents.md) |
+| Environments and workspace Files | `/agents/environments`, nested `/files` | [Environment state](/environments-and-files) and [workspace access](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-files.md) |
+| Environment Templates | `/agents/environments/templates` | [Reusable initialization](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-templates.md) |
+| Source Files | `/files` | [Uploaded content resources](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/source-files.md), distinct from live workspace files |
+| Skills and versions | `/skills`, nested `/versions` | [Bundles and versions](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/file-resource-semantics.md) |
+| Vaults and Credentials | `/vaults`, nested `/credentials` | [Scoped write-only credential storage](/environments-and-files) |
+
+SSE is live, not a historical replay service. After a disconnect, query the Session,
+Turns and Items to reconcile state. An accepted request is not proof of completed
+native execution. Do not automatically replay uncertain tool or file effects.
+
+## Core extension fields
+
+The `/v1` route set is exactly the pinned SDK's operations, listed in
+[upstream-routes.json](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/upstream-routes.json); Core adds
+no route. It adds fields only inside `x_agents_core`, because it runs several
+harnesses and accepts custom model access:
+
+| Field | Where | Reference |
+| --- | --- | --- |
+| `x_agents_core.harness` | Saved Agent create, update and read; the inline Session `agent`; the Session's effective `agent` | [Harness selection](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/harness-selection.md) |
+| `x_agents_core.model_provider` | Saved Agent create, update and read; Session creation. It takes `protocol`, `base_url`, optional `context_window` and `max_output_tokens`, and a write-only `api_key`; reads return `api_key_configured` instead of the key | [Model execution](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/model-execution.md) |
+
+Any other member of `x_agents_core` is rejected with 400. Deployment, placement,
+credential issuance and operational reads are not part of `/v1`; they belong to
+`/core/v1`, which only the Core key can call (see the [API index](/public-api)).
+
+A Session's model provider comes from the Session's `x_agents_core.model_provider`,
+then a saved Agent's, then the deployment default that the administrator sets per
+harness. The deployment default applies to `openai_hosted` and `none` Sessions,
+never to `self_hosted`; `none` Sessions accept only the deployment default.
+`openai_hosted` and `self_hosted` Sessions must end up with one: otherwise creation
+returns 400 with code `model_provider_required` and param
+`x_agents_core.model_provider`, and nothing is written. See
+[model execution](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/model-execution.md).
+
+An application reads a Session's model from `agent.model` and an explicitly
+selected harness from `agent.x_agents_core.harness`; a Session on the deployment
+default harness omits `agent.x_agents_core`. `/v1` has no execution-configuration
+read.
+
+`openai_hosted` keeps the official wire name and means Core-managed compute here.
+The deployment chooses E2B or its own Docker/microsandbox nodes. A public
+`self_hosted` Environment uses caller-owned compute and the same colocated
+Runtime. For a `self_hosted` Session, the deployment operator issues the
+Environment's executor credential with the Core key; see
+[executor credentials](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/environment-executor-credentials.md).
+Our daemon transport and the supported native harness differences are
+explicit; they do not imply interoperability with OpenAI's stock executor transport.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/api/public-agent-api.md)
diff --git a/apps/docs/content/docs/sessions.zh.mdx b/apps/docs/content/docs/sessions.zh.mdx
new file mode 100644
index 000000000..87e48cbf8
--- /dev/null
+++ b/apps/docs/content/docs/sessions.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Sessions and public resources · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+Session 是持久对话上下文,绑定一个 Environment。输入事件启动或引导 Turn。Session、Turn 和 Environment 的状态各有含义,不应互相替代;具体字段以公共参考为准。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/sessions)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/docs/troubleshooting.mdx b/apps/docs/content/docs/troubleshooting.mdx
new file mode 100644
index 000000000..3c9cd97f3
--- /dev/null
+++ b/apps/docs/content/docs/troubleshooting.mdx
@@ -0,0 +1,407 @@
+---
+title: "Operations and troubleshooting"
+description: "oac lifecycle commands, backups, key rotation, upgrades and diagnostics."
+---
+
+The installation operator owns the Core host, its storage and its availability. Node
+hosts run their own services; see [Nodes](/hosted-providers). Settings are described in the
+[configuration reference](/configure).
+
+## The oac command
+
+Each installation has its own management command in its directory. It needs neither
+the bundle nor root:
+
+```sh
+~/.oac/core/oac status
+```
+
+| Command | What it does |
+| --- | --- |
+| `oac status` | Shows each service and its health, Core and Web health, the public URL, API base URL, console address and source commit, the reverse-proxy routes to configure, `config.json` changes not applied yet and generated files edited by hand. A Web-only installation also checks that its Core accepts its Core key. Exits non-zero when a service is unavailable. It never calls a model |
+| `oac start` | Starts every service with the files last written by `apply`; warns about unapplied changes |
+| `oac stop` | Stops PostgreSQL, Core and Web. Data, nodes and sandboxes are kept, and running sandbox work may continue |
+| `oac apply` | Applies `config.json` and restarts what changed; see [how apply works](/configure#how-oac-apply-works) |
+| `oac apply --dry-run` | Shows the changed settings, files and restarts, and changes nothing |
+| `oac apply --discard-edits` | Overwrites generated files edited by hand, keeping each as `generated/.edited-` |
+| `oac apply --confirm-public-url-change URL` | Confirms a public URL change without a prompt; must equal the new URL |
+| `oac apply --yes` | Web-only: pairs Web with a different Core without asking |
+| `oac rotate-core-key [--yes]` | Replaces the Core key; see [Rotate the Core key](#rotate-the-core-key) |
+
+`install.sh --status` and `--stop` are retired; they name the `oac` command instead.
+For a second installation, use its own command, such as
+`~/.oac/web/oac status`.
+
+## Service health
+
+Use these observations for different questions:
+
+| Observation | What it establishes |
+| --- | --- |
+| `oac status`, PostgreSQL health | The database accepts its readiness check |
+| Core `/healthz` | Core's process is alive |
+| An authenticated API read | The caller's key works for that resource |
+| Environment connection | The Runtime transport is connected |
+| A finished Turn and its results | The task's recorded outcome |
+
+Service health does not prove that a native harness or a model works. Use Session,
+Turn, Items and Usage reads for execution, and Web's **Nodes** page for node connection,
+readiness and placement. For local diagnosis, use the installation's own Compose file:
+
+```sh
+docker compose -f "$HOME/.oac/core/generated/compose.json" ps --all
+docker compose -f "$HOME/.oac/core/generated/compose.json" logs --tail 200 core
+```
+
+Native Core logs to its user unit: `journalctl --user -u -core.service`, with
+`project` from `state.json`. Don't paste `docker compose config`, `docker inspect` or
+raw logs into public issue reports.
+
+## Stop and restart
+
+Let active work settle before a planned restart:
+
+```sh
+~/.oac/core/oac stop
+~/.oac/core/oac start
+```
+
+Stopping Core stops no node and no sandbox. Node services, their microVMs and Docker
+containers keep running; stopping is not a way to reclaim compute. A Core restart does
+not continue an interrupted native tool call transparently. After reconnecting, query
+the same Session; don't create a new Session to replay uncertain work. Session event
+streams are live only; recover through Session, Turn and Items reads.
+
+A Web restart, including one caused by `oac apply`, signs everyone out of the
+console.
+
+## Core key
+
+Each installation has one administrator credential, the Core key. The installer
+generates a 64-character random key in `/secrets/core.key`, by default
+`~/.oac/core/secrets/core.key`. The Core key:
+
+- signs in to Web. The browser gets an HttpOnly session cookie, never the key;
+- authorizes Core API (`/core/v1`) requests sent as `Authorization: Bearer `;
+- never authorizes the Agent API (`/v1`). Applications use Project API keys, which in
+ turn can't call `/core/v1`.
+
+Keep it private: `secrets/` is mode `0700` and its files `0600`. Of the services, only
+Web reads `core.key`; Core reads its SHA-256 from `generated/core-key-digests.json`,
+which `oac apply` derives from the key. Both read them only at startup. A Core key
+has at least 32 characters and no whitespace. Web limits failed sign-ins.
+
+Scripts run on the Core host, call Core's loopback port and read the key from its file,
+which keeps it off the command line:
+
+```sh
+curl -fsS -H @<(printf 'Authorization: Bearer %s\n' "$(cat "$HOME/.oac/core/secrets/core.key")") \
+ http://127.0.0.1:8091/core/v1/projects
+```
+
+### Rotate the Core key
+
+```sh
+~/.oac/core/oac rotate-core-key
+```
+
+It refuses while `config.json` has changes that are not applied or a generated file was
+edited by hand: run `oac apply` first. It asks for confirmation (`--yes` skips it),
+stops Web, writes a new key to `secrets/core.key` and regenerates the digest file. On a
+running installation it then restarts Core, starts Web and checks that Core accepts the
+new key and refuses the old one; a stopped installation only gets the new files and
+uses the new key at the next `oac start`. The old key stops working as soon as Core
+restarts, and every console session ends: sign in again and update your scripts. If the
+command stops early, `secrets/core.key` holds the key to use; run `oac apply` to
+finish.
+
+A separate Web-only installation keeps its own copy of the key. After rotating, copy
+`secrets/core.key` from the Core host into that installation's `secrets/core.key`
+(mode `0600`) and run its `oac apply`. Its `oac status` reports a key that Core
+refuses. `rotate-core-key` refuses to run on a Web-only installation.
+
+## Projects and API keys
+
+Create Projects and issue their keys in Web, on **Projects and keys**, or with the
+[Core API](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md) under `/core/v1/projects`. A key's
+plaintext is shown once; Core stores only its digest. All keys of a Project share its
+assets and execution principal, and each write records the key that made it.
+
+To rotate an application key, issue another key in the same Project, update the
+application, then **Revoke** the old key. **Archive** disables all of a Project's keys
+and keeps its assets and accepted work. Administrators can inspect and delete retained
+resources but can't execute with the Core key.
+
+## Data and upgrades
+
+### Back up
+
+Back up these together; a restore needs all of them:
+
+- the PostgreSQL volume `_database`. It holds Projects, key digests, nodes,
+ default models, encrypted credentials and all execution history, including large
+ objects. A logical dump:
+
+ ```sh
+ docker compose -f "$HOME/.oac/core/generated/compose.json" exec -T database \
+ pg_dump -U agents_api agents_api > oac-backup.sql
+ ```
+
+- the installation directory: `config.json`, `state.json` (installation ID) and
+ `secrets/`. `credential.key` must stay with the database, or stored credentials can't
+ be decrypted; never regenerate it to get past an error.
+- `state/e2b/`, when E2B is used: receipts Core needs to clean up E2B sandboxes.
+- each node's state directory on its host,
+ `/var/lib/oac-node/.oac/nodes//` (sudo mode) or
+ `~/.oac/nodes//` (no sudo), with its provider storage: Docker
+ volumes or microsandbox's store.
+- the bundle you installed from, to repair or recover the same release.
+
+A missing node state directory is a recovery incident: restore it with the database
+and provider storage rather than registering over existing resources. Never prune
+Docker volumes or delete native history to make a retry pass. A deleted Session does
+not prove that all provider resources were reclaimed.
+
+### Upgrade an installation
+
+| Installation | How to upgrade |
+| --- | --- |
+| Before the OpenAgentCore rename: `state.json` format 1 and a `parsar-…` project, or a legacy `installation.json` layout | [Convert it](#convert-an-earlier-installation) with this bundle |
+| OpenAgentCore, same release | Rerun `./install.sh --install-dir DIR` from the same bundle to repair it |
+| OpenAgentCore, another release | General upgrades are not supported yet; keep the installed release |
+
+There is no downgrade after database migrations. Back up before converting.
+
+### Convert an earlier installation
+
+Use the old Web and old nodes to drain first: enable maintenance, archive hosted
+Sessions until allocations and pending work are both zero, remove every node on the
+Nodes page, and run each node's uninstall command from the previous release.
+Suspended sandboxes, retained snapshots and uncertain cleanup still count. Archived
+Sessions retain their history but cannot resume their old sandbox. Self-hosted
+executors keep running and are not converted.
+
+Extract the new bundle and run:
+
+```sh
+./install.sh --convert
+```
+
+Without `--install-dir`, this converts `~/.parsar/core` into `~/.oac/core`, or resumes
+an unfinished conversion already moved there. An explicit custom directory converts
+in place. `--yes` skips the one confirmation; `--public-url` only settles a legacy
+layout's conflicting public addresses. Convert the Core host before Web-only hosts.
+The latter rename their directory, project and payload without copying a database.
+
+Preflight reports generated-file edits, invalid configuration, destination conflicts,
+insufficient database-copy space, and every available drain blocker. It reads the
+old Core with its Core key. When that Core is stopped and static checks pass, it
+announces a temporary start using only the existing files. Refusal restores the
+services this probe started; a restoration failure is reported explicitly. No
+installation conversion happens before confirmation.
+
+The confirmation prints the backup command for the old project and the directory
+and secret moves. For a config.json installation, take the backup before confirming:
+
+```sh
+docker compose -f "$HOME/.parsar/core/generated/compose.json" exec -T database \
+ pg_dump -U agents_api agents_api > oac-backup.sql
+```
+
+A legacy installation uses `compose.json` at the directory's top level instead.
+Its supported settings move into config.json and secrets move into secrets/ in the
+same run, with no intermediate upgrade or second confirmation. Unknown legacy files
+are reported and left in place; unknown settings and edited derived values refuse.
+
+Conversion stops the old services, copies the stopped database into
+`oac-_database`, removes the old containers and network, and moves the
+default directory atomically. It retains `parsar-_database` as a backup.
+An interrupted copy is restarted only after verifying that its target volume and
+copy container belong to this conversion. A foreign same-name resource is refused.
+
+The new state format is 2; config.json's schema format remains 1. Ports, public URL,
+secrets, Core key, installation ID and database contents are preserved. The old
+`parsar` path becomes a stub naming the new `oac` command. New services use `OAC_*`
+settings and `io.oac.inputs` labels. Docker and microsandbox deployments retain
+their resources, replace their Runtime with the bundle's release through the normal
+maintenance-only API, then resume admission. E2B remains in maintenance: rebuild its
+template with this release's `build-template.py`, replace it in Web, then resume.
+
+Rerun `./install.sh --convert` with the **same bundle** after any interruption,
+including after the directory move. For a custom directory, repeat `--install-dir`.
+The journal rejects a different bundle or installation identity. It verifies Project
+identities and service health before recording completion. Check retained history,
+then add new nodes through **Nodes → Add node**. Keep the old volume until this
+verification is complete; the final output names its exact `docker volume rm`
+command. Never remove the new volume or use a Docker-wide cleanup.
+
+Before the directory move, the old command can restart the old services against the
+untouched old volume. After the move, finish by rerunning conversion. Once new Core
+migrations run, restoring the old release requires the old bundle, old installation
+files and the pre-conversion backup or retained old volume; this is not a downgrade
+of the new database.
+
+### Upgrade notes
+
+These names are retired. Core and Web refuse to start while a retired setting is
+present, and the installers reject retired flags and variables; each error names the
+replacement:
+
+| Retired | Replacement | Where |
+| --- | --- | --- |
+| `AGENTS_API_EXECUTION_OPTIONS_FILE` | Default models, set per harness in Web (**System**, **Default model**) or with `PUT /core/v1/harnesses/{harness}/model-provider` | `config/core.env`. `--convert` does not carry it over; it leaves the file, which may hold model keys, and reports it. Delete the file once the defaults are set |
+| `AGENTS_API_DAEMON_WS_URL` (a `wss://…/api/v1/agent-daemon/ws` URL) | `OAC_PUBLIC_URL` (the origin, such as `https://core.example`), or `public_url` in `config.json` | `config/core.env` |
+| `AGENTS_API_CONFIG_FILE` | None; delete the line | `config/core.env` |
+| `AGENTS_API_MANAGED_RUNTIMES_FILE` | The sandbox deployment in the database, set in Web | `config/core.env`; see [older file-managed installations](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#older-file-managed-installations) |
+| `admin/sandbox-admin.key`, `admin/digests.json` | `admin/core.key`, `admin/core-key-digests.json` (conversion then moves them to `secrets/` and `generated/`) | Files and their `compose.json` mounts |
+| `AGENTS_API_SANDBOX_ADMIN_DIGESTS_FILE` | `OAC_CORE_KEY_DIGESTS_FILE` | `config/core.env` |
+| `CORE_CONSOLE_ADMIN_TOKEN_FILE`, `install.sh --admin-token-file` | `OAC_WEB_CORE_KEY_FILE`, `--core-key-file` | Web environment; installer flag |
+| `CORE_CONSOLE_AUTH_MODE`, `CORE_CONSOLE_STATE_DIR`, `CORE_CONSOLE_PASSWORD_FILE` | None: Web has no accounts or passwords; sign in with the Core key | Web environment, with their `state/console` and `config/console.password` mounts |
+| `install.sh --sandbox-provider`, `--provider` | `install.sh --sandbox`; add the Core host as a node with Add node | Installer flags |
+| `install.sh --status`, `--stop` | `oac status`, `oac stop` | Installer flags |
+| `PARSAR_NODE_ENROLLMENT_TOKEN` | The token on standard input with `--enrollment-token-stdin`, as Web's Add node command passes it | Node installer; it refuses the variable |
+
+The release also renames the following operator identities. Old process settings
+are refused even when empty; errors name all applicable replacements without values.
+These historical names are inputs only to conversion and retirement diagnostics.
+
+| Before | OpenAgentCore |
+| --- | --- |
+| `AGENTS_API_*` process settings | `OAC_*`; `AGENTS_API_ENGINE` becomes `OAC_DEFAULT_HARNESS`, `AGENTS_API_SANDBOX_INSTALLATION_ID` becomes `OAC_INSTALLATION_ID`, and `AGENTS_API_RUNTIME_HISTORY_FILE` becomes `OAC_HISTORY_SETTINGS_FILE` |
+| `CORE_CONSOLE_*` | `OAC_WEB_*`; `CORE_CONSOLE_UPSTREAM` becomes `OAC_WEB_UPSTREAM` |
+| `PARSAR_LOG_*` | `OAC_LOG_*` |
+| Runtime `PARSAR_*`, including `PARSAR_HOME` | `OAC_RUNTIME_*`, including `OAC_RUNTIME_HOME`; separate Parsar-product hooks are unchanged |
+| Developer/build variables and test database/SDK variables | `OAC_DEV_*` and `OAC_TEST_*`; `OAC_TEST_DATABASE_URL` selects the dedicated test database |
+| `~/.parsar/core`, `parsar`, `parsar_cli.py`, `parsar.pyz`, `.parsar.lock` | `~/.oac/core`, `oac`, `oac_cli.py`, `oac.pyz`, `.oac.lock` |
+| `parsar-` project, containers, default network and database volume | `oac-`; the old database volume is retained as a backup |
+| `parsar--core.service`, `PARSAR_INPUTS`, `io.parsar.inputs`, `x-parsar`, `/run/parsar` | `oac--core.service`, `OAC_INPUTS`, `io.oac.inputs`, `x-oac`, `/run/oac` |
+| `parsar-core--linux-amd64` bundle and artifact prefix | `oac--linux-amd64` |
+| `agents-api`, `agents-api-migrate`, `agents-api-device`, `agents-api-environment-key`, `core-console` | `oac-core`, `oac-core-migrate`, `oac-core-device`, `oac-core-environment-key`, `oac-web` |
+| `agents-api-e2b-provider`, `/opt/parsar/e2b` | `oac-e2b-provider`, `/opt/oac/e2b` |
+| `parsar-daemon`, Runtime `agents-api-*` helpers | `oac-daemon`, `oac-*` helpers |
+| `~/.parsar/parsar-daemon`, `/home/runtime/.parsar`, `/run/parsar/daemon-suspend.json` | `~/.oac/daemon`, `/home/runtime/.oac`, `/run/oac/daemon-suspend.json` |
+| `parsar-core-runtime@sha256:…`, `agents-runtime-` | `oac-runtime@sha256:…`, `oac-runtime-` |
+| Runtime `io.parsar.agents-api.*`, microsandbox `io.parsar.*`, `parsar.runtime.placement` | `io.oac.*`, `io.oac.*`, `io.oac.placement` |
+| E2B `parsar_*` metadata, `/opt/parsar-e2b`, `/root/.parsar/e2b`, `/etc/parsar-runtime-env.json` | `oac_*`, `/opt/oac-e2b`, `/root/.oac/e2b`, `/etc/oac-runtime-env.json` |
+| Model-visible `parsar_workspace`, `parsar_worker`, `parsar_root`, `parsar/subagents`, Codex provider `parsar` | `oac_workspace`, `oac_worker`, `oac_root`, `oac/subagents`, provider `oac` |
+
+The PostgreSQL database and role remain `agents_api`. Public API routes, Go module
+paths, source directory names and npm package names are unchanged. Historical
+Session content is never rewritten. Nodes and self-hosted executors require their
+own [node](/hosted-providers) and [self-hosted](/self-hosted-execution) procedures; the Core converter
+does not rename their stores or adopt their resources.
+
+Before converting, rename or remove only the names from before the Core key:
+`admin/sandbox-admin.key`, `admin/digests.json`, `AGENTS_API_SANDBOX_ADMIN_DIGESTS_FILE`
+and the retired `CORE_CONSOLE_*` settings. Leave `AGENTS_API_DAEMON_WS_URL`,
+`AGENTS_API_CONFIG_FILE` and `AGENTS_API_EXECUTION_OPTIONS_FILE` to `--convert`, which
+maps or reports them. Don't add `AGENTS_API_PUBLIC_URL` by hand to a legacy installation: when it is present,
+conversion takes it as the address Core uses and skips the check against the sandbox
+deployment's Core address that keeps existing nodes bound. Hosted and self-hosted
+Sessions that relied on the retired options file
+have no model provider of their own and can't start new work after the upgrade. Count
+them before converting, from the new bundle:
+
+```sh
+python3 model_provider_sessions.py --install-dir "$HOME/.parsar/core"
+```
+
+Recreate them with `x_agents_core.model_provider` or an Agent that has one saved.
+
+The deployment no longer stores a Core address; nodes keep the one they enrolled with.
+For a Core you run without the installer, read `GET /core/v1/sandbox/deployment` with
+the Core key before upgrading and set `OAC_PUBLIC_URL` to its `core_url`; with
+any other value, every existing node counts as bound to another address and must be
+added again.
+
+#### Node connections at /api/v1
+
+Core and its nodes must come from the same distribution; the node installer refuses a
+mismatched release. Current releases serve every machine connection at `/api/v1`, and
+Web returns 404 there. When the new Core and Web go live, route `/api/v1/*`, including
+WebSocket upgrades, to Core instead of Web
+([HTTPS and the reverse proxy](/install#https-and-the-reverse-proxy)); with the old
+routing, node enrollment and every Runtime connection fail, for Docker, microsandbox,
+E2B and self-hosted executors alike.
+
+Nodes from releases that used the removed `/core/v1/sandbox/enroll` and
+`/core/v1/sandbox/node/*` paths can't connect to a current Core. For a Docker or
+microsandbox deployment:
+
+1. Drain with the previous release while its nodes are connected: enter maintenance,
+ archive retained hosted Sessions and wait until nothing is retained (the
+ [maintenance procedure](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/services/agents-api/HOSTED-SANDBOX-MANAGER.md#removal-and-maintenance)).
+2. Upgrade Core and Web, and change the routing as above.
+3. Save the new release's Runtime, resume, and add the nodes again. On each node host,
+ stop the old node service and move its state directory aside as a backup first.
+
+A Web-selected Docker or microsandbox deployment saved before deployment specifications
+existed has an empty specification after migration. Draining it needs a Core that has
+the pre-specification drain mode (pull request #114) but still serves the old node
+paths. No such release was published: build a distribution from main commit
+`7b66be236a627246c85658722314285e6b39d9b8`, or any commit that contains #114 but not the
+move to `/api/v1/sandbox-node`. That Core loads the deployment only to drain; fresh
+sandboxes, node configuration reads and enrollment are refused. Back up, replace Core
+and Web with that build, drain as in step 1, then upgrade to the current release and
+continue with steps 2 and 3. Node IDs change; Session history and persisted Files and
+Artifacts remain. E2B deployments from that period are not covered.
+
+## Troubleshooting
+
+| Symptom | Cause and fix |
+| --- | --- |
+| `Run as a non-root user on Linux amd64 with Docker access` | Run `install.sh` as a normal user, on Linux amd64 |
+| `Installation failed; inspect prerequisites and private deployment files` | A prerequisite failed without its own message, most often Docker: check that `docker info` and `docker compose version` work for this user |
+| `Docker Compose 2.26.0 or newer is required …` | Update the Docker Compose plugin |
+| `Port N is already in use; select another port` | Free the port, or install with `--core-port`/`--web-port` |
+| `Installation directory is not empty …` | Use an empty `--install-dir` |
+| `This installation is configured by …/config.json …` | Flags only seed a new installation: edit `config.json` and run `oac apply` |
+| `This installation predates config.json …` | [Convert it](#convert-an-earlier-installation) |
+| `This installation runs another release …` | See [Upgrade an installation](#upgrade-an-installation) |
+| `generated/ was edited by hand` | Put the change in `config.json`, then `oac apply --discard-edits` |
+| `config.json has changes that are not applied` | Run `oac apply` |
+| `Core rejects secrets/core.key …` | Run `oac apply`, which restarts Core with the key's digest |
+| `config.json not applied: …` | `oac apply` printed Core's startup error above; fix `config.json` and apply again |
+| Web answers 403 `Forbidden` | Open exactly the console address `oac status` prints; the reverse proxy must pass the original Host |
+| `/v1` or `/api/v1` answers 404 | Those paths reach Web; route them to Core ([reverse proxy](/install#https-and-the-reverse-proxy)) |
+| Web shows that Core is unavailable (502) | Core is stopped or failing: `oac status`, then Core's log |
+| Session creation returns 400 `model_provider_required` | No model provider: set a [default model](/configure#default-models) for the harness, or pass one; self-hosted Sessions always pass their own |
+| Add node says nodes need an HTTPS public URL | Set `public_url` and run `oac apply` |
+| Add node says the console has no node files | Install from the offline bundle, or add the node files and rerun `./install.sh` |
+| A node is not ready | See [node troubleshooting](/hosted-providers#troubleshooting) |
+
+## Exposure and network policy
+
+Core and Web listen on host loopback; PostgreSQL has no published port unless Core is
+native. Your TLS reverse proxy routes `/v1` and `/api/v1` to Core and everything else to
+Web. Web signs administrators in with the Core key, checks the origin of every request,
+and forwards signed-in `/core/v1` requests to Core with the Core key, which stays on
+the server. It answers 404 on `/v1` and `/api/v1` whatever credential a request carries,
+serves only the non-secret node payload at `/node-install/`, and has no Docker or KVM
+access. Machine routes keep their own enrollment and connection credentials. Web
+sign-in sessions live in Web's memory and last 12 hours.
+
+microsandbox uses an explicit network policy: public egress, the Core and DNS ports the
+Runtime needs, and no inbound or private-network access. Private model or MCP endpoints
+need an explicit policy change. A Session's own network policy is separate.
+
+The Docker node uses the nested-sandbox Runtime policy. Its service account needs the
+host's Docker daemon, and Core has no Docker socket. Install nodes on trusted hosts and
+don't share their Docker access with untrusted users.
+
+A Core restart leaves node services and resident microVMs running. A host reboot, the
+end of a user manager or the loss of a running microVM is not a Core restart and is not
+a qualified recovery workflow. Idle snapshots keep their recovery contract.
+
+### API-key write history
+
+Core records which key made each public resource write, for the console. Set
+`core.write_audit_retention` in `config.json` (Go duration, at least `1h`, default
+`2160h`) to control how long non-creation history is kept; creation ownership is kept
+for good. Removing keys or resources does not delete these records. See the
+[query contract](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/write-audit.md); administrator mutations
+have a separate
+[audit log](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/contracts/agents-api/admin-api.md#monitoring-and-audit). Neither
+logs bodies or secrets.
+
+[Repository source](https://github.com/MiniMax-AI/parsar-core/blob/75bf4484ba957344ae374354858bbdfe104f5c4f/docs/getting-started/operations.md)
diff --git a/apps/docs/content/docs/troubleshooting.zh.mdx b/apps/docs/content/docs/troubleshooting.zh.mdx
new file mode 100644
index 000000000..9e1a5db31
--- /dev/null
+++ b/apps/docs/content/docs/troubleshooting.zh.mdx
@@ -0,0 +1,10 @@
+---
+title: "Operations and troubleshooting · 中文导读"
+description: "当前版本的中文导读;完整操作步骤见英文正文。"
+---
+
+使用 oac status 查看安装状态,oac doctor 诊断配置,oac apply 应用 config.json。备份必须同时保留数据库、配置和密钥。升级按当前指南执行;不要假设在线 generation 更新或 reset 已实现。
+
+此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/troubleshooting)。
+
+[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)
diff --git a/apps/docs/content/guide-sources.json b/apps/docs/content/guide-sources.json
new file mode 100644
index 000000000..e768b0c2b
--- /dev/null
+++ b/apps/docs/content/guide-sources.json
@@ -0,0 +1,61 @@
+{
+ "revision": "75bf4484ba957344ae374354858bbdfe104f5c4f",
+ "sources": {
+ "docs/getting-started/README.md": "e4ca3bcbfdcbf3832c1d9e0525f98083ca11a4e42148f9f45090bbe1bc0f2b5c",
+ "docs/design-principles.md": "5e1bff3cdcc77cd02ad692f67977cb4e0a5654989982d722bac0e3c79dbf82cb",
+ "docs/web/architecture.md": "23e5758d72687c2c821975f326f24a789545ce0b0f566802cbe901edf2ebe3f1",
+ "docs/getting-started/install.md": "15cfe81d4005a7337ac82bf6c35a8b441e9f9b0f2a2299021ad6c539f3f336a3",
+ "docs/configuration.md": "7fe061b93a09c0f7baaa70f33f0863d8b29d308bbbc9b0e42b35cbef0ef0c648",
+ "docs/web/core-connection.md": "46f86520e70bf41079708e2c398655aa9087ad9488c8e661d9a73035d11098c4",
+ "docs/getting-started/quickstart.md": "5effeffcfb343f7939910f0c1758e1492d49e7bd66c409b1819cefe67af592d7",
+ "docs/api/README.md": "f8c3872b012af7c490a06578c7d56560e0ec5998865ef043523bc66cc10ae4be",
+ "contracts/agents-api/execution-tools.md": "e0b61f6c0c236c362186c5f6d0ad69a16dd8a8d9afabe1329a1fe22e5f4a71e9",
+ "docs/api/public-agent-api.md": "18829b366bd2e49fe5beb76dc8a709bc1aa8e35d4fbfd2484a0c52b46cf75417",
+ "contracts/agents-api/environments.md": "89ffc903ee44d00ee87c16950202a3ca4c8345160780dcdd4ed2a71b25b1c2dc",
+ "docs/getting-started/nodes.md": "00d98f03d0ac967575bfa35ee960ff24b971bff1531a264517b384376986ac6f",
+ "docs/getting-started/self-hosted.md": "3f1b402cf28bb320ba414eaf36110e9df4d792b81da0456a3c42d469357d8d99",
+ "apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2",
+ "docs/web/README.md": "3b96e673591da67a6f666dddafd49a565c066c347bdfa2eaba0f92ab03daf514",
+ "docs/api/web-management.md": "5480bfcf9dd24a8ffc8d442e4d8582e007dadd22dba6a686d71b4bede0616506",
+ "contracts/agents-api/runtime-observability-api.md": "cd46e777fe716a7e76374f4655c96dfbad9d8be1bfca6203ed6118ab87874c2b",
+ "docs/getting-started/operations.md": "abc965e6ba19d2a2c148c8b5afde7203ca39891383840d85379d0d433b39df1d",
+ "apps/docs/scripts/guides.json": "9d1ba424f8fadd65ffa6175eaa9c7bc4878d3e1437971099812c984e1e593080"
+ },
+ "outputs": {
+ "content/docs/index.mdx": "2cd2e1d7adb6b38bfb1808bc5f9d9b2f699d02b02eb233c0ee72438884dffe9a",
+ "content/docs/index.zh.mdx": "5bdd41893946f1d229eb4cf1599a97f2e38cb9e09722d2a69f62053376ac88a9",
+ "content/docs/concepts.mdx": "32120464dd98c1b80d5d4c57d7f2c7e4365f623532024a632b9fccbda730384a",
+ "content/docs/concepts.zh.mdx": "c60644f395dbac5ee607cdb053249e05ec8352d28de97ef1c38d6c1e4443bbb1",
+ "content/docs/execution-model.mdx": "b3c928e353e173aa7eea5c4fbad06b6896fd1e97f69c87701de8af39fae054fe",
+ "content/docs/execution-model.zh.mdx": "9976a9203c6a83d49c2c753e3a8528a369078e87aeb88850654da948ac8c17e1",
+ "content/docs/install.mdx": "439809c11f5d805e6c6f964c779ec01468dcdad88bd11a56bb180a6e6a13b98a",
+ "content/docs/install.zh.mdx": "7ed0b726cf721b18e85b89145ef8ebd2f5d382afd0489633a7b63962aa2fc4ed",
+ "content/docs/configure.mdx": "4ab159c54f7b9c6e1a2d0c3873de5e3641b48c4e1badf47f63311bef94f8778f",
+ "content/docs/configure.zh.mdx": "a0c3a1df3e9f75e672e1d0a8c68c82c24ac36a8479d10a951de441960d07927f",
+ "content/docs/bootstrap-projects-keys.mdx": "4a39e95986851828e3fbcc0550398146de9bb37cfe22f10df566c51a911ca9ed",
+ "content/docs/bootstrap-projects-keys.zh.mdx": "509b4dcea4c865d6834bcaef78baa681180d48274931230f958a39e46011c88f",
+ "content/docs/quickstart.mdx": "92f62b5df213c363bd7148de0462b2ca66f2054f4458d790196e75daa678bb24",
+ "content/docs/quickstart.zh.mdx": "c5f119b100b102918a45763834d0ed7fd436479fb411e16a62a408411dbcb3e1",
+ "content/docs/public-api.mdx": "a3d39d873054614bdf410d8c2a1e5c33320f1f3bffe155657b64579ea28fdc48",
+ "content/docs/public-api.zh.mdx": "fb0efb9be2fba3f4a3dc71bf053d2c7eddffbfe010f59fa5a1ae60a3f068545b",
+ "content/docs/agents-and-tools.mdx": "78789f7e6a1d327ff514d872b9f8b55a88754ffbfc34b7b10e5f7c115f242e3c",
+ "content/docs/agents-and-tools.zh.mdx": "cc8d4e0ef0ea894f068f3bbd143d5ee70c6346bf98f425b1d5fe07ec3931f128",
+ "content/docs/sessions.mdx": "eaef17849bce13c9f3c22966910e081fcc4851c59a7a8b972e917a45bdebfb57",
+ "content/docs/sessions.zh.mdx": "b1b1a7f8aacd9a902320f9692173ede24036124f85761cf02f23ef3ef2bcc4c1",
+ "content/docs/environments-and-files.mdx": "411328cb87962ee9849021ed14de144ab9d4b31ecb8a42954079c9142d479e98",
+ "content/docs/environments-and-files.zh.mdx": "ee64577220a798f023b330c65302c14438f25a34a78c4e36144bfdb4304a3ae5",
+ "content/docs/hosted-providers.mdx": "7d1825a5ef7e81d71a0c64bd00ce8fcc3c00d632a318eaa4063dacf876fdfee5",
+ "content/docs/hosted-providers.zh.mdx": "f54d80d6e65596dca6b20262abaa3dcb5af245227ffad146fcaed4bd69454dee",
+ "content/docs/self-hosted-execution.mdx": "67a134939247a5a3d16b4a935895259c37d8264377de53179e6598bdb82957d5",
+ "content/docs/self-hosted-execution.zh.mdx": "f11d34ce30d48039376256474bc9509a508a2f6b9906f49b5cf6e396707ae5c0",
+ "public/images/source/apps/web/public/onboarding/monitor-en.webp": "29dc220cb1250c7016b7c4bf7f30e9510b07c07b814a48c3aa9303b2d76f20b2",
+ "content/docs/console.mdx": "a708f26941840375299f14b26dc69a278c1f44a7aedca90d50bb806ba05685c0",
+ "content/docs/console.zh.mdx": "72f953ba46dcc95933773ac0c4dffdc0d0a499be9d9a1cb9467db2659f1fa10d",
+ "content/docs/admin-api.mdx": "23b0ebdbb3ab93cee6f93e083026318465fae581546de5af9953fbf779444452",
+ "content/docs/admin-api.zh.mdx": "07006de30df3080e54c3cd79ed0ea5337dce557aa2f11afea84edb7f92884d38",
+ "content/docs/observability.mdx": "4894808be45568b3d6e1039183350a5315dbbde1144db94cc592eb8bd8c433e4",
+ "content/docs/observability.zh.mdx": "9574b3412a5754f41ab2d3a16062ac1cad06766be481a8e6bff1b84acbde7e7e",
+ "content/docs/troubleshooting.mdx": "2008f1bcdc5e8cfb55602e2d849093140a61682c68795d9c9f707d62e30b0c0a",
+ "content/docs/troubleshooting.zh.mdx": "a47714ea9890385e811b163bbb2dc1412ddd9c12747792cf53630fc06062e5e2"
+ }
+}
diff --git a/apps/docs/lib/i18n.ts b/apps/docs/lib/i18n.ts
new file mode 100644
index 000000000..6ec2bbf10
--- /dev/null
+++ b/apps/docs/lib/i18n.ts
@@ -0,0 +1,10 @@
+import { defineI18n } from "fumadocs-core/i18n"
+
+export const i18n = defineI18n({
+ languages: ["en", "zh"],
+ defaultLanguage: "en",
+ hideLocale: "default-locale",
+ parser: "dot",
+})
+
+export type Lang = (typeof i18n.languages)[number]
diff --git a/apps/docs/lib/legacy-redirects.test.ts b/apps/docs/lib/legacy-redirects.test.ts
new file mode 100644
index 000000000..b0c6eb243
--- /dev/null
+++ b/apps/docs/lib/legacy-redirects.test.ts
@@ -0,0 +1,55 @@
+import assert from "node:assert/strict"
+import { existsSync } from "node:fs"
+import { join } from "node:path"
+import test from "node:test"
+import { i18n } from "./i18n.ts"
+import { LEGACY_TARGETS, legacyTarget } from "./legacy-redirects.ts"
+
+// Resolved from either the repo root or the app directory, so the suite runs
+// whether `node --test` is started from the app or from the repository root.
+const contentRoots = [
+ join(process.cwd(), "content", "docs"),
+ join(process.cwd(), "apps", "docs", "content", "docs"),
+]
+
+/**
+ * A page counts as live only when every declared language has a source file.
+ * A redirect landing on an untranslated page would hand a Chinese reader an
+ * English page, which is the failure these redirects exist to prevent.
+ */
+function isLivePage(target: string): boolean {
+ const stem = target === "/" ? "index" : target.replace(/^\//, "")
+ return contentRoots.some((root) =>
+ i18n.languages.every((lang) =>
+ existsSync(
+ join(root, lang === i18n.defaultLanguage ? `${stem}.mdx` : `${stem}.${lang}.mdx`),
+ ),
+ ),
+ )
+}
+
+test("every redirect target is a page that exists in every language", () => {
+ for (const [from, to] of Object.entries(LEGACY_TARGETS)) {
+ assert.ok(isLivePage(to), `${from} -> ${to} is not a translated page`)
+ }
+})
+
+test("no retired slug is still published", () => {
+ for (const from of Object.keys(LEGACY_TARGETS)) {
+ assert.ok(!isLivePage(`/${from}`), `${from} is mapped but is still a live page`)
+ }
+})
+
+test("legacyTarget resolves with or without surrounding slashes", () => {
+ assert.equal(legacyTarget("/api-keys"), "/bootstrap-projects-keys")
+ assert.equal(legacyTarget("api-keys"), "/bootstrap-projects-keys")
+ assert.equal(legacyTarget("/api-keys/"), "/bootstrap-projects-keys")
+})
+
+test("legacyTarget leaves live pages and the root to the router", () => {
+ assert.equal(legacyTarget("/configure"), undefined)
+ assert.equal(legacyTarget("/public-api"), undefined)
+ assert.equal(legacyTarget("/environments-and-files"), undefined)
+ assert.equal(legacyTarget("/"), undefined)
+ assert.equal(legacyTarget(""), undefined)
+})
\ No newline at end of file
diff --git a/apps/docs/lib/legacy-redirects.ts b/apps/docs/lib/legacy-redirects.ts
new file mode 100644
index 000000000..2aeb661c9
--- /dev/null
+++ b/apps/docs/lib/legacy-redirects.ts
@@ -0,0 +1,70 @@
+/**
+ * Slugs retired when the manual was rewritten around the administrator-API
+ * baseline. Keys are the previous slugs without a locale prefix; values are the
+ * surviving page that now covers the topic, or `/` for a topic that was folded
+ * into an overview.
+ *
+ * `middleware.ts` answers these with a permanent redirect and carries the
+ * reader's language across, so `/zh/api-keys` lands on
+ * `/zh/bootstrap-projects-keys` instead of the English page.
+ *
+ * `legacy-redirects.test.ts` asserts every target is a page that still exists
+ * in every language, and that no retired slug is still published — so this map
+ * cannot silently rot as the manual changes.
+ */
+export const LEGACY_TARGETS: Record = {
+ // Folded into the overview page; no single successor page.
+ "project-map": "/",
+ "console-tour": "/",
+ glossary: "/",
+ "local-development": "/",
+ "locales-themes": "/",
+
+ // Renamed, or folded into a surviving page.
+ "execution-flow": "/execution-model",
+ "install-core": "/install",
+ "configure-core": "/configure",
+ "console-admin": "/admin-api",
+ credentials: "/configure",
+ "api-keys": "/bootstrap-projects-keys",
+ connection: "/configure",
+ verification: "/install",
+
+ // Agent and Session topics.
+ agents: "/agents-and-tools",
+ "create-agent": "/agents-and-tools",
+ "agent-tools": "/agents-and-tools",
+ vaults: "/agents-and-tools",
+ "start-session": "/sessions",
+ conversation: "/sessions",
+
+ // Operations topics.
+ dashboard: "/observability",
+ metrics: "/observability",
+ trace: "/observability",
+ system: "/hosted-providers",
+
+ // Environment, file and provider topics.
+ environment: "/environments-and-files",
+ "environment-templates": "/environments-and-files",
+ files: "/environments-and-files",
+ "sandbox-nodes": "/hosted-providers",
+ "hosted-sandbox-manager": "/hosted-providers",
+ "connect-daemon": "/self-hosted-execution",
+ "connect-executor": "/self-hosted-execution",
+
+ // The public Agent API replaced three separate API pages.
+ "agents-api": "/public-api",
+ "agents-api-sessions": "/public-api",
+ "agents-api-errors": "/public-api",
+}
+
+/**
+ * Resolve a retired path to its successor. `rest` is the path with any locale
+ * prefix already removed; surrounding slashes are ignored, and a live page or
+ * the root resolves to `undefined` so the caller can carry on routing.
+ */
+export function legacyTarget(rest: string): string | undefined {
+ const key = rest.replace(/^\/+/, "").replace(/\/+$/, "")
+ return key ? LEGACY_TARGETS[key] : undefined
+}
\ No newline at end of file
diff --git a/apps/docs/lib/locale-link.test.ts b/apps/docs/lib/locale-link.test.ts
new file mode 100644
index 000000000..e6f8b599b
--- /dev/null
+++ b/apps/docs/lib/locale-link.test.ts
@@ -0,0 +1,69 @@
+import assert from "node:assert/strict"
+import test from "node:test"
+import { i18n } from "./i18n.ts"
+import { prefixLocale } from "./locale-link.ts"
+
+// The rule this file locks down: a *translated* page must never hand the reader
+// back to the default language. `[Configure](/configure)` written in
+// `configure.zh.mdx` has to render as `/zh/configure`.
+test("a translated page keeps prefix-free internal links in its own language", () => {
+ assert.equal(prefixLocale("/configure", "zh", i18n), "/zh/configure")
+ assert.equal(prefixLocale("/configure/", "zh", i18n), "/zh/configure/")
+ assert.equal(prefixLocale("/", "zh", i18n), "/zh")
+ assert.equal(prefixLocale("/#section", "zh", i18n), "/zh#section")
+ assert.equal(prefixLocale("/public-api?tab=2", "zh", i18n), "/zh/public-api?tab=2")
+})
+
+test("the default language stays prefix-free", () => {
+ assert.equal(prefixLocale("/configure", "en", i18n), "/configure")
+ assert.equal(prefixLocale("/", "en", i18n), "/")
+})
+
+test("a link that already carries a locale is left alone", () => {
+ // Hand-written prefixed links must not become `/zh/zh/...`.
+ assert.equal(prefixLocale("/zh/configure", "zh", i18n), "/zh/configure")
+ // A link into another language is deliberate; do not rewrite it.
+ assert.equal(prefixLocale("/en/configure", "zh", i18n), "/en/configure")
+ assert.equal(prefixLocale("/zh", "zh", i18n), "/zh")
+})
+
+test("non-page links are never prefixed", () => {
+ const untouched = [
+ "https://github.com/MiniMax-AI/parsar-core",
+ "http://127.0.0.1:18092/core/v1/admin/summary",
+ "mailto:docs@example.com",
+ "tel:+1234567890",
+ "//example.com/path",
+ "#local-anchor",
+ "./sibling",
+ "../parent",
+ "relative/page",
+ "",
+ ]
+
+ for (const href of untouched) {
+ assert.equal(prefixLocale(href, "zh", i18n), href, `${href} must not be rewritten`)
+ }
+})
+
+test("a path that merely starts with a locale name is still prefixed", () => {
+ // `/environment` must not be mistaken for an `/en`-prefixed link, and
+ // `/zh-tw` is not a locale this site declares.
+ assert.equal(prefixLocale("/environment", "zh", i18n), "/zh/environment")
+ assert.equal(prefixLocale("/zh-tw/guide", "zh", i18n), "/zh/zh-tw/guide")
+})
+
+test("every declared language is recognised as a prefix", () => {
+ for (const lang of i18n.languages) {
+ assert.equal(prefixLocale(`/${lang}/configure`, "zh", i18n), `/${lang}/configure`)
+ }
+})
+
+test("translating the default language into itself is a no-op", () => {
+ // Guards the `lang === defaultLanguage` short circuit: if it ever stops
+ // firing, every English internal link gains an `/en` prefix the router then
+ // redirects away from.
+ for (const href of ["/configure", "/", "/a/b", "#x", "https://example.com"]) {
+ assert.equal(prefixLocale(href, i18n.defaultLanguage, i18n), href)
+ }
+})
\ No newline at end of file
diff --git a/apps/docs/lib/locale-link.ts b/apps/docs/lib/locale-link.ts
new file mode 100644
index 000000000..0cc6cee80
--- /dev/null
+++ b/apps/docs/lib/locale-link.ts
@@ -0,0 +1,53 @@
+/**
+ * The part of the site's i18n configuration that link rewriting depends on.
+ *
+ * Passed in rather than imported so this module stays a pure function with no
+ * runtime dependency, which lets `locale-link.test.ts` load it directly under
+ * `node --test` without a bundler or path-alias resolution.
+ */
+export type LocaleLinkConfig = {
+ languages: readonly string[]
+ defaultLanguage: string
+}
+
+/**
+ * Add the active locale prefix to a root-relative link written in MDX, so that
+ * navigation inside a translated page stays in that language.
+ *
+ * Fumadocs' loader keeps the default language prefix-free
+ * (`hideLocale: "default-locale"`), so `[Configure](/configure)` written in
+ * `configure.zh.mdx` would otherwise render as ``: the
+ * reader leaves the Chinese manual and lands on the English page with no
+ * visible reason. The docs route therefore renders every MDX anchor through
+ * `LocaleLink`, so pages can be written with locale-free internal links.
+ *
+ * Deliberately left untouched:
+ * - absolute URLs with a scheme (`https:`, `mailto:`, `tel:`, …)
+ * - protocol-relative URLs (`//host/path`)
+ * - in-page anchors (`#section`)
+ * - relative paths (`./x`, `../x`)
+ * - paths that already carry a declared locale
+ * - every link on the default language, whose URLs stay prefix-free
+ */
+export function prefixLocale(
+ href: string,
+ lang: string,
+ { languages, defaultLanguage }: LocaleLinkConfig,
+): string {
+ if (!href) return href
+ if (lang === defaultLanguage) return href
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href)) return href
+ if (href.startsWith("//")) return href
+ if (href.startsWith("#")) return href
+ if (!href.startsWith("/")) return href
+
+ const first = href.split("/").filter(Boolean)[0]
+ if (first && languages.includes(first)) return href
+
+ // The root page is served at `/zh`, not `/zh/`, so a link to the root — with
+ // or without an anchor or query — drops the separator rather than keeping it.
+ if (href === "/") return `/${lang}`
+ if (href.startsWith("/#") || href.startsWith("/?")) return `/${lang}${href.slice(1)}`
+
+ return `/${lang}${href}`
+}
\ No newline at end of file
diff --git a/apps/docs/lib/openapi.ts b/apps/docs/lib/openapi.ts
new file mode 100644
index 000000000..c41dbaf49
--- /dev/null
+++ b/apps/docs/lib/openapi.ts
@@ -0,0 +1,45 @@
+// Registers the OpenAPI documents the reference pages render.
+//
+// Generated MDX names its surface by id rather than by file path so the pages
+// stay portable across checkout locations. `scripts/generate-api-reference.mjs`
+// owns the ids and the files; add an entry here when it gains a surface.
+//
+// `getSchemas()` keys its result by the very string passed as `input`, so the
+// lookup below goes through the same `absolute` map that built the input.
+
+import { createOpenAPI } from "fumadocs-openapi/server"
+import path from "node:path"
+
+export const SURFACES = {
+ "public-api": "openapi/public-api.yaml",
+ "core-api": "openapi/core-api.yaml",
+ "runtime-api": "openapi/runtime-api.yaml",
+} as const
+
+export type SurfaceId = keyof typeof SURFACES
+
+const absolute = Object.fromEntries(
+ Object.entries(SURFACES).map(([id, relative]) => [
+ id,
+ path.join(process.cwd(), relative),
+ ]),
+) as Record
+
+export const openapi = createOpenAPI({ input: Object.values(absolute), disablePlayground: true })
+
+/** Reads one surface, by the id the generated MDX refers to. */
+export async function loadSurface(id: string) {
+ const file = absolute[id as SurfaceId]
+ if (!file) {
+ throw new Error(
+ `Unknown API surface "${id}". Known surfaces: ` +
+ `${Object.keys(SURFACES).join(", ")}.`,
+ )
+ }
+ const schemas = await openapi.getSchemas()
+ const document = schemas[file]
+ if (!document) {
+ throw new Error(`API surface "${id}" was not processed from ${file}.`)
+ }
+ return document
+}
diff --git a/apps/docs/lib/site.ts b/apps/docs/lib/site.ts
new file mode 100644
index 000000000..a0a0516a0
--- /dev/null
+++ b/apps/docs/lib/site.ts
@@ -0,0 +1,86 @@
+import { existsSync } from "node:fs"
+import { join } from "node:path"
+import { i18n } from "./i18n"
+import { source } from "./source"
+
+/**
+ * Canonical origin of the published manual, used only to build absolute URLs
+ * for crawler metadata: the per-page canonical link, the hreflang alternates
+ * and the sitemap. Set `DOCS_SITE_ORIGIN` to the real origin before publishing
+ * the manual; the default matches the local review server.
+ */
+export const SITE_ORIGIN = (
+ process.env.DOCS_SITE_ORIGIN ?? "http://127.0.0.1:4001"
+).replace(/\/+$/, "")
+
+/**
+ * Absolute URL of a page from its Fumadocs-relative url (`/configure`,
+ * `/zh/configure`). The home page arrives as `/`, which would serialize with a
+ * trailing slash that the route does not serve; strip it so the sitemap entry
+ * and the page's own canonical link stay byte-identical.
+ */
+export function absoluteDocsUrl(relative: string): string {
+ return `${SITE_ORIGIN}${relative === "/" ? "" : relative}`
+}
+
+/** Content directories, so the check works from the repo root or from the app. */
+function contentRoots(): string[] {
+ return [
+ join(process.cwd(), "content", "docs"),
+ join(process.cwd(), "apps", "docs", "content", "docs"),
+ ]
+}
+
+function pageStem(slugs: string[]): string {
+ return slugs.length === 0 ? "index" : slugs.join("/")
+}
+
+/**
+ * Whether a translated source file exists for a page.
+ *
+ * `source.getPage(slugs, lang)` answers for the default language when a
+ * translation is missing, so it cannot tell "translated" from "fell back".
+ * The file system can.
+ */
+export function hasLocalizedPage(slugs: string[], lang: string): boolean {
+ const stem = pageStem(slugs)
+ const candidates =
+ lang === i18n.defaultLanguage
+ ? [`${stem}.mdx`, `${stem}/index.mdx`]
+ : [`${stem}.${lang}.mdx`, `${stem}/index.${lang}.mdx`]
+
+ return contentRoots().some((root) =>
+ candidates.some((candidate) => existsSync(join(root, candidate))),
+ )
+}
+
+/**
+ * Next.js `metadata.alternates` for a docs page:
+ * - `canonical` points at the default-language version, so search engines
+ * consolidate ranking signals on one URL instead of treating the English
+ * and Chinese pages as duplicates
+ * - `languages` declares every translation under its hreflang code
+ * - `x-default` points at the canonical URL for unmatched audiences
+ *
+ * A page translated into one language still gets a valid block; only what
+ * exists is declared.
+ */
+export function docsAlternates(slugs: string[]): {
+ canonical: string
+ languages: Record
+} {
+ const languages: Record = {}
+
+ for (const lang of i18n.languages) {
+ if (!hasLocalizedPage(slugs, lang)) continue
+ const page = source.getPage(slugs, lang)
+ if (!page) continue
+ languages[lang] = absoluteDocsUrl(page.url)
+ }
+
+ const canonical =
+ languages[i18n.defaultLanguage] ?? Object.values(languages)[0]
+ if (canonical) languages["x-default"] = canonical
+
+ return { canonical: canonical ?? absoluteDocsUrl("/"), languages }
+}
diff --git a/apps/docs/lib/source.ts b/apps/docs/lib/source.ts
new file mode 100644
index 000000000..761a26f9f
--- /dev/null
+++ b/apps/docs/lib/source.ts
@@ -0,0 +1,9 @@
+import { docs } from "@/.source"
+import { loader } from "fumadocs-core/source"
+import { i18n } from "./i18n"
+
+export const source = loader({
+ baseUrl: "/",
+ source: docs.toFumadocsSource(),
+ i18n,
+})
diff --git a/apps/docs/middleware.ts b/apps/docs/middleware.ts
new file mode 100644
index 000000000..bff1a2a5a
--- /dev/null
+++ b/apps/docs/middleware.ts
@@ -0,0 +1,62 @@
+import { NextResponse, type NextRequest } from "next/server"
+import { i18n } from "@/lib/i18n"
+import { legacyTarget } from "@/lib/legacy-redirects"
+
+export default function middleware(request: NextRequest) {
+ const pathname = request.nextUrl.pathname
+ const segments = pathname.split("/").filter(Boolean)
+ const pathLocale = i18n.languages.find((locale) => segments[0] === locale)
+ const rest = `/${segments.slice(pathLocale ? 1 : 0).join("/")}`
+
+ // Retired slugs first, so a reader following a link into the previous manual
+ // reaches the page that replaced it instead of a 404. Handled here rather than
+ // in `next.config.mjs` to keep the locale prefix and the redirect in one hop:
+ // `/en/api-keys` collapses to `/bootstrap-projects-keys` directly, instead of
+ // first stripping the prefix and then redirecting a second time.
+ const legacy = legacyTarget(rest)
+ if (legacy) {
+ const target =
+ pathLocale && pathLocale !== i18n.defaultLanguage
+ ? `/${pathLocale}${legacy === "/" ? "" : legacy}`
+ : legacy
+ const url = new URL(target, request.url)
+ // `new URL(path, base)` drops the base query, which would silently lose
+ // campaign and referral parameters on the redirect.
+ url.search = request.nextUrl.search
+ return NextResponse.redirect(url, 308)
+ }
+
+ // The default language is served prefix-free, so its canonical URL has to
+ // reach the `[lang]` route without a redirect the reader can see.
+ if (!pathLocale) {
+ const url = new URL(request.url)
+ url.pathname = `/${i18n.defaultLanguage}${pathname === "/" ? "" : pathname}`
+ return NextResponse.rewrite(url)
+ }
+
+ if (pathLocale === i18n.defaultLanguage) {
+ // An explicit default-language prefix is not the canonical URL, so this is
+ // permanent: the docs route serves the default language prefix-free.
+ const stripped = pathname.slice(`/${pathLocale}`.length)
+ const target = stripped || "/"
+ const url = new URL(target, request.url)
+ url.search = request.nextUrl.search
+ return NextResponse.redirect(url, 308)
+ }
+
+ return NextResponse.next()
+}
+
+export const config = {
+ // Locale routing must not touch API routes, Next's own assets, or anything
+ // served as a file from `public/`. Without the `images/` exclusion the rewrite
+ // turns `/images/x.png` into `/en/images/x.png`, which matches no route and
+ // breaks every screenshot in the manual; the extension guard covers the rest,
+ // including `favicon.ico`, `icon.png`, `sitemap.xml` and `robots.txt`, which
+ // must stay outside `[lang]` or crawlers get a 404.
+ //
+ // The `api/` and `api$` forms are deliberate rather than a bare `api`: a
+ // prefix match would also exclude `/api-keys`, a retired page this middleware
+ // has to redirect. Same reasoning for `images/` and `images$`.
+ matcher: ["/((?!api/|api$|_next/|images/|images$|.*\\.[a-zA-Z0-9]+$).*)", "/"],
+}
diff --git a/apps/docs/next-env.d.ts b/apps/docs/next-env.d.ts
new file mode 100644
index 000000000..830fb594c
--- /dev/null
+++ b/apps/docs/next-env.d.ts
@@ -0,0 +1,6 @@
+///
+///
+///
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
diff --git a/apps/docs/next.config.mjs b/apps/docs/next.config.mjs
new file mode 100644
index 000000000..02a51afa6
--- /dev/null
+++ b/apps/docs/next.config.mjs
@@ -0,0 +1,17 @@
+import { createMDX } from "fumadocs-mdx/next"
+import { fileURLToPath } from "node:url"
+
+const withMDX = createMDX()
+
+/** @type {import('next').NextConfig} */
+const config = {
+ reactStrictMode: true,
+ // Keep the listener's origin for local rewrites. Normalizing 127.0.0.1 to
+ // localhost makes Next proxy the rewrite and re-enter the locale redirect.
+ skipMiddlewareUrlNormalize: true,
+ outputFileTracingRoot: fileURLToPath(new URL("../../", import.meta.url)),
+ // Keep the Next.js development overlay indicator out of the product UI.
+ devIndicators: false,
+}
+
+export default withMDX(config)
diff --git a/apps/docs/openapi/core-api.yaml b/apps/docs/openapi/core-api.yaml
new file mode 100644
index 000000000..472e1f59e
--- /dev/null
+++ b/apps/docs/openapi/core-api.yaml
@@ -0,0 +1,7130 @@
+# Generated documentation projection; do not edit.
+info:
+ contact: {}
+ description: Deployment and operations routes under /core/v1 for Core Web's server and operator scripts. Every operation requires the Core key; Project API keys and machine credentials are not accepted.
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ title: OpenAgentCore Core API
+ version: '1'
+paths:
+ /core/v1/audit-log:
+ get:
+ description: Core key only. Newest-first cursor pagination of safe metadata. Actor labels are unverified console labels, not authorization identities. Request bodies and secrets are never recorded.
+ parameters:
+ - schema:
+ type: string
+ description: Target Project ID
+ in: query
+ name: project_id
+ - schema:
+ type: string
+ description: Resource type
+ in: query
+ name: resource_type
+ - schema:
+ type: string
+ description: Resource ID
+ in: query
+ name: resource_id
+ - schema:
+ type: string
+ description: Mutation action
+ in: query
+ name: action
+ - schema:
+ type: string
+ description: Inclusive RFC3339 timestamp
+ in: query
+ name: created_after
+ - schema:
+ type: string
+ description: Exclusive RFC3339 timestamp
+ in: query
+ name: created_before
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ description: Opaque next_cursor from the preceding page
+ in: query
+ name: after
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.AdminAuditPage'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Query committed administrator mutations
+ tags:
+ - Core Administration
+ /core/v1/harnesses:
+ get:
+ description: Core key only. Returns every harness this build supports, in name order. enabled and default are read-only views of the process configuration (OAC_DEFAULT_HARNESS and OAC_HARNESSES). model_provider is the harness's deployment default, stored in Core, or null. Keys are never returned; api_key_configured reports that one is set.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreHarnessList'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List harnesses and their deployment default model providers
+ tags:
+ - Deployment Model Providers
+ /core/v1/harnesses/{harness}/model-provider:
+ delete:
+ description: Core key only. Idempotent; each successful request is audited. Sessions that already froze the default keep it. Afterwards new openai_hosted Sessions for this harness need a Session or Agent bundle.
+ parameters:
+ - schema:
+ type: string
+ enum:
+ - claude_sdk
+ - codex
+ - mcode
+ description: Harness
+ in: path
+ name: harness
+ required: true
+ responses:
+ '204':
+ description: No Content
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Remove a harness's deployment default model provider
+ tags:
+ - Deployment Model Providers
+ get:
+ description: Core key only. Returns the safe view; the key is never returned. 404 when the harness does not exist or has no deployment default.
+ parameters:
+ - schema:
+ type: string
+ enum:
+ - claude_sdk
+ - codex
+ - mcode
+ description: Harness
+ in: path
+ name: harness
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.HarnessModelProvider'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve a harness's deployment default model provider
+ tags:
+ - Deployment Model Providers
+ put:
+ description: 'Core key only. The body is the complete x_agents_core.model_provider bundle, including the write-only api_key; there is no partial update and bundles are never merged. The provider is validated for this harness: an HTTPS base_url without credentials, query or fragment, the harness''s protocol (responses for codex, anthropic for claude_sdk and mcode) and, for mcode, positive context_window and max_output_tokens. New openai_hosted and none Sessions that resolve no Session or Agent bundle freeze this default into their encrypted snapshot; existing Sessions never change. self_hosted Sessions never use it. The key is encrypted and never returned. Each write records an administrator audit entry without the key.'
+ parameters:
+ - schema:
+ type: string
+ enum:
+ - claude_sdk
+ - codex
+ - mcode
+ description: Harness
+ in: path
+ name: harness
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.HarnessModelProvider'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Replace a harness's deployment default model provider
+ tags:
+ - Deployment Model Providers
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ModelProviderInput'
+ description: Complete model provider bundle
+ required: true
+ /core/v1/installation:
+ get:
+ description: Core key only; available before any sandbox deployment exists. Reports the public URL that applications, nodes, sandboxes and self-hosted executors use, the API base URL, Core's source commit and installation ID, the installer's settings snapshot with where to change it, and what is bound to the current public URL. Sensitive settings report only whether they are configured.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.Installation'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve installation facts and process settings
+ tags:
+ - Core Administration
+ /core/v1/metrics:
+ get:
+ description: Core key only. Complete UTC buckets; unknown measurements are null. Samples are process-local and are not backfilled after a restart.
+ parameters:
+ - schema:
+ type: string
+ enum:
+ - 1h
+ - 6h
+ - 24h
+ - 7d
+ description: Time range (default 1h)
+ in: query
+ name: range
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/coremetrics.View'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve Core operational metrics
+ tags:
+ - Core Administration
+ /core/v1/projects:
+ get:
+ parameters:
+ - schema:
+ type: string
+ description: Project ID cursor
+ in: query
+ name: after
+ - schema:
+ type: integer
+ description: Page size (1-100)
+ in: query
+ name: limit
+ - schema:
+ type: string
+ description: asc or desc by Project ID
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.ProjectPage'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List Projects and active key counts
+ tags:
+ - Administrator Projects
+ post:
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.Project'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Create an empty independent Project
+ tags:
+ - Administrator Projects
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.ProjectRequest'
+ description: Project display name
+ required: true
+ /core/v1/projects/{project_id}:
+ post:
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.Project'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Rename a Project
+ tags:
+ - Administrator Projects
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.ProjectRequest'
+ description: Project display name
+ required: true
+ /core/v1/projects/{project_id}/agents:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Last Agent ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SavedAgentList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List reusable Agents in a Project
+ tags:
+ - Agents
+ /core/v1/projects/{project_id}/agents/{agent_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Agent ID
+ in: path
+ name: agent_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.AgentDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a reusable Agent in a Project
+ tags:
+ - Agents
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Agent ID
+ in: path
+ name: agent_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SavedAgent'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve a reusable Agent in a Project
+ tags:
+ - Agents
+ /core/v1/projects/{project_id}/archive:
+ post:
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.Project'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Archive a Project and revoke all its keys while retaining assets
+ tags:
+ - Administrator Projects
+ /core/v1/projects/{project_id}/environment-templates:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Previous Template ID
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplateList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List Environment Templates in a Project
+ tags:
+ - Environment Templates
+ /core/v1/projects/{project_id}/environment-templates/{environment_template_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Template ID
+ in: path
+ name: environment_template_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplateDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete an Environment Template in a Project
+ tags:
+ - Environment Templates
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Template ID
+ in: path
+ name: environment_template_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplate'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve an Environment Template in a Project
+ tags:
+ - Environment Templates
+ /core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials:
+ get:
+ description: Core key only. Returns metadata of the credentials restricted to this Environment, oldest first; secrets are never listed. The Environment must be a self_hosted Environment of the Project whose Session exists; otherwise 404.
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Environment UUID
+ in: path
+ name: environment_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.ExecutorCredentialList'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List a self_hosted Environment's executor credentials
+ tags:
+ - Executor Credentials
+ post:
+ description: Core key only. Returns a connect-only secret once, restricted to daemon enrollment and connection for this Environment, with the Project's principal as its execution principal. Repeating an issuance key_id returns 409 executor_credential_exists; after an uncertain response, list the credentials and rotate that key_id explicitly. Rotation keeps the key's Environment, invalidates the old secret and restores a revoked key; rotating an unknown key_id returns 404. In an archived Project, issuance and rotation return 409 project_archived. The Environment must be a self_hosted Environment of the Project whose Session exists; otherwise 404. Each write records an administrator audit entry without the secret.
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Environment UUID
+ in: path
+ name: environment_id
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.IssuedExecutorCredential'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Issue or explicitly rotate a self_hosted Environment executor credential
+ tags:
+ - Executor Credentials
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.EnvironmentExecutorCredentialRequest'
+ description: Request
+ required: true
+ /core/v1/projects/{project_id}/environments/{environment_id}/executor-credentials/{key_id}:
+ delete:
+ description: Core key only. Revokes one credential restricted to this Environment; repeated revocation is safe and it also works in an archived Project. Revocation denies future enrollment and connection but does not stop executor-owned compute. The Environment must be a self_hosted Environment of the Project whose Session exists; otherwise 404.
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Environment UUID
+ in: path
+ name: environment_id
+ required: true
+ - schema:
+ type: string
+ description: Executor key UUID
+ in: path
+ name: key_id
+ required: true
+ responses:
+ '204':
+ description: No Content
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Revoke a self_hosted Environment executor credential
+ tags:
+ - Executor Credentials
+ /core/v1/projects/{project_id}/files:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Last File ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 10000
+ minimum: 1
+ description: Maximum page size, 1–10000
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Only return Files with this purpose
+ in: query
+ name: purpose
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFileList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List source files in a Project
+ tags:
+ - Files
+ /core/v1/projects/{project_id}/files/{file_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Source file ID
+ in: path
+ name: file_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFileDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a source file in a Project
+ tags:
+ - Files
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Source file ID
+ in: path
+ name: file_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFile'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve source file metadata in a Project
+ tags:
+ - Files
+ /core/v1/projects/{project_id}/keys:
+ get:
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Key ID cursor
+ in: query
+ name: after
+ - schema:
+ type: integer
+ description: Page size (1-100)
+ in: query
+ name: limit
+ - schema:
+ type: string
+ description: asc or desc by key ID
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.ProjectAPIKeyPage'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List safe key metadata for a Project
+ tags:
+ - Administrator Projects
+ post:
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.IssuedProjectAPIKey'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Issue an independent secret in an existing Project
+ tags:
+ - Administrator Projects
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.ProjectAPIKeyRequest'
+ description: Key display name
+ required: true
+ /core/v1/projects/{project_id}/keys/{key_id}:
+ delete:
+ parameters:
+ - schema:
+ type: string
+ description: Project UUID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: API key UUID
+ in: path
+ name: key_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxMutationResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Revoke one Project key while retaining shared assets
+ tags:
+ - Administrator Projects
+ /core/v1/projects/{project_id}/resource-owners:
+ get:
+ description: Core key only. The Project ID path selects its space. Returns null for resources without recorded creation provenance, including historical and foreign resources. No key secret is returned.
+ parameters:
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Resource type
+ in: query
+ name: resource_type
+ required: true
+ - schema:
+ type: string
+ description: Comma-separated public resource IDs, maximum 100
+ in: query
+ name: resource_ids
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.ResourceOwnerList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Batch lookup resource creation keys
+ tags:
+ - Write Audit
+ /core/v1/projects/{project_id}/sessions:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Root Agent ID whose Sessions to return
+ in: query
+ name: agent_id
+ - schema:
+ type: string
+ description: Last Session ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List execution Sessions in a Project
+ tags:
+ - Sessions
+ /core/v1/projects/{project_id}/sessions/{session_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete an execution Session in a Project
+ tags:
+ - Sessions
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Session'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve an execution Session in a Project
+ tags:
+ - Sessions
+ /core/v1/projects/{project_id}/sessions/{session_id}/archive:
+ get:
+ description: Core key only. Reports actual resource disposition, including expiry and failure cleanup. This is not archive provenance and does not assert active Turn settlement. Read this after an uncertain archive response; never infer released from a missing sandbox alone.
+ parameters:
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.ManagedSessionArchive'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve a managed Session's resource cleanup state
+ tags:
+ - Core Administration
+ post:
+ description: Core key only. Requires maintenance and the current deployment generation. Permanently closes execution, requests cancellation and releases sandbox/snapshots through existing cleanup. Session history and persisted files/artifacts remain; unpersisted workspace contents are lost. A cleanup_pending response is not proof of resource release. Does not affect caller-managed Runtime.
+ parameters:
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.ManagedSessionArchive'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Release a managed Session's execution resources while retaining history
+ tags:
+ - Core Administration
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.AdminSessionArchiveRequest'
+ description: Current deployment generation
+ required: true
+ /core/v1/projects/{project_id}/sessions/{session_id}/artifacts:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Producing Environment ID; an unknown or malformed ID returns an empty page
+ in: query
+ name: environment_id
+ - schema:
+ type: string
+ description: Last immutable artifact ID
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Publication order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionArtifactList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List immutable Session artifacts in a Project
+ tags:
+ - Artifacts
+ /core/v1/projects/{project_id}/sessions/{session_id}/artifacts/{artifact_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Artifact ID
+ in: path
+ name: artifact_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionArtifactDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a published artifact in a Project
+ tags:
+ - Artifacts
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Artifact ID
+ in: path
+ name: artifact_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionArtifact'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve immutable artifact metadata in a Project
+ tags:
+ - Artifacts
+ /core/v1/projects/{project_id}/sessions/{session_id}/artifacts/{artifact_id}/content:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Artifact ID
+ in: path
+ name: artifact_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ '400':
+ description: Bad Request
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Download immutable artifact bytes in a Project
+ tags:
+ - Artifacts
+ /core/v1/projects/{project_id}/sessions/{session_id}/execution-configuration:
+ get:
+ description: Core key only; the Project ID selects the target space and does not authenticate. Returns the committed model, harness and safe provider selection with recorded sources. This read never decrypts credentials, resolves current defaults or probes execution health. Deployment defaults frozen after they moved into Core show their safe view; older deployment selections remain redacted. Historical provenance and missing provider projections are explicitly unknown/unavailable.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionExecutionConfiguration'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve a Session's frozen execution configuration in a Project
+ tags:
+ - Execution configuration
+ /core/v1/projects/{project_id}/sessions/{session_id}/items:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Last Item ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ItemList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List persisted execution Items in a Project
+ tags:
+ - Items
+ /core/v1/projects/{project_id}/sessions/{session_id}/runtime-history:
+ get:
+ description: Core key only; the Project ID selects the target space and does not authenticate. Returns stored Runtime observations for one Session. End is exclusive; the server selects a bounded resolution. Responses contain at most 1,000 series, 10,000 points per coverage/series array, and 100,000 total coverage plus series points. It never reads or changes live compute.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: integer
+ maximum: 9007199254740991
+ minimum: 0
+ description: Inclusive Unix-second start
+ in: query
+ name: start
+ required: true
+ - schema:
+ type: integer
+ maximum: 9007199254740991
+ minimum: 1
+ description: Exclusive Unix-second end
+ in: query
+ name: end
+ required: true
+ - schema:
+ type: integer
+ maximum: 10000
+ minimum: 2
+ description: Maximum points per series; defaults to the lower of 120 and the advertised service maximum
+ in: query
+ name: max_points
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.RuntimeHistory'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve Session Runtime history in a Project
+ tags:
+ - Runtime history
+ /core/v1/projects/{project_id}/sessions/{session_id}/runtime-observation:
+ get:
+ description: Core key only; the Project ID selects the target space and does not authenticate. Returns one read-only current Runtime observation. It never provisions, renews, restarts, pauses or stops compute.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.RuntimeObservation'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve a Session Runtime observation in a Project
+ tags:
+ - Runtime observations
+ /core/v1/projects/{project_id}/sessions/{session_id}/turns:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Last Turn ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.TurnList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List execution Turns in a Project
+ tags:
+ - Turns
+ /core/v1/projects/{project_id}/sessions/{session_id}/turns/{turn_id}:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Turn ID
+ in: path
+ name: turn_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Turn'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve an execution Turn in a Project
+ tags:
+ - Turns
+ /core/v1/projects/{project_id}/skills:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill resource cursor
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 0
+ description: Page size; 0 returns an empty page
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillList'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List Skills in a Project
+ tags:
+ - Skills
+ /core/v1/projects/{project_id}/skills/{skill_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillDeleted'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a Skill and its versions in a Project
+ tags:
+ - Skills
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Skill'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve Skill metadata in a Project
+ tags:
+ - Skills
+ /core/v1/projects/{project_id}/skills/{skill_id}/content:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ security:
+ - DeploymentAdminAuth: []
+ summary: Download Skill content in a Project
+ tags:
+ - Skills
+ /core/v1/projects/{project_id}/skills/{skill_id}/versions:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Version resource cursor
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 0
+ description: Page size; 0 returns an empty page
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Version order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersionList'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List Skill versions in a Project
+ tags:
+ - Skills
+ /core/v1/projects/{project_id}/skills/{skill_id}/versions/{version}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Concrete version number
+ in: path
+ name: version
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersionDeleted'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a Skill version in a Project
+ tags:
+ - Skills
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Concrete version number
+ in: path
+ name: version
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersion'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve Skill version metadata in a Project
+ tags:
+ - Skills
+ /core/v1/projects/{project_id}/skills/{skill_id}/versions/{version}/content:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Concrete version number
+ in: path
+ name: version
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ security:
+ - DeploymentAdminAuth: []
+ summary: Download immutable Skill version content in a Project
+ tags:
+ - Skills
+ /core/v1/projects/{project_id}/vaults:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Last Vault ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ description: Requested page size, clamped to 1–100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ enum:
+ - active
+ - archived
+ description: Scalar status filter
+ in: query
+ name: status
+ - schema:
+ type: array
+ items:
+ enum:
+ - active
+ - archived
+ type: string
+ style: form
+ explode: true
+ description: Array status filter; combined with status as a union
+ in: query
+ name: status[]
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.VaultList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List Vaults in a Project
+ tags:
+ - Vaults
+ /core/v1/projects/{project_id}/vaults/{vault_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.VaultDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a Vault and all its Credentials in a Project
+ tags:
+ - Vaults
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Vault'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve a Vault in a Project
+ tags:
+ - Vaults
+ /core/v1/projects/{project_id}/vaults/{vault_id}/credentials:
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Last Credential ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ description: Requested page size, clamped to 1–100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ enum:
+ - active
+ - archived
+ description: Scalar status filter
+ in: query
+ name: status
+ - schema:
+ type: array
+ items:
+ enum:
+ - active
+ - archived
+ type: string
+ style: form
+ explode: true
+ description: Array status filter; combined with status as a union
+ in: query
+ name: status[]
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CredentialList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List safe Vault Credential metadata in a Project
+ tags:
+ - Credentials
+ /core/v1/projects/{project_id}/vaults/{vault_id}/credentials/{credential_id}:
+ delete:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Credential ID
+ in: path
+ name: credential_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CredentialDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Delete a Vault Credential in a Project
+ tags:
+ - Credentials
+ get:
+ description: Core key only. Reuses the public resource projection and operation rules; the Project ID selects the target space and does not authenticate.
+ parameters:
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Credential ID
+ in: path
+ name: credential_id
+ required: true
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Credential'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve safe Vault Credential metadata in a Project
+ tags:
+ - Credentials
+ /core/v1/projects/{project_id}/write-operations:
+ get:
+ description: Core key only. Reverse chronological keyset pagination over committed writes. Creation records remain; other records follow configured retention. The key path selects its independent space, never a caller-supplied tenant.
+ parameters:
+ - schema:
+ type: string
+ description: Project ID
+ in: path
+ name: project_id
+ required: true
+ - schema:
+ type: string
+ description: Recorded creator key ID
+ in: query
+ name: key_id
+ - schema:
+ type: string
+ description: Resource type
+ in: query
+ name: resource_type
+ - schema:
+ type: string
+ description: Public resource ID
+ in: query
+ name: resource_id
+ - schema:
+ type: string
+ description: Inclusive RFC3339 timestamp
+ in: query
+ name: created_after
+ - schema:
+ type: string
+ description: Exclusive RFC3339 timestamp
+ in: query
+ name: created_before
+ - schema:
+ type: integer
+ description: Page size, 1-100, default 50
+ in: query
+ name: limit
+ - schema:
+ type: string
+ description: Opaque next_cursor from the preceding page
+ in: query
+ name: after
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.WriteOperationPage'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Query API-key write operations
+ tags:
+ - Write Audit
+ /core/v1/sandbox/deployment:
+ get:
+ description: Core key only. Does not grant project resource access. Responses contain only explicit safe fields. E2B template_build values are those Core read when the selection was saved; this read does not call E2B.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeDeploymentView'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve sandbox deployment
+ tags:
+ - Sandbox Manager
+ post:
+ description: Selects a provider, enforced resource limits and pinned Runtime release. Core derives the deployment's core_url from the installation public URL and rejects a core_url member with 400. E2B returns 409 sandbox_configuration_error while the public URL is loopback. E2B credentials are write-only. E2B may omit resources to adopt the validated template build's CPU and memory, returned in specification.resources. Exact retries return the existing selection; differing selections and file-managed deployments reject. This does not create compute or execute work.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeDeploymentView'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Initialize the deployment sandbox provider
+ tags:
+ - Sandbox Manager
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxDeploymentInput'
+ description: Deployment selection
+ required: true
+ put:
+ description: Requires maintenance, the current generation and verified cleanup of all old resources. Credentials are write-only. E2B may omit resources to adopt the validated template build's CPU and memory. A core_url member is rejected with 400; the address comes from the installation public URL. Historical records are retained; old node credentials and enrollments are retired. Explicitly resume after success. Never automatically retry an uncertain write.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeDeploymentView'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Change a fully drained deployment's sandbox configuration
+ tags:
+ - Sandbox Manager
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxDeploymentChangeInput'
+ description: Replacement deployment selection
+ required: true
+ /core/v1/sandbox/deployment/maintenance:
+ patch:
+ description: Requires the current generation. Maintenance preserves execution, cleanup and history. Resume requires successfully activated provider configuration.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeDeploymentView'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Pause or resume new hosted allocations
+ tags:
+ - Sandbox Manager
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.SandboxMaintenanceRequest'
+ description: Maintenance state
+ required: true
+ /core/v1/sandbox/enrollment-tokens:
+ post:
+ description: Core key only. Does not grant project resource access. Responses contain only explicit safe fields.
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxEnrollmentToken'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Create a ten-minute one-use node enrollment token
+ tags:
+ - Sandbox Manager
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxEnrollmentTokenRequest'
+ description: Request
+ required: true
+ /core/v1/sandbox/nodes:
+ get:
+ description: Core key only. Does not grant project resource access. Responses contain only explicit safe fields.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxNodeList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List deployment sandbox nodes
+ tags:
+ - Sandbox Manager
+ /core/v1/sandbox/nodes/{node_id}:
+ delete:
+ description: Core key only. Does not grant project resource access. Responses contain only explicit safe fields.
+ parameters:
+ - schema:
+ type: string
+ description: Sandbox node UUID
+ in: path
+ name: node_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxMutationResponse'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Remove a sandbox node with no retained resources
+ tags:
+ - Sandbox Manager
+ get:
+ description: Core key only. Complete UTC buckets. Missing host measurements and offline history are null; reads never sample or backfill.
+ parameters:
+ - schema:
+ type: string
+ description: Sandbox node UUID
+ in: path
+ name: node_id
+ required: true
+ - schema:
+ type: string
+ enum:
+ - 1h
+ - 6h
+ - 24h
+ description: Time range (default 1h)
+ in: query
+ name: range
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeNodeDetail'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Retrieve sandbox node and host history
+ tags:
+ - Sandbox Manager
+ patch:
+ description: Core key only. Does not grant project resource access. Responses contain only explicit safe fields.
+ parameters:
+ - schema:
+ type: string
+ description: Sandbox node UUID
+ in: path
+ name: node_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxMutationResponse'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Update sandbox node name and capacity
+ tags:
+ - Sandbox Manager
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeNodeUpdate'
+ description: Request
+ required: true
+ /core/v1/sandbox/nodes/{node_id}/allocations:
+ get:
+ description: Core key only. Does not grant project resource access. Responses contain only explicit safe fields.
+ parameters:
+ - schema:
+ type: string
+ description: Sandbox node UUID
+ in: path
+ name: node_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.SandboxAllocationList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List retained allocations on a sandbox node
+ tags:
+ - Sandbox Manager
+ /core/v1/sandbox/runtime-observations:
+ get:
+ description: Core key only. Each observation is labelled with its owning Project ID. Uses the existing read-only Runtime sampler, with bounded concurrency and no execution or provisioning. A provider with a batch metrics read, such as E2B, samples the page's running sandboxes in one bounded request.
+ parameters:
+ - schema:
+ type: string
+ description: Last Session ID from the preceding page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Session creation order
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.AdminRuntimeObservationList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: List Runtime observations across managed Projects
+ tags:
+ - Core Administration
+ /core/v1/summary:
+ get:
+ description: Core key only. after/limit/order paginate Projects. Agent grouping returns groups within those spaces. Date bounds filter Session creation, not current asset counts. Usage sums only non-null public Session usage; coverage includes every selected Session. Each Project is read in a consistent database snapshot. Totals are not billing records.
+ parameters:
+ - schema:
+ type: string
+ description: One API Project
+ in: query
+ name: project_id
+ - schema:
+ type: string
+ enum:
+ - project
+ - agent
+ - key
+ description: Grouping
+ in: query
+ name: group_by
+ - schema:
+ type: string
+ description: Inclusive RFC3339 Session creation time
+ in: query
+ name: created_after
+ - schema:
+ type: string
+ description: Exclusive RFC3339 Session creation time
+ in: query
+ name: created_before
+ - schema:
+ type: string
+ description: Last Project ID in preceding page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Number of Projects
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Project order
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.AdminSummaryResponse'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/api.CoreErrorResponse'
+ security:
+ - DeploymentAdminAuth: []
+ summary: Summarize resource counts and Session usage by Project, Agent or creator key
+ tags:
+ - Core Administration
+schemes:
+ - http
+ - https
+openapi: 3.1.0
+servers:
+ - url: https://core-direct.example.com
+ description: 'Operator-script placeholder: replace with the Core loopback origin, not the public Web entry.'
+components:
+ schemas:
+ api.AdminRuntimeObservation:
+ properties:
+ observation:
+ $ref: '#/components/schemas/api.AdminRuntimeObservationDetail'
+ project_id:
+ type: string
+ type: object
+ api.AdminRuntimeObservationDetail:
+ properties:
+ allocation_created_at:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ cpu:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.RuntimeCPUObservation'
+ - type: 'null'
+ disk:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/api.RuntimeDiskObservation'
+ - type: 'null'
+ description: |-
+ Current Runtime disk usage and capacity. E2B reports them; Docker and
+ microsandbox observations return null.
+ environment_id:
+ anyOf:
+ - format: uuid
+ type: string
+ - type: 'null'
+ id:
+ format: uuid
+ type: string
+ instance:
+ $ref: '#/components/schemas/v1.RuntimeInstance'
+ lifecycle_state:
+ anyOf:
+ - enum:
+ - active
+ - sleeping
+ - transitioning
+ - pending
+ - stopped
+ type: string
+ - type: 'null'
+ memory:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.RuntimeMemoryObservation'
+ - type: 'null'
+ mode:
+ enum:
+ - none
+ - self_hosted
+ - openai_hosted
+ type: string
+ object:
+ enum:
+ - agent.runtime_observation
+ type: string
+ observed_at:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ provider_type:
+ anyOf:
+ - type: string
+ - type: 'null'
+ reason:
+ anyOf:
+ - enum:
+ - runtime_mode_not_observable
+ - allocation_pending
+ - runtime_not_running
+ - source_not_configured
+ - sample_timeout
+ - sample_unavailable
+ type: string
+ - type: 'null'
+ resolved_at:
+ minimum: 0
+ type: integer
+ session_id:
+ format: uuid
+ type: string
+ started_at:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ status:
+ enum:
+ - observed
+ - unsupported
+ - unavailable
+ type: string
+ required:
+ - allocation_created_at
+ - cpu
+ - disk
+ - environment_id
+ - id
+ - instance
+ - lifecycle_state
+ - memory
+ - mode
+ - object
+ - observed_at
+ - provider_type
+ - reason
+ - resolved_at
+ - session_id
+ - started_at
+ - status
+ type: object
+ api.AdminRuntimeObservationList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/api.AdminRuntimeObservation'
+ type: array
+ first_id:
+ type: string
+ has_more:
+ type: boolean
+ last_id:
+ type: string
+ object:
+ type: string
+ type: object
+ api.AdminSessionArchiveRequest:
+ properties:
+ expected_generation:
+ type: integer
+ type: object
+ api.AdminSessionCounts:
+ properties:
+ failed:
+ type: integer
+ idle:
+ type: integer
+ in_progress:
+ type: integer
+ requires_action:
+ type: integer
+ total:
+ type: integer
+ type: object
+ api.AdminSummaryResponse:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/api.AdminSummaryRow'
+ type: array
+ has_more:
+ type: boolean
+ next_cursor:
+ type: string
+ type: object
+ api.AdminSummaryRow:
+ properties:
+ agent_id:
+ type: string
+ assets:
+ $ref: '#/components/schemas/store.AdminAssetCounts'
+ coverage:
+ $ref: '#/components/schemas/api.AdminUsageCoverage'
+ key_id:
+ type: string
+ last_active_at:
+ type: integer
+ project_id:
+ type: string
+ sessions:
+ $ref: '#/components/schemas/api.AdminSessionCounts'
+ usage:
+ $ref: '#/components/schemas/v1.TokenUsage'
+ type: object
+ api.AdminUsageCoverage:
+ properties:
+ measured_sessions:
+ type: integer
+ ratio:
+ type: number
+ total_sessions:
+ type: integer
+ type: object
+ api.CoreAPIError:
+ properties:
+ code:
+ anyOf:
+ - type: string
+ - type: 'null'
+ details:
+ description: |-
+ Details contains only documented, Core-owned facts: string, finite number,
+ boolean, null or string array values. Never include request echoes, secrets
+ or native/provider error text. Empty or invalid details are omitted.
+ type: object
+ message:
+ type: string
+ param:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type:
+ type: string
+ required:
+ - message
+ - type
+ type: object
+ api.CoreErrorResponse:
+ properties:
+ error:
+ $ref: '#/components/schemas/api.CoreAPIError'
+ required:
+ - error
+ type: object
+ api.CoreHarness:
+ properties:
+ default:
+ type: boolean
+ enabled:
+ type: boolean
+ id:
+ enum:
+ - claude_sdk
+ - codex
+ - mcode
+ type: string
+ model_provider:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/api.HarnessModelProvider'
+ - type: 'null'
+ object:
+ enum:
+ - core.harness
+ type: string
+ required:
+ - default
+ - enabled
+ - id
+ - model_provider
+ - object
+ type: object
+ api.CoreHarnessList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/api.CoreHarness'
+ type: array
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - object
+ type: object
+ api.EnvironmentExecutorCredentialRequest:
+ properties:
+ key_id:
+ format: uuid
+ type: string
+ rotate:
+ type: boolean
+ type: object
+ api.ExecutorCredentialList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.ExecutorCredential'
+ type: array
+ type: object
+ api.HarnessModelProvider:
+ properties:
+ api_key_configured:
+ type: boolean
+ base_url:
+ type: string
+ context_window:
+ type: integer
+ harness:
+ enum:
+ - claude_sdk
+ - codex
+ - mcode
+ type: string
+ max_output_tokens:
+ type: integer
+ object:
+ enum:
+ - core.model_provider
+ type: string
+ protocol:
+ enum:
+ - anthropic
+ - responses
+ type: string
+ updated_at:
+ type: string
+ required:
+ - api_key_configured
+ - base_url
+ - harness
+ - object
+ - protocol
+ - updated_at
+ type: object
+ api.Installation:
+ properties:
+ address_bindings:
+ $ref: '#/components/schemas/store.AddressBindings'
+ api_base_url:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: public_url followed by /v1; null when public_url is null.
+ configuration:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/api.InstallationConfiguration'
+ - type: 'null'
+ description: The installer's settings snapshot (OAC_SETTINGS_FILE); null when the installer did not start Core.
+ installation_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: OAC_INSTALLATION_ID; null when Core runs without the sandbox manager.
+ local_only:
+ description: True when public_url names a loopback host, reachable only from the Core host.
+ type: boolean
+ object:
+ enum:
+ - core.installation
+ type: string
+ public_url:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: 'OAC_PUBLIC_URL: the origin applications, nodes, sandboxes and self-hosted executors use. Null when unset.'
+ source_commit:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Full source commit Core was built from; null for development builds.
+ type: object
+ api.InstallationConfiguration:
+ properties:
+ applied_at:
+ type: string
+ apply_command:
+ description: Command that applies config.json changes.
+ type: string
+ path:
+ description: Absolute host path of the installation's config.json.
+ type: string
+ settings:
+ items:
+ $ref: '#/components/schemas/api.InstallationSetting'
+ type: array
+ type: object
+ api.InstallationSetting:
+ properties:
+ changeable:
+ description: False for settings fixed at installation.
+ type: boolean
+ configured:
+ description: 'Present only for a sensitive setting: whether it has a value.'
+ type: boolean
+ default:
+ anyOf:
+ - {}
+ - type: 'null'
+ key:
+ description: Dotted config.json key, such as ports.core.
+ type: string
+ restarts:
+ description: Services that restart when the setting changes.
+ items:
+ type: string
+ type: array
+ sensitive:
+ type: boolean
+ value:
+ anyOf:
+ - {}
+ - type: 'null'
+ description: Applied value; always null for a sensitive setting.
+ type: object
+ api.ProjectAPIKeyRequest:
+ properties:
+ name:
+ type: string
+ type: object
+ api.ProjectRequest:
+ properties:
+ name:
+ type: string
+ type: object
+ api.ResourceOwnerList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.ResourceOwner'
+ type: array
+ type: object
+ api.RuntimeDiskObservation:
+ properties:
+ limit_bytes:
+ anyOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
+ usage_bytes:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ required:
+ - limit_bytes
+ - usage_bytes
+ type: object
+ api.SandboxAllocationList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.RuntimeNodeAllocation'
+ type: array
+ type: object
+ api.SandboxDeploymentChangeInput:
+ properties:
+ e2b:
+ $ref: '#/components/schemas/api.SandboxE2BInput'
+ expected_generation:
+ type: integer
+ provider:
+ type: string
+ resources:
+ allOf:
+ - $ref: '#/components/schemas/sandbox.Resources'
+ description: |-
+ Per-sandbox limits, required for Docker and microsandbox. E2B may omit
+ them; Core then uses the validated template build's cpus and memory_mib.
+ runtime:
+ $ref: '#/components/schemas/sandbox.RuntimeRelease'
+ type: object
+ api.SandboxDeploymentInput:
+ properties:
+ e2b:
+ $ref: '#/components/schemas/api.SandboxE2BInput'
+ provider:
+ type: string
+ resources:
+ allOf:
+ - $ref: '#/components/schemas/sandbox.Resources'
+ description: |-
+ Per-sandbox limits, required for Docker and microsandbox. E2B may omit
+ them; Core then uses the validated template build's cpus and memory_mib.
+ runtime:
+ $ref: '#/components/schemas/sandbox.RuntimeRelease'
+ type: object
+ api.SandboxE2BInput:
+ properties:
+ api_key:
+ type: string
+ template:
+ type: string
+ type: object
+ api.SandboxEnrollmentToken:
+ properties:
+ enrollment_id:
+ description: Public, non-secret handle of this command; never a credential. The node it registers reports the same value as enrollment_id.
+ type: string
+ expires_at:
+ type: string
+ token:
+ type: string
+ type: object
+ api.SandboxEnrollmentTokenRequest:
+ properties:
+ max_active:
+ type: integer
+ max_retained:
+ description: Docker never suspends, so Core replaces this with max_active; microsandbox uses both limits.
+ type: integer
+ type: object
+ api.SandboxMutationResponse:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ updated:
+ type: boolean
+ type: object
+ api.SandboxNodeList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.RuntimeNode'
+ type: array
+ type: object
+ coremetrics.Database:
+ properties:
+ ping_ms:
+ $ref: '#/components/schemas/coremetrics.Latency'
+ pool:
+ $ref: '#/components/schemas/coremetrics.Pool'
+ series:
+ items:
+ $ref: '#/components/schemas/coremetrics.DatabaseBucket'
+ type: array
+ size_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ type: object
+ coremetrics.DatabaseBucket:
+ properties:
+ ping_p95_ms:
+ anyOf:
+ - type: number
+ - type: 'null'
+ pool_in_use:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ start:
+ type: string
+ type: object
+ coremetrics.Execution:
+ properties:
+ connected_daemons:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ in_progress_turns:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ interrupted:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ oldest_queued_seconds:
+ anyOf:
+ - type: number
+ - type: 'null'
+ queue_wait_ms:
+ $ref: '#/components/schemas/coremetrics.Latency'
+ queued_turns:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ series:
+ items:
+ $ref: '#/components/schemas/coremetrics.ExecutionBucket'
+ type: array
+ slots_in_use:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ slots_total:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ unavailable:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ waiting_for_daemon:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ type: object
+ coremetrics.ExecutionBucket:
+ properties:
+ in_progress:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ queue_wait_p95_ms:
+ anyOf:
+ - type: number
+ - type: 'null'
+ queued:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ start:
+ type: string
+ type: object
+ coremetrics.Job:
+ properties:
+ failed:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ id:
+ type: string
+ last_run_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ processed:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ status:
+ type: string
+ type: object
+ coremetrics.Latency:
+ properties:
+ p50:
+ anyOf:
+ - type: number
+ - type: 'null'
+ p95:
+ anyOf:
+ - type: number
+ - type: 'null'
+ type: object
+ coremetrics.Pool:
+ properties:
+ idle:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ in_use:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ max:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ type: object
+ coremetrics.Process:
+ properties:
+ cpu_cores:
+ anyOf:
+ - type: number
+ - type: 'null'
+ cpu_limit_cores:
+ anyOf:
+ - type: number
+ - type: 'null'
+ goroutines:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ memory_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ memory_limit_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ rss_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ series:
+ items:
+ $ref: '#/components/schemas/coremetrics.ProcessBucket'
+ type: array
+ type: object
+ coremetrics.ProcessBucket:
+ properties:
+ cpu_cores:
+ anyOf:
+ - type: number
+ - type: 'null'
+ rss_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ start:
+ type: string
+ type: object
+ coremetrics.Range:
+ properties:
+ end:
+ type: string
+ resolution_seconds:
+ type: integer
+ start:
+ type: string
+ type: object
+ coremetrics.ServiceState:
+ properties:
+ execution_owner:
+ anyOf:
+ - type: boolean
+ - type: 'null'
+ revision:
+ anyOf:
+ - type: string
+ - type: 'null'
+ started_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ status:
+ type: string
+ type: object
+ coremetrics.View:
+ properties:
+ database:
+ $ref: '#/components/schemas/coremetrics.Database'
+ execution:
+ $ref: '#/components/schemas/coremetrics.Execution'
+ jobs:
+ items:
+ $ref: '#/components/schemas/coremetrics.Job'
+ type: array
+ object:
+ type: string
+ process:
+ $ref: '#/components/schemas/coremetrics.Process'
+ range:
+ $ref: '#/components/schemas/coremetrics.Range'
+ service:
+ $ref: '#/components/schemas/coremetrics.ServiceState'
+ type: object
+ sandbox.DeploymentSpec:
+ properties:
+ resources:
+ $ref: '#/components/schemas/sandbox.Resources'
+ runtime:
+ $ref: '#/components/schemas/sandbox.RuntimeRelease'
+ type: object
+ sandbox.Resources:
+ properties:
+ cpus:
+ type: integer
+ environment_disk_mib:
+ type: integer
+ memory_mib:
+ type: integer
+ root_disk_mib:
+ type: integer
+ type: object
+ sandbox.RuntimeRelease:
+ properties:
+ firmware_sha256:
+ type: string
+ image_id:
+ type: string
+ image_manifest_digest:
+ type: string
+ microsandbox_ref:
+ type: string
+ runtime_sha256:
+ type: string
+ source_commit:
+ type: string
+ type: object
+ store.AddressBindings:
+ properties:
+ hosted_sandboxes:
+ type: integer
+ nodes:
+ type: integer
+ nodes_on_other_address:
+ type: integer
+ self_hosted_executors:
+ type: integer
+ type: object
+ store.AdminAssetCounts:
+ properties:
+ agents:
+ type: integer
+ credentials:
+ type: integer
+ environment_templates:
+ type: integer
+ files:
+ type: integer
+ skills:
+ type: integer
+ vaults:
+ type: integer
+ type: object
+ store.AdminAuditOperation:
+ properties:
+ action:
+ type: string
+ actor_label:
+ type: string
+ admin_credential_id:
+ type: string
+ created_at:
+ type: string
+ id:
+ type: string
+ project_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ request_id:
+ type: string
+ resource_id:
+ type: string
+ resource_type:
+ type: string
+ result_ids:
+ items:
+ type: object
+ type: array
+ trace_id:
+ type: string
+ type: object
+ store.AdminAuditPage:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.AdminAuditOperation'
+ type: array
+ has_more:
+ type: boolean
+ next_cursor:
+ type: string
+ type: object
+ store.AuditAPIKey:
+ properties:
+ id:
+ type: string
+ kind:
+ type: string
+ name:
+ type: string
+ prefix:
+ type: string
+ revoked_at:
+ type: string
+ type: object
+ store.ExecutorCredential:
+ properties:
+ created_at:
+ type: string
+ key_id:
+ format: uuid
+ type: string
+ revoked_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type: object
+ store.IssuedExecutorCredential:
+ properties:
+ environment_id:
+ type: string
+ executor_token:
+ type: string
+ key_id:
+ type: string
+ type: object
+ store.IssuedProjectAPIKey:
+ properties:
+ created_at:
+ type: string
+ id:
+ type: string
+ key:
+ type: string
+ name:
+ type: string
+ prefix:
+ type: string
+ project_id:
+ type: string
+ revoked_at:
+ type: string
+ type: object
+ store.ManagedSessionArchive:
+ properties:
+ environment_id:
+ type: string
+ session_id:
+ type: string
+ state:
+ type: string
+ type: object
+ store.NodeHostHistory:
+ properties:
+ points:
+ items:
+ $ref: '#/components/schemas/store.NodeHostHistoryPoint'
+ type: array
+ resolution_seconds:
+ type: integer
+ type: object
+ store.NodeHostHistoryPoint:
+ properties:
+ available_disk_bytes_min:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ cpu_utilization_max:
+ anyOf:
+ - type: number
+ - type: 'null'
+ memory_used_bytes_max:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ start:
+ type: string
+ type: object
+ store.Project:
+ properties:
+ active_key_count:
+ type: integer
+ archived_at:
+ type: string
+ created_at:
+ type: string
+ id:
+ type: string
+ name:
+ type: string
+ type: object
+ store.ProjectAPIKey:
+ properties:
+ created_at:
+ type: string
+ id:
+ type: string
+ name:
+ type: string
+ prefix:
+ type: string
+ project_id:
+ type: string
+ revoked_at:
+ type: string
+ type: object
+ store.ProjectAPIKeyPage:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.ProjectAPIKey'
+ type: array
+ has_more:
+ type: boolean
+ type: object
+ store.ProjectPage:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.Project'
+ type: array
+ has_more:
+ type: boolean
+ type: object
+ store.ResourceOwner:
+ properties:
+ admin_audit_id:
+ type: string
+ api_key:
+ $ref: '#/components/schemas/store.AuditAPIKey'
+ resource_id:
+ type: string
+ source:
+ type: string
+ type: object
+ store.RuntimeDeploymentView:
+ properties:
+ core_url:
+ description: 'Read-only: the installation public URL (OAC_PUBLIC_URL), which nodes and sandboxes use to reach Core. The deployment API does not accept it.'
+ type: string
+ e2b:
+ $ref: '#/components/schemas/store.SandboxE2BView'
+ generation:
+ type: integer
+ installation_id:
+ type: string
+ maintenance:
+ type: boolean
+ mode:
+ type: string
+ owner_epoch:
+ type: integer
+ provider:
+ type: string
+ resources:
+ $ref: '#/components/schemas/store.SandboxDeploymentResources'
+ specification:
+ $ref: '#/components/schemas/sandbox.DeploymentSpec'
+ specification_digest:
+ type: string
+ suspension:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/store.SandboxSuspensionView'
+ - type: 'null'
+ description: Idle suspension policy; microsandbox only, otherwise null.
+ type: object
+ store.RuntimeNode:
+ properties:
+ active:
+ type: integer
+ available_disk_bytes:
+ type: integer
+ available_memory_bytes:
+ type: integer
+ cleanup_pending:
+ type: integer
+ core_url:
+ description: |-
+ The Core address this node enrolled with. A node whose address differs
+ from the installation public URL receives no new sandboxes; re-add it.
+ type: string
+ cpu_count:
+ type: integer
+ created_at:
+ type: string
+ diagnostic:
+ description: Fixed reason for the last reported unreadiness; absent while the provider is ready. Clients treat an unknown value as provider_unavailable.
+ enum:
+ - provider_unavailable
+ - docker_unavailable
+ - docker_limits_unsupported
+ - runtime_image_unavailable
+ - kvm_unavailable
+ - microsandbox_artifacts_unavailable
+ - capacity_insufficient
+ type: string
+ enrollment_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The enrollment_id of the command that registered this node (POST /core/v1/sandbox/enrollment-tokens); null for nodes enrolled before Core recorded it.
+ id:
+ type: string
+ last_seen_at:
+ type: string
+ max_active:
+ type: integer
+ max_retained:
+ type: integer
+ name:
+ type: string
+ online:
+ type: boolean
+ provider:
+ type: string
+ provider_ready:
+ type: boolean
+ reserved:
+ type: integer
+ retained:
+ type: integer
+ running:
+ type: integer
+ snapshots:
+ type: integer
+ type: object
+ store.RuntimeNodeAllocation:
+ properties:
+ compute_phase:
+ type: string
+ compute_phase_changed_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The time the allocation entered its current compute_phase, or null when unknown; an allocation that existed before Core recorded it reports null until its next phase change. For a suspended microsandbox allocation, this time plus the deployment's snapshot retention tells roughly when Core reclaims it.
+ created_at:
+ type: string
+ diagnostic:
+ type: string
+ environment_id:
+ type: string
+ id:
+ type: string
+ initialization:
+ type: string
+ node_id:
+ type: string
+ session_id:
+ type: string
+ state:
+ type: string
+ tenant_id:
+ type: string
+ type: object
+ store.RuntimeNodeDetail:
+ properties:
+ active:
+ type: integer
+ available_disk_bytes:
+ type: integer
+ available_memory_bytes:
+ type: integer
+ cleanup_pending:
+ type: integer
+ core_url:
+ description: |-
+ The Core address this node enrolled with. A node whose address differs
+ from the installation public URL receives no new sandboxes; re-add it.
+ type: string
+ cpu_count:
+ type: integer
+ created_at:
+ type: string
+ diagnostic:
+ description: Fixed reason for the last reported unreadiness; absent while the provider is ready. Clients treat an unknown value as provider_unavailable.
+ enum:
+ - provider_unavailable
+ - docker_unavailable
+ - docker_limits_unsupported
+ - runtime_image_unavailable
+ - kvm_unavailable
+ - microsandbox_artifacts_unavailable
+ - capacity_insufficient
+ type: string
+ enrollment_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: The enrollment_id of the command that registered this node (POST /core/v1/sandbox/enrollment-tokens); null for nodes enrolled before Core recorded it.
+ history:
+ $ref: '#/components/schemas/store.NodeHostHistory'
+ host:
+ $ref: '#/components/schemas/store.RuntimeNodeHost'
+ id:
+ type: string
+ last_seen_at:
+ type: string
+ max_active:
+ type: integer
+ max_retained:
+ type: integer
+ name:
+ type: string
+ online:
+ type: boolean
+ provider:
+ type: string
+ provider_ready:
+ type: boolean
+ reserved:
+ type: integer
+ retained:
+ type: integer
+ running:
+ type: integer
+ snapshots:
+ type: integer
+ type: object
+ store.RuntimeNodeHost:
+ properties:
+ available_disk_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ available_memory_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ cpu_utilization:
+ anyOf:
+ - type: number
+ - type: 'null'
+ effective_cpu_cores:
+ anyOf:
+ - type: number
+ - type: 'null'
+ observed_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ total_memory_bytes:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ type: object
+ store.RuntimeNodeUpdate:
+ properties:
+ max_active:
+ type: integer
+ max_retained:
+ description: Docker never suspends, so Core replaces this with max_active; microsandbox uses both limits.
+ type: integer
+ name:
+ type: string
+ type: object
+ store.SandboxDeploymentResources:
+ properties:
+ allocations:
+ type: integer
+ pending:
+ type: integer
+ type: object
+ store.SandboxE2BTemplateBuildView:
+ properties:
+ resources:
+ $ref: '#/components/schemas/store.SandboxTemplateResources'
+ status:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: Build status at selection time; validation admits only ready builds.
+ type: object
+ store.SandboxE2BView:
+ properties:
+ credential_configured:
+ type: boolean
+ template:
+ type: string
+ template_build:
+ allOf:
+ - $ref: '#/components/schemas/store.SandboxE2BTemplateBuildView'
+ description: The fixed template build as Core read it when this selection was saved.
+ type: object
+ store.SandboxMaintenanceRequest:
+ properties:
+ expected_generation:
+ type: integer
+ maintenance:
+ type: boolean
+ type: object
+ store.SandboxSuspensionView:
+ properties:
+ idle_seconds:
+ type: integer
+ retention_seconds:
+ type: integer
+ type: object
+ store.SandboxTemplateResources:
+ properties:
+ cpus:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ memory_mib:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ root_disk_mib:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ type: object
+ store.WriteOperation:
+ properties:
+ action:
+ type: string
+ api_key:
+ $ref: '#/components/schemas/store.AuditAPIKey'
+ created_at:
+ type: string
+ id:
+ type: string
+ parent_id:
+ type: string
+ request_id:
+ type: string
+ resource_id:
+ type: string
+ resource_type:
+ type: string
+ trace_id:
+ type: string
+ type: object
+ store.WriteOperationPage:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/store.WriteOperation'
+ type: array
+ has_more:
+ type: boolean
+ next_cursor:
+ type: string
+ type: object
+ v1.Agent:
+ properties:
+ id:
+ type: string
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ model:
+ type: string
+ multi_agent:
+ $ref: '#/components/schemas/v1.MultiAgentConfig'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ reasoning:
+ $ref: '#/components/schemas/v1.Reasoning'
+ service_tier:
+ enum:
+ - auto
+ type: string
+ text:
+ $ref: '#/components/schemas/v1.TextConfig'
+ tools:
+ items:
+ type: object
+ type: array
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.AgentsCore'
+ - type: 'null'
+ required:
+ - id
+ - model
+ - multi_agent
+ - reasoning
+ - service_tier
+ - text
+ - tools
+ type: object
+ v1.AgentDeleted:
+ properties:
+ deleted:
+ enum:
+ - true
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.AgentsCore:
+ properties:
+ harness:
+ enum:
+ - codex
+ - claude_sdk
+ - mcode
+ type: string
+ required:
+ - harness
+ type: object
+ v1.Credential:
+ properties:
+ auth:
+ $ref: '#/components/schemas/v1.CredentialAuth'
+ created_at:
+ type: integer
+ id:
+ type: string
+ name:
+ type: string
+ object:
+ enum:
+ - vault.credential
+ type: string
+ updated_at:
+ type: integer
+ vault_id:
+ type: string
+ required:
+ - auth
+ - created_at
+ - id
+ - name
+ - object
+ - updated_at
+ - vault_id
+ type: object
+ v1.CredentialAuth:
+ properties:
+ expires_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ mcp_server_url:
+ type: string
+ refresh:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.OAuthCredentialRefresh'
+ - type: 'null'
+ type:
+ enum:
+ - static_bearer
+ - mcp_oauth
+ type: string
+ required:
+ - mcp_server_url
+ - type
+ type: object
+ v1.CredentialDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - vault.credential.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.CredentialList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Credential'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.EnvironmentNetwork:
+ properties:
+ access:
+ enum:
+ - enabled
+ - disabled
+ - restricted
+ type: string
+ allowed_domains:
+ items:
+ type: string
+ type: array
+ required:
+ - access
+ - allowed_domains
+ type: object
+ v1.EnvironmentPackages:
+ properties:
+ npm:
+ items:
+ type: string
+ type: array
+ python:
+ items:
+ type: string
+ type: array
+ system:
+ items:
+ type: string
+ type: array
+ required:
+ - npm
+ - python
+ - system
+ type: object
+ v1.EnvironmentTemplate:
+ properties:
+ capability_directories:
+ items:
+ type: string
+ type: array
+ created_at:
+ type: integer
+ files:
+ items:
+ type: object
+ type: array
+ id:
+ type: string
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ network:
+ $ref: '#/components/schemas/v1.EnvironmentNetwork'
+ object:
+ enum:
+ - agent.environment.template
+ type: string
+ packages:
+ $ref: '#/components/schemas/v1.EnvironmentPackages'
+ plugins:
+ items:
+ type: object
+ type: array
+ skills:
+ items:
+ type: object
+ type: array
+ updated_at:
+ type: integer
+ required:
+ - capability_directories
+ - created_at
+ - files
+ - id
+ - network
+ - object
+ - packages
+ - plugins
+ - skills
+ - updated_at
+ type: object
+ v1.EnvironmentTemplateDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.environment.template.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.EnvironmentTemplateList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.EnvironmentTemplate'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.ExecutionProviderSelection:
+ properties:
+ configuration:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.ModelProviderView'
+ - type: 'null'
+ source:
+ enum:
+ - session
+ - agent
+ - deployment
+ - unknown
+ type: string
+ status:
+ enum:
+ - available
+ - redacted
+ - unavailable
+ type: string
+ required:
+ - configuration
+ - source
+ - status
+ type: object
+ v1.ExecutionSelection:
+ properties:
+ source:
+ enum:
+ - session
+ - agent
+ - deployment
+ - unknown
+ type: string
+ value:
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - source
+ - value
+ type: object
+ v1.InputTokenDetails:
+ properties:
+ cached_tokens:
+ type: integer
+ required:
+ - cached_tokens
+ type: object
+ v1.Item:
+ properties:
+ action:
+ $ref: '#/components/schemas/v1.WebSearchAction'
+ agent_id:
+ type: string
+ arguments: {}
+ call_id:
+ type: string
+ command:
+ type: string
+ content:
+ items:
+ $ref: '#/components/schemas/v1.ItemContent'
+ type: array
+ cwd:
+ type: string
+ duration_ms:
+ type: integer
+ error: {}
+ exit_code:
+ type: integer
+ id:
+ type: string
+ model:
+ type: string
+ name:
+ type: string
+ output: {}
+ phase:
+ anyOf:
+ - enum:
+ - commentary
+ - final_answer
+ type: string
+ - type: 'null'
+ reasoning_effort:
+ type: string
+ recipient_agent_id:
+ type: string
+ recipient_agent_ids:
+ items:
+ type: string
+ type: array
+ role:
+ enum:
+ - user
+ - assistant
+ type: string
+ sender_agent_id:
+ type: string
+ server_label:
+ type: string
+ status:
+ enum:
+ - in_progress
+ - completed
+ - failed
+ - incomplete
+ type: string
+ summary:
+ items:
+ $ref: '#/components/schemas/v1.SummaryText'
+ type: array
+ turn_id:
+ type: string
+ type:
+ enum:
+ - message
+ - command_execution
+ - mcp_call
+ - function_call
+ - function_call_output
+ - web_search_call
+ - reasoning
+ - agent_message
+ - create_subagent_call
+ - send_subagent_input_call
+ - resume_subagent_call
+ - wait_for_subagents_call
+ - interrupt_subagent_call
+ - close_subagent_call
+ type: string
+ required:
+ - id
+ - turn_id
+ - type
+ type: object
+ v1.ItemContent:
+ properties:
+ encrypted_content:
+ type: string
+ image_url:
+ type: string
+ text:
+ type: string
+ type:
+ enum:
+ - input_text
+ - output_text
+ - input_image
+ - encrypted_content
+ type: string
+ required:
+ - type
+ type: object
+ v1.ItemList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Item'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.ModelProviderInput:
+ properties:
+ api_key:
+ type: string
+ base_url:
+ type: string
+ context_window:
+ type: integer
+ max_output_tokens:
+ type: integer
+ protocol:
+ enum:
+ - anthropic
+ - responses
+ type: string
+ required:
+ - api_key
+ - base_url
+ - protocol
+ type: object
+ v1.ModelProviderView:
+ properties:
+ api_key_configured:
+ type: boolean
+ base_url:
+ type: string
+ context_window:
+ type: integer
+ max_output_tokens:
+ type: integer
+ protocol:
+ enum:
+ - anthropic
+ - responses
+ type: string
+ required:
+ - api_key_configured
+ - base_url
+ - protocol
+ type: object
+ v1.MultiAgentConfig:
+ properties:
+ enabled:
+ type: boolean
+ max_concurrent_subagents:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ required:
+ - enabled
+ type: object
+ v1.OAuthCredentialRefresh:
+ properties:
+ client_id:
+ type: string
+ resource:
+ anyOf:
+ - type: string
+ - type: 'null'
+ scope:
+ anyOf:
+ - type: string
+ - type: 'null'
+ token_endpoint:
+ type: string
+ token_endpoint_auth:
+ $ref: '#/components/schemas/v1.OAuthEndpointAuth'
+ required:
+ - client_id
+ - token_endpoint
+ - token_endpoint_auth
+ type: object
+ v1.OAuthEndpointAuth:
+ properties:
+ type:
+ enum:
+ - none
+ - client_secret_basic
+ - client_secret_post
+ type: string
+ required:
+ - type
+ type: object
+ v1.OutputTokenDetails:
+ properties:
+ reasoning_tokens:
+ type: integer
+ required:
+ - reasoning_tokens
+ type: object
+ v1.Reasoning:
+ properties:
+ effort:
+ anyOf:
+ - type: string
+ - type: 'null'
+ summary:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type: object
+ v1.RequiredAction:
+ properties:
+ arguments: {}
+ call_id:
+ type: string
+ environment_id:
+ type: string
+ name:
+ type: string
+ turn_id:
+ type: string
+ type:
+ enum:
+ - function_call
+ - environment_connection
+ type: string
+ required:
+ - type
+ type: object
+ v1.RuntimeCPUObservation:
+ properties:
+ capacity_cores:
+ anyOf:
+ - minimum: 5.e-324
+ type: number
+ - type: 'null'
+ usage_cores:
+ anyOf:
+ - minimum: 0
+ type: number
+ - type: 'null'
+ usage_seconds_total:
+ anyOf:
+ - minimum: 0
+ type: number
+ - type: 'null'
+ utilization_ratio:
+ anyOf:
+ - minimum: 0
+ type: number
+ - type: 'null'
+ required:
+ - capacity_cores
+ - usage_cores
+ - usage_seconds_total
+ - utilization_ratio
+ type: object
+ v1.RuntimeHistory:
+ properties:
+ coverage:
+ $ref: '#/components/schemas/v1.RuntimeHistoryCoverage'
+ generated_at:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ object:
+ enum:
+ - agent.runtime_history
+ type: string
+ requested_range:
+ $ref: '#/components/schemas/v1.RuntimeHistoryRange'
+ resolution_seconds:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ series:
+ items:
+ $ref: '#/components/schemas/v1.RuntimeHistorySeries'
+ maxItems: 1000
+ type: array
+ session_id:
+ format: uuid
+ type: string
+ source:
+ enum:
+ - durable
+ type: string
+ token_usage:
+ items:
+ $ref: '#/components/schemas/v1.RuntimeHistoryTokenUsagePoint'
+ maxItems: 10000
+ type: array
+ required:
+ - coverage
+ - generated_at
+ - object
+ - requested_range
+ - resolution_seconds
+ - series
+ - session_id
+ - source
+ - token_usage
+ type: object
+ v1.RuntimeHistoryCPU:
+ properties:
+ capacity_cores:
+ anyOf:
+ - minimum: 5.e-324
+ type: number
+ - type: 'null'
+ contributor_count:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ utilization_ratio:
+ anyOf:
+ - minimum: 0
+ type: number
+ - type: 'null'
+ required:
+ - capacity_cores
+ - contributor_count
+ - utilization_ratio
+ type: object
+ v1.RuntimeHistoryCoverage:
+ properties:
+ buckets:
+ items:
+ $ref: '#/components/schemas/v1.RuntimeHistoryCoveragePoint'
+ maxItems: 10000
+ type: array
+ expected_sample_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ first_sample_at:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ last_sample_at:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ retained_start:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ sample_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ required:
+ - buckets
+ - expected_sample_count
+ - first_sample_at
+ - last_sample_at
+ - retained_start
+ - sample_count
+ type: object
+ v1.RuntimeHistoryCoveragePoint:
+ properties:
+ end:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ first_observed_at:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ last_observed_at:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ observation_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ observed_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ start:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ unavailable_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ required:
+ - end
+ - first_observed_at
+ - last_observed_at
+ - observation_count
+ - observed_count
+ - start
+ - unavailable_count
+ type: object
+ v1.RuntimeHistoryMemory:
+ properties:
+ contributor_count:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ limit_bytes:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ - type: 'null'
+ usage_bytes:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ required:
+ - contributor_count
+ - limit_bytes
+ - usage_bytes
+ type: object
+ v1.RuntimeHistoryPoint:
+ properties:
+ cpu:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.RuntimeHistoryCPU'
+ - type: 'null'
+ end:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ first_observed_at:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ last_observed_at:
+ anyOf:
+ - maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ - type: 'null'
+ memory:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.RuntimeHistoryMemory'
+ - type: 'null'
+ observation_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ observed_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ start:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ unavailable_count:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ required:
+ - cpu
+ - end
+ - first_observed_at
+ - last_observed_at
+ - memory
+ - observation_count
+ - observed_count
+ - start
+ - unavailable_count
+ type: object
+ v1.RuntimeHistoryRange:
+ properties:
+ end:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ start:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ required:
+ - end
+ - start
+ type: object
+ v1.RuntimeHistorySeries:
+ properties:
+ allocation_id:
+ format: uuid
+ type: string
+ environment_id:
+ format: uuid
+ type: string
+ points:
+ items:
+ $ref: '#/components/schemas/v1.RuntimeHistoryPoint'
+ maxItems: 10000
+ type: array
+ provider_type:
+ pattern: ^[a-z][a-z0-9_]{0,31}$
+ type: string
+ started_at:
+ $ref: '#/components/schemas/v1.RuntimeHistoryTime'
+ required:
+ - allocation_id
+ - environment_id
+ - points
+ - provider_type
+ - started_at
+ type: object
+ v1.RuntimeHistoryTime:
+ properties:
+ nanoseconds:
+ maximum: 999999999
+ minimum: 0
+ type: integer
+ seconds:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ required:
+ - nanoseconds
+ - seconds
+ type: object
+ v1.RuntimeHistoryTokenUsagePoint:
+ properties:
+ end:
+ maximum: 9007199254740991
+ minimum: 1
+ type: integer
+ input_tokens:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ output_tokens:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ sampled_at:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ start:
+ maximum: 9007199254740991
+ minimum: 0
+ type: integer
+ required:
+ - end
+ - input_tokens
+ - output_tokens
+ - sampled_at
+ - start
+ type: object
+ v1.RuntimeInstance:
+ properties:
+ allocation_id:
+ anyOf:
+ - format: uuid
+ type: string
+ - type: 'null'
+ connection_generation:
+ anyOf:
+ - format: uuid
+ type: string
+ - type: 'null'
+ device_id:
+ anyOf:
+ - format: uuid
+ type: string
+ - type: 'null'
+ kind:
+ enum:
+ - managed_allocation
+ - self_hosted_connection
+ - none
+ type: string
+ required:
+ - allocation_id
+ - connection_generation
+ - device_id
+ - kind
+ type: object
+ v1.RuntimeMemoryObservation:
+ properties:
+ limit_bytes:
+ anyOf:
+ - minimum: 1
+ type: integer
+ - type: 'null'
+ usage_bytes:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ required:
+ - limit_bytes
+ - usage_bytes
+ type: object
+ v1.RuntimeObservation:
+ properties:
+ allocation_created_at:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ cpu:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.RuntimeCPUObservation'
+ - type: 'null'
+ environment_id:
+ anyOf:
+ - format: uuid
+ type: string
+ - type: 'null'
+ id:
+ format: uuid
+ type: string
+ instance:
+ $ref: '#/components/schemas/v1.RuntimeInstance'
+ lifecycle_state:
+ anyOf:
+ - enum:
+ - active
+ - sleeping
+ - transitioning
+ - pending
+ - stopped
+ type: string
+ - type: 'null'
+ memory:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.RuntimeMemoryObservation'
+ - type: 'null'
+ mode:
+ enum:
+ - none
+ - self_hosted
+ - openai_hosted
+ type: string
+ object:
+ enum:
+ - agent.runtime_observation
+ type: string
+ observed_at:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ provider_type:
+ anyOf:
+ - type: string
+ - type: 'null'
+ reason:
+ anyOf:
+ - enum:
+ - runtime_mode_not_observable
+ - allocation_pending
+ - runtime_not_running
+ - source_not_configured
+ - sample_timeout
+ - sample_unavailable
+ type: string
+ - type: 'null'
+ resolved_at:
+ minimum: 0
+ type: integer
+ session_id:
+ format: uuid
+ type: string
+ started_at:
+ anyOf:
+ - minimum: 0
+ type: integer
+ - type: 'null'
+ status:
+ enum:
+ - observed
+ - unsupported
+ - unavailable
+ type: string
+ required:
+ - allocation_created_at
+ - cpu
+ - environment_id
+ - id
+ - instance
+ - lifecycle_state
+ - memory
+ - mode
+ - object
+ - observed_at
+ - provider_type
+ - reason
+ - resolved_at
+ - session_id
+ - started_at
+ - status
+ type: object
+ v1.SavedAgent:
+ properties:
+ created_at:
+ type: integer
+ id:
+ type: string
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ metadata:
+ additionalProperties:
+ type: string
+ type: object
+ model:
+ type: string
+ multi_agent:
+ $ref: '#/components/schemas/v1.MultiAgentConfig'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - agent
+ type: string
+ reasoning:
+ $ref: '#/components/schemas/v1.Reasoning'
+ service_tier:
+ enum:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ type: string
+ text:
+ $ref: '#/components/schemas/v1.SavedAgentText'
+ tools:
+ items:
+ type: object
+ type: array
+ updated_at:
+ type: integer
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentCore'
+ - type: 'null'
+ required:
+ - created_at
+ - id
+ - metadata
+ - model
+ - multi_agent
+ - object
+ - reasoning
+ - service_tier
+ - text
+ - tools
+ - updated_at
+ type: object
+ v1.SavedAgentCore:
+ properties:
+ harness:
+ enum:
+ - codex
+ - claude_sdk
+ - mcode
+ type: string
+ model_provider:
+ $ref: '#/components/schemas/v1.ModelProviderView'
+ type: object
+ v1.SavedAgentList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SavedAgent'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SavedAgentText:
+ properties:
+ format:
+ $ref: '#/components/schemas/v1.SavedAgentTextFormat'
+ verbosity:
+ enum:
+ - low
+ - medium
+ - high
+ type: string
+ required:
+ - format
+ - verbosity
+ type: object
+ v1.SavedAgentTextFormat:
+ properties:
+ schema:
+ type: object
+ type:
+ enum:
+ - text
+ - json_schema
+ type: string
+ required:
+ - type
+ type: object
+ v1.Session:
+ properties:
+ agent:
+ $ref: '#/components/schemas/v1.Agent'
+ created_at:
+ type: integer
+ environment:
+ $ref: '#/components/schemas/v1.SessionEnvironment'
+ error:
+ anyOf:
+ - type: string
+ - type: 'null'
+ id:
+ type: string
+ last_active_at:
+ type: integer
+ metadata:
+ additionalProperties:
+ type: string
+ type: object
+ object:
+ enum:
+ - agent.session
+ type: string
+ required_actions:
+ items:
+ $ref: '#/components/schemas/v1.RequiredAction'
+ type: array
+ status:
+ enum:
+ - idle
+ - in_progress
+ - requires_action
+ - failed
+ type: string
+ usage:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TokenUsage'
+ - type: 'null'
+ vault_ids:
+ items:
+ type: string
+ type: array
+ required:
+ - agent
+ - created_at
+ - environment
+ - id
+ - last_active_at
+ - metadata
+ - object
+ - required_actions
+ - status
+ - vault_ids
+ type: object
+ v1.SessionArtifact:
+ properties:
+ created_at:
+ type: integer
+ environment_id:
+ type: string
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.artifact
+ type: string
+ path:
+ type: string
+ session_id:
+ type: string
+ size_bytes:
+ type: integer
+ turn_id:
+ type: string
+ required:
+ - created_at
+ - environment_id
+ - id
+ - object
+ - path
+ - session_id
+ - size_bytes
+ - turn_id
+ type: object
+ v1.SessionArtifactDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.artifact.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SessionArtifactList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SessionArtifact'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SessionDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SessionEnvironment:
+ properties:
+ capability_directories:
+ items:
+ type: string
+ type: array
+ files:
+ items:
+ type: object
+ type: array
+ id:
+ type: string
+ network:
+ $ref: '#/components/schemas/v1.EnvironmentNetwork'
+ packages:
+ $ref: '#/components/schemas/v1.EnvironmentPackages'
+ plugins:
+ items:
+ type: object
+ type: array
+ remote_url:
+ type: string
+ skills:
+ items:
+ type: object
+ type: array
+ type:
+ enum:
+ - none
+ - self_hosted
+ - openai_hosted
+ type: string
+ workspace_directory:
+ type: string
+ required:
+ - type
+ type: object
+ v1.SessionExecutionConfiguration:
+ properties:
+ harness:
+ $ref: '#/components/schemas/v1.ExecutionSelection'
+ model:
+ $ref: '#/components/schemas/v1.ExecutionSelection'
+ model_provider:
+ $ref: '#/components/schemas/v1.ExecutionProviderSelection'
+ object:
+ enum:
+ - agent.session.execution_configuration
+ type: string
+ schema_version:
+ enum:
+ - 1
+ type: integer
+ session_id:
+ type: string
+ required:
+ - harness
+ - model
+ - model_provider
+ - object
+ - schema_version
+ - session_id
+ type: object
+ v1.SessionList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Session'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.Skill:
+ properties:
+ created_at:
+ type: integer
+ default_version:
+ type: string
+ description:
+ type: string
+ id:
+ type: string
+ latest_version:
+ type: string
+ name:
+ type: string
+ object:
+ enum:
+ - skill
+ type: string
+ required:
+ - created_at
+ - default_version
+ - description
+ - id
+ - latest_version
+ - name
+ - object
+ type: object
+ v1.SkillDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - skill.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SkillList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Skill'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SkillVersion:
+ properties:
+ created_at:
+ type: integer
+ description:
+ type: string
+ id:
+ type: string
+ name:
+ type: string
+ object:
+ enum:
+ - skill.version
+ type: string
+ skill_id:
+ type: string
+ version:
+ type: string
+ required:
+ - created_at
+ - description
+ - id
+ - name
+ - object
+ - skill_id
+ - version
+ type: object
+ v1.SkillVersionDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - skill.version.deleted
+ type: string
+ version:
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ - version
+ type: object
+ v1.SkillVersionList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SkillVersion'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SourceFile:
+ properties:
+ bytes:
+ minimum: 0
+ type: integer
+ created_at:
+ type: integer
+ expires_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ filename:
+ type: string
+ id:
+ type: string
+ object:
+ enum:
+ - file
+ type: string
+ purpose:
+ enum:
+ - user_data
+ type: string
+ status:
+ enum:
+ - processed
+ type: string
+ status_details:
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - bytes
+ - created_at
+ - filename
+ - id
+ - object
+ - purpose
+ - status
+ type: object
+ v1.SourceFileDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - file
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SourceFileList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SourceFile'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SummaryText:
+ properties:
+ text:
+ type: string
+ type:
+ enum:
+ - summary_text
+ type: string
+ required:
+ - text
+ - type
+ type: object
+ v1.TextConfig:
+ properties:
+ format:
+ $ref: '#/components/schemas/v1.TextFormat'
+ verbosity:
+ enum:
+ - low
+ - medium
+ - high
+ type: string
+ required:
+ - format
+ - verbosity
+ type: object
+ v1.TextFormat:
+ properties:
+ schema:
+ type: object
+ type:
+ enum:
+ - text
+ - json_schema
+ type: string
+ required:
+ - type
+ type: object
+ v1.TokenUsage:
+ properties:
+ input_tokens:
+ type: integer
+ input_tokens_details:
+ $ref: '#/components/schemas/v1.InputTokenDetails'
+ output_tokens:
+ type: integer
+ output_tokens_details:
+ $ref: '#/components/schemas/v1.OutputTokenDetails'
+ total_tokens:
+ type: integer
+ required:
+ - input_tokens
+ - input_tokens_details
+ - output_tokens
+ - output_tokens_details
+ - total_tokens
+ type: object
+ v1.Turn:
+ properties:
+ agent_id:
+ type: string
+ completed_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ created_at:
+ type: integer
+ error:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TurnError'
+ - type: 'null'
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.turn
+ type: string
+ session_id:
+ type: string
+ started_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ status:
+ enum:
+ - queued
+ - in_progress
+ - waiting
+ - completed
+ - failed
+ - cancelled
+ type: string
+ subagent_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ usage:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TokenUsage'
+ - type: 'null'
+ required:
+ - agent_id
+ - created_at
+ - id
+ - object
+ - session_id
+ - status
+ type: object
+ v1.TurnError:
+ properties:
+ code:
+ enum:
+ - context_length_exceeded
+ - session_budget_exceeded
+ - usage_limit_exceeded
+ - rate_limit_exceeded
+ - server_overloaded
+ - cyber_policy
+ - connection_failed
+ - server_error
+ - authentication_error
+ - invalid_request
+ - resource_not_found
+ - sandbox_error
+ - executor_version_incompatible
+ - active_turn_not_steerable
+ - request_timeout
+ - internal_error
+ type: string
+ message:
+ type: string
+ required:
+ - code
+ - message
+ type: object
+ v1.TurnList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Turn'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.Vault:
+ properties:
+ created_at:
+ type: integer
+ id:
+ type: string
+ metadata:
+ additionalProperties:
+ type: string
+ type: object
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - vault
+ type: string
+ required:
+ - created_at
+ - id
+ - metadata
+ - object
+ type: object
+ v1.VaultDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - vault.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.VaultList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Vault'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.WebSearchAction:
+ properties:
+ pattern:
+ type: string
+ queries:
+ items:
+ type: string
+ type: array
+ query:
+ type: string
+ type:
+ enum:
+ - search
+ - open_page
+ - find_in_page
+ - other
+ type: string
+ url:
+ type: string
+ required:
+ - type
+ type: object
+ securitySchemes:
+ DeploymentAdminAuth:
+ type: http
+ scheme: bearer
+tags:
+ - name: Core Administration
+ description: 'Core Administration. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Deployment Model Providers
+ description: 'Deployment Model Providers. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Administrator Projects
+ description: 'Administrator Projects. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Agents
+ description: 'Agents. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Environment Templates
+ description: 'Environment Templates. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Executor Credentials
+ description: 'Executor Credentials. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Files
+ description: 'Files. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Write Audit
+ description: 'Write Audit. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Sessions
+ description: 'Sessions. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Artifacts
+ description: 'Artifacts. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Execution configuration
+ description: 'Execution configuration. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Items
+ description: 'Items. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Runtime history
+ description: 'Runtime history. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Runtime observations
+ description: 'Runtime observations. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Turns
+ description: 'Turns. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Skills
+ description: 'Skills. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Vaults
+ description: 'Vaults. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Credentials
+ description: 'Credentials. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
+ - name: Sandbox Manager
+ description: 'Sandbox Manager. Core administration API: Core key held by Web’s server or an operator script. Generated local management contract. This is not part of the public OpenAI API.'
diff --git a/apps/docs/openapi/public-api.yaml b/apps/docs/openapi/public-api.yaml
new file mode 100644
index 000000000..29898c58f
--- /dev/null
+++ b/apps/docs/openapi/public-api.yaml
@@ -0,0 +1,5693 @@
+# Generated documentation projection; do not edit.
+info:
+ contact: {}
+ description: Supported single-Agent execution resources from the pinned openai-python beta/agents contract. Bearer keys bind an execution principal to one project; optional OpenAI-Organization and OpenAI-Project headers must match that binding.
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ title: OpenAgentCore Agents API
+ version: '1'
+paths:
+ /agents:
+ get:
+ description: Lists only the authenticated tenant's saved Agents, independently of Sessions. Limit 0 is treated as 1 and larger limits as 100, as observed on the hosted service. The local default is 20; exact upstream default/cap and empty cursor fields remain unverified. An unknown, malformed or foreign after cursor returns not found.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Last Agent ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SavedAgentList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List reusable Agents
+ tags:
+ - Agents
+ post:
+ description: 'Persists configuration independently of execution. Names over 128 characters and metadata outside 16 string pairs with 64-character keys and 512-character values return invalid_request_error with the official param; U+0000 in stored strings is rejected as a local storage limit. As on every Agents API JSON route, a non-JSON Content-Type, invalid UTF-8, malformed JSON, a repeated key at any depth or a non-object root returns invalid_request_error with a null param and the official message before other checks; an empty or null body is {}. Missing, unknown, wrongly typed or unsupported enum members of the pinned configuration shapes (tools, text, reasoning, service_tier, multi_agent) return invalid_request_error with the JSON path as param; duplicate function names, repeated web_search or tool_search and non-object schema root types return it with a null param. Supports model/name/instructions/metadata, explicit reasoning and service tiers, multi_agent, text/json_schema, function/tool_search/programmatic_tool_calling/web_search and HTTP MCP with nullable credential_id, service origin (omitted or null on HTTP transport is saved as service) and boolean required defaulting to false. Saving credential_id grants no access: Session admission checks attached Vault ownership and destination. MCP allowed_tools preserves null versus empty; saved HTTP transport includes empty headers. Model-derived reasoning defaults, other MCP variants and public retry conformance remain incomplete. web_search saves every pinned mode: omitted or null mode is saved as live and omitted or null context_size as medium; allowed_domains preserves null versus empty and a present location, including {}, includes all four keys with null for omitted ones, as observed officially (req_db41d2f6261b4abfb69465eafe719ab5, req_165d53b88445490b9146d8272c54134d). Session execution accepts only explicit disabled web_search and disabled programmatic_tool_calling through qualified Runtime controls; saved enabled forms reject at Session admission. Session execution admits only its supported configuration subset.'
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SavedAgent'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Create a reusable Agent
+ tags:
+ - Agents
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CreateAgentRequest'
+ description: Reusable Agent configuration
+ required: true
+ /agents/{agent_id}:
+ delete:
+ description: Deletes only the authenticated tenant's saved configuration. Existing Session snapshots, history and recorded creation retry identities remain independent. Missing and repeated deletion locally return404; exact hosted error and in-flight creation/deletion semantics remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Agent ID
+ in: path
+ name: agent_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.AgentDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete a reusable Agent
+ tags:
+ - Agents
+ get:
+ description: Reads the saved resource owned by the authenticated tenant, independently of execution Sessions.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Agent ID
+ in: path
+ name: agent_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SavedAgent'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve a reusable Agent
+ tags:
+ - Agents
+ post:
+ description: Preserves omitted fields and replaces supplied fields using shared saved-configuration validation. Null name/instructions clear; null or empty metadata clears all pairs. Name, metadata and configuration validation errors return invalid_request_error with the official param, using the Agent create rules before the Agent lookup. Existing Session snapshots are unchanged. Empty updates advance updated_at without changing saved fields. Nested replacement/null defaults, model-derived reasoning and exact hosted error behavior remain incompletely verified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Agent ID
+ in: path
+ name: agent_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SavedAgent'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Update a reusable Agent
+ tags:
+ - Agents
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.UpdateAgentRequest'
+ description: Supplied reusable Agent fields
+ required: true
+ /agents/environments/{environment_id}:
+ get:
+ description: Returns durable connection status and safe installed metadata for supported self_hosted and basic openai_hosted profiles. Initial files expose frozen safe metadata without content; Plugin/Skill entries expose only safe configured installation metadata. Capability-directory discoveries are not added to those arrays. Unsupported installation configurations remain implementation gaps. This read does not prepare execution, start compute or require an enabled execution worker. Session deletion removes the associated Environment from public reads; project-shared read authorization is unchanged. Connection status does not prove native readiness or process quiescence.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Environment ID
+ in: path
+ name: environment_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentInfo'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve an execution Environment
+ tags:
+ - Environments
+ /agents/environments/{environment_id}/files:
+ get:
+ description: Lists direct regular files in one authorized self_hosted or qualified local workspace directory. Local paths use the public /workspace root and must be in cleaned form. This partial implementation defaults to the workspace root and limit 20; recursive scope and these defaults are not verified upstream semantics. A missing path, a regular file or a symbolic link returns an empty page; links are never followed. Daemons without a local workspace binding use the Claude SDK adapter reader, which keeps 404 for a missing path and 503 for a regular file or symbolic link. Well-formed unknown query keys are ignored; malformed query encoding and a repeated supported key are rejected. Sorts by case-sensitive path components, descending by default. Keep the same path, order and limit when using page. Each page rereads the complete bounded directory; changed file paths/sizes invalidate continuation locally with 400. There is no snapshot guarantee. An openai_hosted Environment that has not connected yet returns 400. Truncated or uncertain native results fail with 503 without returning a partial page. This read never starts a Turn or admits model input. Actual transport disconnect/reconnect events remain observable.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Environment ID
+ in: path
+ name: environment_id
+ required: true
+ - schema:
+ type: string
+ description: Absolute directory in cleaned form inside /workspace
+ in: query
+ name: path
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Maximum file count; local default 20
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Case-sensitive path-component order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Opaque continuation token; keep path, order and limit unchanged
+ in: query
+ name: page
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentFileList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List live Environment files
+ tags:
+ - Environments
+ post:
+ description: Uploads standard Base64 bytes to a file beneath /workspace in a qualified local Environment and returns 201. Accepts inline bytes or a project-owned source file_id through the same write path. Unknown body fields are rejected with their name as param. Basic public hosted creation requires explicit managed Runtime configuration; an openai_hosted Environment that has not connected yet returns 400. Inline data is limited to 5 MiB decoded and a file_id copy to 50 MiB. Missing parent directories are created with mode 0700 and the file with mode 0600. An existing destination is never replaced; a directory, an existing file or a path through a symlink or non-directory returns 400. Idle writes exclude execution. Missing receipts return unavailable and retain a durable mutation gate without automatic replay. Error/timing parity with upstream remains unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Environment ID
+ in: path
+ name: environment_id
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentFile'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Create an Environment file from inline bytes or a source file
+ tags:
+ - Environments
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentFileCreateRequest'
+ description: Inline bytes or source file ID and absolute workspace path
+ required: true
+ /agents/environments/templates:
+ get:
+ description: Lists tenant-owned safe template metadata in creation order with ID tie-breaking. Defaults to limit 20 and descending order; limit 0 is treated as 1 and larger limits as 100. Foreign, missing and malformed cursors return the same not found error. Concurrent-page and exact hosted error behavior remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Previous Template ID
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplateList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List Environment Templates
+ tags:
+ - Environment Templates
+ post:
+ description: Saves tenant-owned hosted configuration. Supports nullable name, enabled/disabled or exact-domain restricted network, initial inline/file_id files, confidential env, ordered setup_commands, system/npm/Python packages inline/referenced Skill ZIPs, Plugin ZIPs and workspace-contained capability directories. Omitted/null network defaults to enabled. Restricted network requires 1–100 exact ASCII hostnames; other host forms and populated unsupported installations are rejected before persistence without echoing input. Network policy rejections return invalid_request_error with a null param. No compute is allocated. Exact hosted error/retry semantics remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplate'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Create an Environment Template
+ tags:
+ - Environment Templates
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplateRequest'
+ description: Reusable configuration
+ required: true
+ /agents/environments/templates/{environment_template_id}:
+ delete:
+ description: Deletes the tenant-owned reusable configuration without changing or deleting existing Sessions and their frozen configuration.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Template ID
+ in: path
+ name: environment_template_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplateDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete an Environment Template
+ tags:
+ - Environment Templates
+ get:
+ description: Returns safe tenant-owned configuration metadata without allocating compute. Missing and foreign resources return the same not-found response.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Template ID
+ in: path
+ name: environment_template_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplate'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve an Environment Template
+ tags:
+ - Environment Templates
+ post:
+ description: Supplied fields replace atomically; omitted fields remain unchanged. Null name clears and null network resets to the pinned enabled default. Existing Session snapshots and creation retries remain unchanged. Initial files replace as a list; null/empty clears. File data is encrypted separately and excluded from response metadata. Skills replace as a list; null/empty clears. Skill archives are encrypted separately and omitted from responses. Plugins and capability directories replace as lists; null/empty clears. Plugin archives are encrypted and omitted from responses. Capability directories are snapshotted after setup. Environment MCP execution requires a qualified native transport and runtime network policy. Empty updates advance updated_at without changing saved fields or confidential contents. Network policy rejections return invalid_request_error with a null param.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Template ID
+ in: path
+ name: environment_template_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplate'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Update an Environment Template
+ tags:
+ - Environment Templates
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.EnvironmentTemplateRequest'
+ description: Configuration replacements
+ required: true
+ /agents/sessions:
+ get:
+ description: Cursor and results are scoped to the authenticated execution tenant; an unknown, malformed or foreign after cursor returns not found. Optional agent_id matches the immutable root Agent ID, including inline Agents and historical Sessions whose saved source was updated or deleted. Omission lists all Agents. Returns the same Environment and pending-input activity projection as Session retrieval, including self_hosted Sessions.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Root Agent ID whose Sessions to return
+ in: query
+ name: agent_id
+ - schema:
+ type: string
+ description: Last Session ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List execution Sessions
+ tags:
+ - Sessions
+ post:
+ description: The optional Core model_provider bundle resolves from the Session override, saved Agent defaults, then, for openai_hosted and none, the deployment default of the resolved harness; self_hosted never uses the deployment default and none accepts only it. openai_hosted and self_hosted Sessions that resolve no bundle return 400 model_provider_required with param x_agents_core.model_provider before any write. Core encrypts and freezes the resolved bundle; later Agent or deployment default edits and same-key retries cannot change it. Keys are never returned. Supports inline configuration or a tenant-owned saved agent_id with per-Session field replacements. Execution supports model/instructions, text verbosity, non-deferred function tools, adapter-qualified multi_agent with persisted Subagent reads, implicit reasoning, service tier auto and environment type none, subject to the configured engine. Codex additionally supports HTTP MCP with service origin (omitted or null on HTTP transport is saved as service), native allowed_tools and boolean required defaulting to false. Session vault_ids attach only project-owned Vaults; credential_id selects an attached static bearer or OAuth credential for the exact HTTPS URL, while null/omission selects a unique match or remains anonymous. Session reads, lists and event snapshots show that implicitly selected credential ID in a null or omitted credential_id, also after the credential is deleted; anonymous selections stay null and the stored caller intent is unchanged. After the input requirement and before any write, a credential_id without vault_ids, one outside the attached Vaults (one message for missing, foreign and unattached IDs) or one for another server_url returns 400 invalid_request_error, and several implicit matches return 409 conflict_error. Missing decryption configuration fails dispatch without anonymous fallback. Required initialization uses native startup before the first native Turn, including cold resume, and requires a separately advertised capability; exact hosted creation timing and error parity remain unverified. Other MCP origins and native OAuth login remain unsupported. The self_hosted profile requires Codex, an absolute workspace_directory and empty capability_directories, with optional non-deferred function tools and HTTP MCP using service origin, optionally authenticated by the attached Vault rules. Remote MCP and remote Bearer authentication each require separately advertised combination support; old peers cannot receive unsupported work. Omitted/null capability_directories use the empty-list default; self_hosted requires configured execution plus executor registry. Claude SDK currently requires medium verbosity and object-root function schemas. It supports anonymous or attached static-bearer service-origin HTTP MCP on none with boolean required and separately advertised MCP/bearer/required runtime support. Required servers must be connected before the first native input is released; pending or failed startup rejects execution. The shared Vault selection and immutable binding rules apply; unsupported native labels/tool names reject before persistence. An attached Vault with no matching credential may remain anonymous; missing keys or failed credential lookup/decryption never fall back to anonymous execution. Omitted stream defaults to false; stream and agent_id cannot be null. Metadata may be null; non-string values and limit violations return invalid_request_error with a metadata or metadata. param. The inline agent uses the Agent create configuration validation with agent.-prefixed params, reported before the input requirement and saved-Agent lookup; saved configurations with conflicting tools or schema roots reject admission with the same errors, and execution limits keep unsupported_or_invalid_configuration. Hosted network policy rejections return invalid_request_error with a null param. Initial input accepts a string or ordered user-message array. Codex and Claude SDK on none and qualified openai_hosted also accept inline PNG/JPEG image content; other image combinations and remote URLs are unsupported. None initial input atomically starts a Turn; self_hosted initial input is reserved while returning its Environment connection target, with execution deferred to native readiness and Session failure on initial timeout. Initial input is required for none and for streamed creation outside self_hosted. Omitted/null input remains valid for non-streaming hosted and self_hosted creation. With stream=true, returns live Session events starting with the committed creation snapshot and closes right after the first agent.session.idle recorded when a Turn ends or an input reservation stops being pending, or any agent.session.failed, without sending later events. A creation that admitted nothing closes after the snapshot; a settlement that records no event closes after events up to the cursor read with a settled Session projection. Required actions keep it open; disconnect does not cancel execution. The GET events stream remains live-only. New Sessions retain their authenticated creator; all creation retries require the same typed subject, including across key rotation. Saved-Agent retries and inline requests using Vault attachments or credential references retain caller intent independently of later resource changes; new hosted inline requests also freeze caller intent before deployment defaults resolve; unrelated non-hosted inline retries preserve resolved/default equivalences, and their resolved hash leaves out any deployment default. Provider keys enter retry hashes only as fingerprints keyed by the credential key. Unknown historical creators reject retries; known creators without recorded intent retain resolved-snapshot retry rules. These conflict policies are local and not verified hosted parity. A same-key stream=true retry of an existing creation returns 201 with no events and closes at once; retry with stream=false or use the GET events stream to recover. Claude SDK on none and Core-managed Docker openai_hosted supports qualified object-root json_schema output with medium verbosity, single-Agent execution and ordinary functions. Hosted execution reuses native workspace tools and Files/Artifacts; Skills, Plugins, capability directories, HTTP MCP, Subagent and tool_search combinations remain unqualified, including inherited template contents. Other non-text initial input remains unsupported. Basic Codex and Claude SDK openai_hosted creation requires an explicitly configured managed provider. The Claude workspace profile supports non-deferred function tools with text or successful inline PNG/JPEG results alongside native workspace tools; HTTP MCP remains unsupported. Idle Sessions provision automatically; initial provisioning has no caller connection action. Network defaults to enabled; disabled and restricted exact ASCII hostnames are supported. Restricted policy requires 1–100 allowed domains. Unsupported hostname forms and startup installations are rejected. Confidential env, system/npm/Python packages and ordered setup commands use the shared initialization lifecycle; requested network applies after setup. Initial inline and tenant-owned file_id files freeze encrypted bytes before provisioning, then install through the common Core lifecycle before native execution or live Files access. With a template reference, omitted/null files, env, packages and setup_commands inherit. Non-null files and command lists replace; env overlays by key; each package manager inherits on omission/null and otherwise replaces its list. Empty lists clear their selected field. Tenant-owned environment_template_id references inherit omitted/null network and allow only narrowing overrides. Inline hosted network:null retains the enabled default; updating a Template with network:null resets its saved policy to enabled. Core freezes effective configuration; template updates/deletion do not alter Session snapshots or same-intent creation retries. Inline or tenant-owned skill_reference Skills share initialization. Templates preserve default/latest/explicit selectors; Session creation freezes concrete metadata and encrypted content atomically. Skill, Plugin and capability-directory list omission/null inherit; a non-null list replaces, including empty-list clearing. Omitted/null Skill version selectors resolve the default version. Source deletion/default updates cannot change committed Session Skill contents. Deferred function discovery uses type-only tool_search and per-function defer_loading in the qualified single-agent Claude environment:none function profile, including qualified inline image messages and text results. Explicit web_search mode disabled and programmatic_tool_calling enabled false use frozen common Runtime controls. Enabled forms, including those saved on an Agent, remain unqualified and reject before any write unless the Session replaces tools. Omitted programmatic configuration preserves native behavior, a documented difference from the official default-on behavior. Other combinations remain unqualified; see the operation coverage.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Creation retry key, up to 128 bytes
+ in: header
+ name: Idempotency-Key
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Session'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.Session'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Create an execution Session
+ tags:
+ - Sessions
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CreateSessionRequest'
+ description: Session configuration
+ required: true
+ /agents/sessions/{session_id}:
+ delete:
+ description: Removes a durably idle or failed Session and its history from the public API. A Session whose root Turn is queued, in progress or waiting (including required actions) or whose input reservation is pending returns 409 conflict_error and is left unchanged; cancel it and wait until it is idle before deleting. Subagent child Turns and pending Environment file writes are not checked and do not block deletion. Repeating the deletion of the caller's own deleted Session returns the same confirmation; missing and foreign Sessions return 404. Internal records and native history are retained pending separate physical cleanup; overlapping stream timing remains unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete an execution Session
+ tags:
+ - Sessions
+ get:
+ description: Returns supported none, self_hosted and basic openai_hosted Session environments. Self-hosted pending input can require a caller connection before a Turn exists. Hosted initial provisioning remains idle until a Turn starts; connection observations are not native execution readiness.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Session'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve an execution Session
+ tags:
+ - Sessions
+ post:
+ description: The metadata field is required in an update body. Send null or {} to clear it, or supply an object to replace all pairs. Up to 16 string pairs, with keys at most 64 characters and values at most 512 characters; violations and non-string values return invalid_request_error with a metadata or metadata. param. U+0000 is rejected as a local storage limit. Malformed, missing and foreign Session IDs share the not-found response. Execution configuration and activity are unchanged. Returns the same safe Environment and pending-input activity projection as Session retrieval.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Session'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Update execution Session metadata
+ tags:
+ - Sessions
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.UpdateSessionRequest'
+ description: Session metadata
+ required: true
+ /agents/sessions/{session_id}/artifacts:
+ get:
+ description: Lists published outputs independently of Environment availability. Sorting uses publication time and ID. A later Turn publishes a path again only when it is new, its bytes changed, or no Artifact remains for it. A malformed environment_id matches nothing. An after value that is not an Artifact of this Session, including a malformed one, returns 400 invalid_request_error with the message "after is not a valid artifact ID". The local default page size is 20; exact upstream defaults remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Producing Environment ID; an unknown or malformed ID returns an empty page
+ in: query
+ name: environment_id
+ - schema:
+ type: string
+ description: Last immutable artifact ID
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Publication order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionArtifactList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List immutable Session artifacts
+ tags:
+ - Artifacts
+ /agents/sessions/{session_id}/artifacts/{artifact_id}:
+ delete:
+ description: Deletes the published copy without modifying its original workspace file. Already admitted content reads may finish; later reads reject.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Artifact ID
+ in: path
+ name: artifact_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionArtifactDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete a published artifact
+ tags:
+ - Artifacts
+ get:
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Artifact ID
+ in: path
+ name: artifact_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SessionArtifact'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve immutable artifact metadata
+ tags:
+ - Artifacts
+ /agents/sessions/{session_id}/artifacts/{artifact_id}/content:
+ get:
+ description: Streams stored bytes after tenant and Session authorization, including after Environment expiration. Exact upstream headers and Range behavior remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Artifact ID
+ in: path
+ name: artifact_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ '400':
+ description: Bad Request
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/octet-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Download immutable artifact bytes
+ tags:
+ - Artifacts
+ /agents/sessions/{session_id}/events:
+ get:
+ description: Live-only events, including command output fragments from capable Codex peers as agent.output.command_execution_output.delta with stable Item/output indexes. Native text conversion and output quotas apply; completion snapshots remain authoritative. Reconnect through Session, Turn and Items reads; missed events are not replayed. A lagging stream closes with an error when its bounded buffer is exceeded. When a hosted Environment fails to provision, the stream sends agent.session.environment.failed, an error event (environment_error/sandbox_error with the safe step and exit-status reason, never command output) and agent.session.failed, then ends. Session activity includes immutable pending-input connection actions before Turn creation; self_hosted environments use the same safe output as Session retrieval.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.SessionEvent'
+ '400':
+ description: Bad Request
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ text/event-stream:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Stream live Session events
+ tags:
+ - Events
+ post:
+ description: An empty events array is a resource-authorized no-op; it creates no execution retry identity, Turn, Item or input receipt. For environment none, atomically accepts text messages, cancellation and function results. Messages steer active work or start a queued Turn. The supported self_hosted profile accepts text-only messages; qualified Codex and Claude SDK openai_hosted profiles also accept inline PNG/JPEG. Under the Session lock, matching retries retain their original target; new active messages append to the current Turn, while idle messages reserve work and wait up to the original five-minute connection/admission deadline. Return 202 only after durable admission, without claiming native application; active messages create no Turn or reservation. Cancellation-only prepared-environment batches use existing durable cancellation admission and return 202 without waiting for native exit; a new cancellation conflicts while a pre-Turn reservation is pending. Homogeneous tool_result-only prepared-environment batches reuse existing scoped result admission and application receipts without creating a Turn or bypassing a pending reservation. Mixed prepared-environment batches remain unsupported. HTTP expiry/cancellation use local 409 environment_input_expired/environment_input_cancelled errors. New input on a Session whose hosted Environment failed to provision returns the observed 409 conflict_error "the hosted environment failed to provision"; input already waiting when it fails and expired Environments keep the local 409 environment_unavailable. Input the Session cannot accept in its current state, such as a result after cancellation or a batch while earlier input is pending, and a result that differs from the call's saved result return 409 with type and code conflict_error; reusing an Idempotency-Key with a different batch returns the local 409 idempotency_conflict. Inside an owned Session, a result for an unknown call or for a call of another Turn returns 400 invalid_request_error and changes nothing; missing and foreign Sessions return 404. Losing execution ownership returns 503. The response write deadline accommodates the admission window for either prepared Environment, independently of new-hosted-admission and executor URL settings. Disconnecting the waiting HTTP request does not cancel retained work or restart its deadline. Retry keys identify the whole ordered batch. Function output accepts text or ordered text/image parts subject to engine support; Claude SDK accepts text results and, on none and qualified openai_hosted, successful inline PNG/JPEG results, preserving ordered content; error images and remote references reject before admission. Native image resizing may change bytes. Runtime image-result support is checked only for image-bearing delivery. Codex and Claude SDK on none and qualified openai_hosted accept ordered inline PNG/JPEG image messages. Self-hosted profiles and other engines remain text-only; remote image URLs are unsupported. Image references are retained unchanged without service-side downloads.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Retry key, up to 128 bytes
+ in: header
+ name: Idempotency-Key
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ responses:
+ '202':
+ description: Accepted
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Submit Session input events
+ tags:
+ - Sessions
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CreateEventsRequest'
+ description: Ordered input events
+ required: true
+ /agents/sessions/{session_id}/items:
+ get:
+ description: Returns supported message and tool Items in first-observation order. Native engine fields are projected explicitly; unfinished Items on terminal Turns are incomplete. Cursors are Items of the same tenant and Session. Any other after value, including a malformed one, returns 400 invalid_request_error with the message "Invalid session item ID in `after`".
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Last Item ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ItemList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List persisted execution Items
+ tags:
+ - Items
+ /agents/sessions/{session_id}/subagents:
+ get:
+ description: Includes nested and closed Subagents. Cursors are Subagents of the same tenant and Session. Any other after value, including a malformed one, returns 400 invalid_request_error with the message "Invalid resource ID in `after`". A limit outside 1–100 is rejected.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Last Subagent ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Resource order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SubagentList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List Session Subagents
+ tags:
+ - Subagents
+ /agents/sessions/{session_id}/subagents/{subagent_id}:
+ get:
+ description: Returns this Session's persisted Subagent. Active includes idle between Turns. Resuming preserves opened_at and clears closed_at. Unknown or inaccessible parent scopes return not found.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Subagent ID
+ in: path
+ name: subagent_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Subagent'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve a Session Subagent
+ tags:
+ - Subagents
+ /agents/sessions/{session_id}/subagents/{subagent_id}/items:
+ get:
+ description: Returns only this Subagent's own Items across all its Turns, not its descendants' Items. Cursors are Items of the same tenant, Session and Subagent. Any other after value, including a malformed one, returns 400 invalid_request_error with the message "Invalid session item ID in `after`".
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Subagent ID
+ in: path
+ name: subagent_id
+ required: true
+ - schema:
+ type: string
+ description: Last Item ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Resource order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ItemList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List a Subagent's Items
+ tags:
+ - Subagents
+ /agents/sessions/{session_id}/subagents/{subagent_id}/turns:
+ get:
+ description: Includes this Subagent's Turns after resume, with the Session's Agent ID as agent_id. Cursors are Turns of the same tenant, Session and Subagent. Any other after value, including a malformed one, returns 400 invalid_request_error with the message "Invalid resource ID in `after`". Missing recorded usage remains null. A limit outside 1–100 is rejected.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Subagent ID
+ in: path
+ name: subagent_id
+ required: true
+ - schema:
+ type: string
+ description: Last Turn ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.TurnList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List a Subagent's Turns
+ tags:
+ - Subagents
+ /agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}:
+ get:
+ description: Returns a Turn owned by this Subagent. Its agent_id is the Session's Agent ID and its subagent_id identifies the Subagent. Session Turn routes do not return child Turns. Unknown or inaccessible parent scopes return not found.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Subagent ID
+ in: path
+ name: subagent_id
+ required: true
+ - schema:
+ type: string
+ description: Turn ID
+ in: path
+ name: turn_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Turn'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve a Subagent Turn
+ tags:
+ - Subagents
+ /agents/sessions/{session_id}/subagents/{subagent_id}/turns/{turn_id}/items:
+ get:
+ description: Returns Items owned by this exact Subagent Turn. Cursors are Items of the same tenant, Session, Subagent and Turn. Any other after value, including a malformed one, returns 400 invalid_request_error with the message "Invalid session item ID in `after`".
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Subagent ID
+ in: path
+ name: subagent_id
+ required: true
+ - schema:
+ type: string
+ description: Turn ID
+ in: path
+ name: turn_id
+ required: true
+ - schema:
+ type: string
+ description: Last Item ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ minimum: 0
+ description: Page size; 0 is treated as 1 and values above 100 as 100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Resource order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ItemList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List a Subagent Turn's Items
+ tags:
+ - Subagents
+ /agents/sessions/{session_id}/turns:
+ get:
+ description: Returns the Session's root Turns in creation order; Subagent Turns are listed through the Subagent Turn routes. The cursor belongs to the same Session and tenant; any other after value, including a malformed one or a Subagent Turn ID, returns not found. Usage contains the latest recorded complete token breakdown; missing measurements remain null.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Last Turn ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 1
+ description: Page size
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.TurnList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List execution Turns
+ tags:
+ - Turns
+ /agents/sessions/{session_id}/turns/{turn_id}:
+ get:
+ description: Returns a root Turn of this Session. A Subagent Turn ID returns the same not found error as a missing Turn; read it through the Subagent Turn routes.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Session ID
+ in: path
+ name: session_id
+ required: true
+ - schema:
+ type: string
+ description: Turn ID
+ in: path
+ name: turn_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Turn'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve an execution Turn
+ tags:
+ - Turns
+ /files:
+ get:
+ description: Lists project-owned Files without reading their bodies. The limit defaults to 10000 and must be 1–10000. Equal creation times use ID ordering. Purpose validation precedes cursor lookup; current storage contains only user_data. An explicit empty purpose is treated as omitted. Repeated purpose values remain rejected. Hosted positive filtering, default order and concurrent-page behavior remain unverified. No Beta header is required.
+ parameters:
+ - schema:
+ type: string
+ description: Last File ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 10000
+ minimum: 1
+ description: Maximum page size, 1–10000
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ description: Only return Files with this purpose
+ in: query
+ name: purpose
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFileList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List source files
+ tags:
+ - Files
+ post:
+ description: Accepts one multipart file and purpose=user_data in either order, with a private 512 MiB content limit and 64 KiB envelope allowance. Commits only after the entire request validates. The source is project-owned, independent of Sessions and workspace copies. No Beta header is required. Other purposes, expires_after, listing, resumable Uploads, quotas/rate-limit and complete hosted error/status parity remain unsupported or unverified.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFile'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Upload a source file
+ tags:
+ - Files
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ file:
+ type: string
+ description: Source bytes
+ purpose:
+ type: string
+ description: user_data
+ required:
+ - file
+ - purpose
+ /files/{file_id}:
+ delete:
+ description: Atomically deletes project-owned metadata and stored bytes. Already-admitted reads or copies may finish. Workspace copies remain independent. Historical WAL/backups are not erased. No Beta header is required; exact hosted concurrent deletion/error semantics remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: Source file ID
+ in: path
+ name: file_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFileDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete a source file
+ tags:
+ - Files
+ get:
+ description: Returns immutable project-owned user_data file metadata. No Beta header is required. Other purposes, expiration and full hosted status/error semantics remain unimplemented or unverified.
+ parameters:
+ - schema:
+ type: string
+ description: Source file ID
+ in: path
+ name: file_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SourceFile'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve source file metadata
+ tags:
+ - Files
+ /files/{file_id}/content:
+ get:
+ description: Resolves project-owned File metadata before enforcing download policy. Public download of user_data Files returns 400; missing and foreign Files return the same 404. Internal initial-file and workspace copies remain available. No Beta header is required.
+ parameters:
+ - schema:
+ type: string
+ description: Source file ID
+ in: path
+ name: file_id
+ required: true
+ responses:
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Download source file bytes
+ tags:
+ - Files
+ /skills:
+ get:
+ description: Lists tenant-owned metadata in timestamp order. Default page size 20, maximum 100. Limit 0 returns an empty page whose has_more reports whether any Skill follows the cursor; exact hosted defaults remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: Skill resource cursor
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 0
+ description: Page size; 0 returns an empty page
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillList'
+ security:
+ - BearerAuth: []
+ summary: List Skills
+ tags:
+ - Skills
+ post:
+ description: Accepts one ZIP in files or a directory in files[]. Applies the qualified portable Skill bundle profile. No Beta header is required; full hosted upload limits and activation extensions are not qualified.
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Skill'
+ security:
+ - BearerAuth: []
+ summary: Upload a Skill
+ tags:
+ - Skills
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ files:
+ type: string
+ description: Skill ZIP or directory files
+ required:
+ - files
+ /skills/{skill_id}:
+ delete:
+ description: Deletes tenant-owned source bundles. Existing Session installation snapshots remain independent.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillDeleted'
+ security:
+ - BearerAuth: []
+ summary: Delete a Skill and its versions
+ tags:
+ - Skills
+ get:
+ description: Returns tenant-owned metadata without decrypting contents or starting Runtime. No Beta header is required.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Skill'
+ security:
+ - BearerAuth: []
+ summary: Retrieve Skill metadata
+ tags:
+ - Skills
+ post:
+ description: Changes only the tenant-owned default pointer; immutable versions and existing Session snapshots remain unchanged.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Skill'
+ security:
+ - BearerAuth: []
+ summary: Update the default Skill version
+ tags:
+ - Skills
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillUpdateRequest'
+ description: Default version
+ required: true
+ /skills/{skill_id}/content:
+ get:
+ description: Downloads an authorized ZIP using the default pointer when no concrete version is supplied. Exact upstream unversioned selection, content headers and range semantics remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ security:
+ - BearerAuth: []
+ summary: Download Skill content
+ tags:
+ - Skills
+ /skills/{skill_id}/versions:
+ get:
+ description: Orders by version number; after identifies a version resource, not a version number. An after value that does not begin with skillver, or a version of another Skill, returns 400 invalid_value with param after; a missing version returns not found. No contents are decrypted. Limit 0 returns an empty page whose has_more reports whether any version follows the cursor.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Version resource cursor
+ in: query
+ name: after
+ - schema:
+ type: integer
+ maximum: 100
+ minimum: 0
+ description: Page size; 0 returns an empty page
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Version order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersionList'
+ security:
+ - BearerAuth: []
+ summary: List Skill versions
+ tags:
+ - Skills
+ post:
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersion'
+ security:
+ - BearerAuth: []
+ summary: Upload an immutable Skill version
+ tags:
+ - Skills
+ requestBody:
+ content:
+ application/x-www-form-urlencoded:
+ schema:
+ type: object
+ properties:
+ files:
+ type: string
+ description: Skill ZIP or directory files
+ default:
+ type: boolean
+ description: Set as default
+ required:
+ - files
+ /skills/{skill_id}/versions/{version}:
+ delete:
+ description: Deleting the only remaining version also deletes the Skill; existing Session installation snapshots remain independent. The default version cannot be deleted while other versions remain. Version numbers are never reused.
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Concrete version number
+ in: path
+ name: version
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersionDeleted'
+ security:
+ - BearerAuth: []
+ summary: Delete a Skill version
+ tags:
+ - Skills
+ get:
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Concrete version number
+ in: path
+ name: version
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.SkillVersion'
+ security:
+ - BearerAuth: []
+ summary: Retrieve Skill version metadata
+ tags:
+ - Skills
+ /skills/{skill_id}/versions/{version}/content:
+ get:
+ parameters:
+ - schema:
+ type: string
+ description: Skill ID
+ in: path
+ name: skill_id
+ required: true
+ - schema:
+ type: string
+ description: Concrete version number
+ in: path
+ name: version
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/octet-stream:
+ schema:
+ type: string
+ format: binary
+ security:
+ - BearerAuth: []
+ summary: Download immutable Skill version content
+ tags:
+ - Skills
+ /vaults:
+ get:
+ description: Lists project-owned Vaults independently of execution. An unknown, malformed or foreign after cursor returns not found. Includes active and archived records by default. Status accepts a scalar, the SDK's status[] array or both, filtering by their union; a repeated scalar is rejected. Limits default to 20 and clamp to 1–100. Equal creation times use ID ordering; exact hosted errors and concurrent-page behavior remain unverified. Archive/delete lifecycle is not implemented.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Last Vault ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ description: Requested page size, clamped to 1–100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ enum:
+ - active
+ - archived
+ description: Scalar status filter
+ in: query
+ name: status
+ - schema:
+ type: array
+ items:
+ enum:
+ - active
+ - archived
+ type: string
+ style: form
+ explode: true
+ description: Array status filter; combined with status as a union
+ in: query
+ name: status[]
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.VaultList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List Vaults
+ tags:
+ - Vaults
+ post:
+ description: Creates a project-owned Vault independently of execution. Omitted name stays null; a supplied string is trimmed and must contain 1–256 UTF-8 bytes. Explicit null name is invalid. Omitted/null metadata becomes an empty object; non-string values return invalid_request_error with a metadata. param. Metadata has a local 64 KiB encoded storage bound. U+0000 in stored strings is rejected as a local storage limit. Credentials, Session binding and hosted error/retry parity remain incomplete.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Vault'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Create a Vault
+ tags:
+ - Vaults
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CreateVaultRequest'
+ description: Vault name and metadata
+ required: true
+ /vaults/{vault_id}:
+ delete:
+ description: Atomically removes the authenticated project's Vault and all its stored Credentials without an encryption key, decryption or external requests. Existing Session snapshots, history and recorded retries retain their frozen identities; subsequent credential lookups fail without reselection or anonymous fallback. Already-resolved tokens and running Sessions are not revoked or cancelled. Missing/repeated deletion locally returns 404. Exact hosted archive, post-delete visibility and concurrent/error semantics remain unverified; physical erasure from native history, WAL or backups is not established.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.VaultDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete a Vault and all its Credentials
+ tags:
+ - Vaults
+ get:
+ description: Reads a Vault owned by the authenticated project without resolving credentials, Sessions or execution devices. Missing and foreign IDs share the same not-found response; exact hosted error semantics remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Vault'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve a Vault
+ tags:
+ - Vaults
+ /vaults/{vault_id}/credentials:
+ get:
+ description: Lists only metadata from the authenticated project's requested Vault, without decryption or execution. An unknown, malformed or foreign after cursor, including another Vault's Credential, returns not found. Includes active and archived Credentials by default, independently of Vault status. Status accepts a scalar, the SDK status[] array or both, filtering by their union; a repeated scalar is rejected. Limits default to 20 and clamp to 1–100. Equal creation times use ID ordering. Hosted errors, concurrent-page behavior and archive/delete lifecycle remain unverified or unimplemented.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Last Credential ID from the previous page
+ in: query
+ name: after
+ - schema:
+ type: integer
+ description: Requested page size, clamped to 1–100
+ in: query
+ name: limit
+ - schema:
+ type: string
+ enum:
+ - asc
+ - desc
+ description: Creation order; omit for descending, explicit empty values are invalid
+ in: query
+ name: order
+ - schema:
+ type: string
+ enum:
+ - active
+ - archived
+ description: Scalar status filter
+ in: query
+ name: status
+ - schema:
+ type: array
+ items:
+ enum:
+ - active
+ - archived
+ type: string
+ style: form
+ explode: true
+ description: Array status filter; combined with status as a union
+ in: query
+ name: status[]
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CredentialList'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: List safe Vault Credential metadata
+ tags:
+ - Credentials
+ post:
+ description: Stores static_bearer or mcp_oauth secrets as execution-owned authenticated ciphertext without contacting any endpoint. Static bearer and OAuth access tokens must be nonempty strings; their bytes are preserved. OAuth accepts a required access token, nullable RFC3339 expiry and optional refresh configuration with none, client_secret_basic or client_secret_post authentication. Required name is trimmed to 1–256 UTF-8 bytes. Credential and token endpoints require HTTPS without userinfo or fragments. Responses contain safe metadata only, including explicit nullable OAuth expiry, refresh, resource and scope. Missing encryption configuration returns local 503. External authorization and provider revocation remain caller responsibilities; exact hosted error/default semantics remain unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Credential'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Create a Vault Credential
+ tags:
+ - Credentials
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CreateCredentialRequest'
+ description: Write-only credential authentication union
+ required: true
+ /vaults/{vault_id}/credentials/{credential_id}:
+ delete:
+ description: Removes one Credential and its encrypted token within the authenticated project and owning Vault, without an encryption key or secret decryption. Subsequent metadata reads, updates and dispatch lookups cannot use it. Existing Session snapshots and history retain their frozen identities; already-resolved tokens and running Sessions are not revoked or cancelled. This local policy removes the row rather than defining archived lifecycle; missing/repeated deletion returns 404. Exact hosted archive, post-delete visibility and retry/error semantics remain unverified. Provider revocation and physical erasure from native history, WAL or backups are separate concerns.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Credential ID
+ in: path
+ name: credential_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.CredentialDeleted'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Delete a Vault Credential
+ tags:
+ - Credentials
+ get:
+ description: Reads only non-secret metadata scoped to the authenticated project and owning Vault. No token decryption, network request or execution is performed. Unknown, foreign, wrong-Vault and malformed IDs use the same local not-found response; hosted error parity remains unverified.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Credential ID
+ in: path
+ name: credential_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Credential'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Retrieve safe Vault Credential metadata
+ tags:
+ - Credentials
+ post:
+ description: Explicitly empty static bearer or OAuth access tokens and OAuth patches without a mutable field are rejected before storage. Omitted OAuth access tokens preserve the existing grant when expiry or refresh fields change. Updates the existing static_bearer or mcp_oauth authentication method without network requests. OAuth access_token omission/null retains the token; a new token clears omitted expiry, explicit null clears expiry, and other omitted fields remain unchanged. OAuth refresh patches cannot add configuration or change client, endpoint, resource or authentication method; nullable token/client-secret values retain stored secrets while explicit null scope clears scope. Whole-null refresh and token_endpoint_auth retain existing configuration under local policy. Identity, destination, creation time and Session bindings remain unchanged. Responses expose safe metadata only. Already-dispatched work is not revoked; provider revocation, storage-key rotation and exact hosted concurrent-update/error semantics remain separate.
+ parameters:
+ - schema:
+ type: string
+ description: agents=v1
+ in: header
+ name: OpenAI-Beta
+ required: true
+ - schema:
+ type: string
+ description: Vault ID
+ in: path
+ name: vault_id
+ required: true
+ - schema:
+ type: string
+ description: Credential ID
+ in: path
+ name: credential_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.Credential'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '413':
+ description: Request Entity Too Large
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - BearerAuth: []
+ summary: Replace Vault Credential authentication secrets
+ tags:
+ - Credentials
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.UpdateCredentialRequest'
+ description: Write-only credential authentication replacement union
+ required: true
+schemes:
+ - http
+ - https
+openapi: 3.1.0
+servers:
+ - url: https://core.example.com/v1
+ description: Documentation placeholder; substitute your Core public origin.
+components:
+ schemas:
+ v1.APIError:
+ properties:
+ code:
+ anyOf:
+ - type: string
+ - type: 'null'
+ message:
+ type: string
+ param:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type:
+ type: string
+ required:
+ - message
+ - type
+ type: object
+ v1.Agent:
+ properties:
+ id:
+ type: string
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ model:
+ type: string
+ multi_agent:
+ $ref: '#/components/schemas/v1.MultiAgentConfig'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ reasoning:
+ $ref: '#/components/schemas/v1.Reasoning'
+ service_tier:
+ enum:
+ - auto
+ type: string
+ text:
+ $ref: '#/components/schemas/v1.TextConfig'
+ tools:
+ items:
+ type: object
+ type: array
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.AgentsCore'
+ - type: 'null'
+ required:
+ - id
+ - model
+ - multi_agent
+ - reasoning
+ - service_tier
+ - text
+ - tools
+ type: object
+ v1.AgentContent:
+ properties:
+ encrypted_content:
+ type: string
+ text:
+ type: string
+ type:
+ enum:
+ - output_text
+ - encrypted_content
+ type: string
+ required:
+ - type
+ type: object
+ v1.AgentDeleted:
+ properties:
+ deleted:
+ enum:
+ - true
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.AgentsCore:
+ properties:
+ harness:
+ enum:
+ - codex
+ - claude_sdk
+ - mcode
+ type: string
+ required:
+ - harness
+ type: object
+ v1.CreateAgentRequest:
+ properties:
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ metadata:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ model:
+ type: string
+ multi_agent:
+ anyOf:
+ - type: object
+ - type: 'null'
+ name:
+ anyOf:
+ - maxLength: 128
+ type: string
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.Reasoning'
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - enum:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ type: string
+ - type: 'null'
+ text:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentTextInput'
+ - type: 'null'
+ tools:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentCoreInput'
+ - type: 'null'
+ required:
+ - model
+ type: object
+ v1.CreateCredentialRequest:
+ properties:
+ auth:
+ $ref: '#/components/schemas/v1.CredentialAuthInput'
+ name:
+ type: string
+ required:
+ - auth
+ - name
+ type: object
+ v1.CreateEventsRequest:
+ properties:
+ events:
+ items:
+ $ref: '#/components/schemas/v1.SessionInput'
+ type: array
+ required:
+ - events
+ type: object
+ v1.CreateSessionRequest:
+ properties:
+ agent:
+ $ref: '#/components/schemas/v1.InlineAgent'
+ agent_id:
+ type: string
+ environment:
+ $ref: '#/components/schemas/v1.Environment'
+ input:
+ anyOf:
+ - {}
+ - type: 'null'
+ description: |-
+ Input accepts a string or an ordered array of user InputMessage objects.
+ Required for none and streamed creation outside self_hosted; otherwise optional.
+ metadata:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ stream:
+ default: false
+ type: boolean
+ vault_ids:
+ items:
+ type: string
+ type: array
+ x_agents_core:
+ $ref: '#/components/schemas/v1.SessionExecutionInput'
+ required:
+ - environment
+ type: object
+ v1.CreateVaultRequest:
+ properties:
+ metadata:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ name:
+ type: string
+ type: object
+ v1.Credential:
+ properties:
+ auth:
+ $ref: '#/components/schemas/v1.CredentialAuth'
+ created_at:
+ type: integer
+ id:
+ type: string
+ name:
+ type: string
+ object:
+ enum:
+ - vault.credential
+ type: string
+ updated_at:
+ type: integer
+ vault_id:
+ type: string
+ required:
+ - auth
+ - created_at
+ - id
+ - name
+ - object
+ - updated_at
+ - vault_id
+ type: object
+ v1.CredentialAuth:
+ properties:
+ expires_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ mcp_server_url:
+ type: string
+ refresh:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.OAuthCredentialRefresh'
+ - type: 'null'
+ type:
+ enum:
+ - static_bearer
+ - mcp_oauth
+ type: string
+ required:
+ - mcp_server_url
+ - type
+ type: object
+ v1.CredentialAuthInput:
+ properties:
+ access_token:
+ minLength: 1
+ type: string
+ expires_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ mcp_server_url:
+ type: string
+ refresh:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.OAuthCredentialRefreshInput'
+ - type: 'null'
+ token:
+ minLength: 1
+ type: string
+ type:
+ enum:
+ - static_bearer
+ - mcp_oauth
+ type: string
+ required:
+ - mcp_server_url
+ - type
+ type: object
+ v1.CredentialAuthReplacement:
+ properties:
+ access_token:
+ anyOf:
+ - minLength: 1
+ type: string
+ - type: 'null'
+ expires_at:
+ anyOf:
+ - type: string
+ - type: 'null'
+ refresh:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.OAuthCredentialRefreshReplacement'
+ - type: 'null'
+ token:
+ minLength: 1
+ type: string
+ type:
+ enum:
+ - static_bearer
+ - mcp_oauth
+ type: string
+ required:
+ - type
+ type: object
+ v1.CredentialDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - vault.credential.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.CredentialList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Credential'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.Environment:
+ properties:
+ capability_directories:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ - type: 'null'
+ env:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ environment_template_id:
+ type: string
+ files:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ network:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.EnvironmentNetworkInput'
+ - type: 'null'
+ packages:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.EnvironmentPackages'
+ - type: 'null'
+ plugins:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ setup_commands:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ skills:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ type:
+ enum:
+ - none
+ - self_hosted
+ - openai_hosted
+ type: string
+ workspace_directory:
+ type: string
+ required:
+ - type
+ type: object
+ v1.EnvironmentFile:
+ properties:
+ environment_id:
+ type: string
+ object:
+ enum:
+ - agent.environment.file
+ type: string
+ path:
+ type: string
+ size_bytes:
+ minimum: 0
+ type: integer
+ required:
+ - environment_id
+ - object
+ - path
+ - size_bytes
+ type: object
+ v1.EnvironmentFileCreateRequest:
+ properties:
+ data:
+ type: string
+ file_id:
+ type: string
+ path:
+ type: string
+ type:
+ enum:
+ - inline
+ - file_id
+ type: string
+ required:
+ - path
+ - type
+ type: object
+ v1.EnvironmentFileList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.EnvironmentFile'
+ type: array
+ has_more:
+ type: boolean
+ next:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - page
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.EnvironmentInfo:
+ properties:
+ files:
+ items:
+ type: object
+ type: array
+ id:
+ type: string
+ object:
+ enum:
+ - agent.environment
+ type: string
+ plugins:
+ items:
+ type: object
+ type: array
+ skills:
+ items:
+ type: object
+ type: array
+ status:
+ enum:
+ - pending
+ - connected
+ - disconnected
+ - expired
+ - failed
+ type: string
+ type:
+ enum:
+ - openai_hosted
+ - self_hosted
+ type: string
+ required:
+ - files
+ - id
+ - object
+ - plugins
+ - skills
+ - status
+ - type
+ type: object
+ v1.EnvironmentNetwork:
+ properties:
+ access:
+ enum:
+ - enabled
+ - disabled
+ - restricted
+ type: string
+ allowed_domains:
+ items:
+ type: string
+ type: array
+ required:
+ - access
+ - allowed_domains
+ type: object
+ v1.EnvironmentNetworkInput:
+ properties:
+ access:
+ enum:
+ - enabled
+ - disabled
+ - restricted
+ type: string
+ allowed_domains:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ - type: 'null'
+ required:
+ - access
+ type: object
+ v1.EnvironmentPackages:
+ properties:
+ npm:
+ items:
+ type: string
+ type: array
+ python:
+ items:
+ type: string
+ type: array
+ system:
+ items:
+ type: string
+ type: array
+ required:
+ - npm
+ - python
+ - system
+ type: object
+ v1.EnvironmentPackagesInput:
+ properties:
+ npm:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ - type: 'null'
+ python:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ - type: 'null'
+ system:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ - type: 'null'
+ type: object
+ v1.EnvironmentTemplate:
+ properties:
+ capability_directories:
+ items:
+ type: string
+ type: array
+ created_at:
+ type: integer
+ files:
+ items:
+ type: object
+ type: array
+ id:
+ type: string
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ network:
+ $ref: '#/components/schemas/v1.EnvironmentNetwork'
+ object:
+ enum:
+ - agent.environment.template
+ type: string
+ packages:
+ $ref: '#/components/schemas/v1.EnvironmentPackages'
+ plugins:
+ items:
+ type: object
+ type: array
+ skills:
+ items:
+ type: object
+ type: array
+ updated_at:
+ type: integer
+ required:
+ - capability_directories
+ - created_at
+ - files
+ - id
+ - network
+ - object
+ - packages
+ - plugins
+ - skills
+ - updated_at
+ type: object
+ v1.EnvironmentTemplateDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.environment.template.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.EnvironmentTemplateList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.EnvironmentTemplate'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.EnvironmentTemplateRequest:
+ properties:
+ capability_directories:
+ anyOf:
+ - items:
+ type: string
+ type: array
+ - type: 'null'
+ env:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ files:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ network:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.EnvironmentNetworkInput'
+ - type: 'null'
+ packages:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.EnvironmentPackagesInput'
+ - type: 'null'
+ plugins:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ setup_commands:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ skills:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ type: object
+ v1.ErrorResponse:
+ properties:
+ error:
+ $ref: '#/components/schemas/v1.APIError'
+ required:
+ - error
+ type: object
+ v1.InlineAgent:
+ properties:
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ model:
+ type: string
+ multi_agent:
+ anyOf:
+ - type: object
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.Reasoning'
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - enum:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ type: string
+ - type: 'null'
+ text:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentTextInput'
+ - type: 'null'
+ tools:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.AgentsCore'
+ - type: 'null'
+ type: object
+ v1.InputContent:
+ properties:
+ image_url:
+ type: string
+ text:
+ type: string
+ type:
+ enum:
+ - input_text
+ - input_image
+ type: string
+ required:
+ - type
+ type: object
+ v1.InputMessage:
+ properties:
+ content:
+ items:
+ $ref: '#/components/schemas/v1.InputContent'
+ type: array
+ role:
+ enum:
+ - user
+ type: string
+ type:
+ enum:
+ - message
+ type: string
+ required:
+ - content
+ - role
+ type: object
+ v1.InputTokenDetails:
+ properties:
+ cached_tokens:
+ type: integer
+ required:
+ - cached_tokens
+ type: object
+ v1.Item:
+ properties:
+ action:
+ $ref: '#/components/schemas/v1.WebSearchAction'
+ agent_id:
+ type: string
+ arguments: {}
+ call_id:
+ type: string
+ command:
+ type: string
+ content:
+ items:
+ $ref: '#/components/schemas/v1.ItemContent'
+ type: array
+ cwd:
+ type: string
+ duration_ms:
+ type: integer
+ error: {}
+ exit_code:
+ type: integer
+ id:
+ type: string
+ model:
+ type: string
+ name:
+ type: string
+ output: {}
+ phase:
+ anyOf:
+ - enum:
+ - commentary
+ - final_answer
+ type: string
+ - type: 'null'
+ reasoning_effort:
+ type: string
+ recipient_agent_id:
+ type: string
+ recipient_agent_ids:
+ items:
+ type: string
+ type: array
+ role:
+ enum:
+ - user
+ - assistant
+ type: string
+ sender_agent_id:
+ type: string
+ server_label:
+ type: string
+ status:
+ enum:
+ - in_progress
+ - completed
+ - failed
+ - incomplete
+ type: string
+ summary:
+ items:
+ $ref: '#/components/schemas/v1.SummaryText'
+ type: array
+ turn_id:
+ type: string
+ type:
+ enum:
+ - message
+ - command_execution
+ - mcp_call
+ - function_call
+ - function_call_output
+ - web_search_call
+ - reasoning
+ - agent_message
+ - create_subagent_call
+ - send_subagent_input_call
+ - resume_subagent_call
+ - wait_for_subagents_call
+ - interrupt_subagent_call
+ - close_subagent_call
+ type: string
+ required:
+ - id
+ - turn_id
+ - type
+ type: object
+ v1.ItemContent:
+ properties:
+ encrypted_content:
+ type: string
+ image_url:
+ type: string
+ text:
+ type: string
+ type:
+ enum:
+ - input_text
+ - output_text
+ - input_image
+ - encrypted_content
+ type: string
+ required:
+ - type
+ type: object
+ v1.ItemList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Item'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.ModelProviderInput:
+ properties:
+ api_key:
+ type: string
+ base_url:
+ type: string
+ context_window:
+ type: integer
+ max_output_tokens:
+ type: integer
+ protocol:
+ enum:
+ - anthropic
+ - responses
+ type: string
+ required:
+ - api_key
+ - base_url
+ - protocol
+ type: object
+ v1.ModelProviderView:
+ properties:
+ api_key_configured:
+ type: boolean
+ base_url:
+ type: string
+ context_window:
+ type: integer
+ max_output_tokens:
+ type: integer
+ protocol:
+ enum:
+ - anthropic
+ - responses
+ type: string
+ required:
+ - api_key_configured
+ - base_url
+ - protocol
+ type: object
+ v1.MultiAgentConfig:
+ properties:
+ enabled:
+ type: boolean
+ max_concurrent_subagents:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ required:
+ - enabled
+ type: object
+ v1.OAuthCredentialRefresh:
+ properties:
+ client_id:
+ type: string
+ resource:
+ anyOf:
+ - type: string
+ - type: 'null'
+ scope:
+ anyOf:
+ - type: string
+ - type: 'null'
+ token_endpoint:
+ type: string
+ token_endpoint_auth:
+ $ref: '#/components/schemas/v1.OAuthEndpointAuth'
+ required:
+ - client_id
+ - token_endpoint
+ - token_endpoint_auth
+ type: object
+ v1.OAuthCredentialRefreshInput:
+ properties:
+ client_id:
+ type: string
+ refresh_token:
+ type: string
+ resource:
+ anyOf:
+ - type: string
+ - type: 'null'
+ scope:
+ anyOf:
+ - type: string
+ - type: 'null'
+ token_endpoint:
+ type: string
+ token_endpoint_auth:
+ $ref: '#/components/schemas/v1.OAuthEndpointAuthInput'
+ required:
+ - client_id
+ - refresh_token
+ - token_endpoint
+ - token_endpoint_auth
+ type: object
+ v1.OAuthCredentialRefreshReplacement:
+ properties:
+ refresh_token:
+ anyOf:
+ - type: string
+ - type: 'null'
+ scope:
+ anyOf:
+ - type: string
+ - type: 'null'
+ token_endpoint_auth:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.OAuthEndpointAuthReplacement'
+ - type: 'null'
+ type: object
+ v1.OAuthEndpointAuth:
+ properties:
+ type:
+ enum:
+ - none
+ - client_secret_basic
+ - client_secret_post
+ type: string
+ required:
+ - type
+ type: object
+ v1.OAuthEndpointAuthInput:
+ properties:
+ client_secret:
+ type: string
+ type:
+ enum:
+ - none
+ - client_secret_basic
+ - client_secret_post
+ type: string
+ required:
+ - type
+ type: object
+ v1.OAuthEndpointAuthReplacement:
+ properties:
+ client_secret:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type:
+ enum:
+ - client_secret_basic
+ - client_secret_post
+ type: string
+ required:
+ - type
+ type: object
+ v1.OutputTokenDetails:
+ properties:
+ reasoning_tokens:
+ type: integer
+ required:
+ - reasoning_tokens
+ type: object
+ v1.Reasoning:
+ properties:
+ effort:
+ anyOf:
+ - type: string
+ - type: 'null'
+ summary:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type: object
+ v1.RequiredAction:
+ properties:
+ arguments: {}
+ call_id:
+ type: string
+ environment_id:
+ type: string
+ name:
+ type: string
+ turn_id:
+ type: string
+ type:
+ enum:
+ - function_call
+ - environment_connection
+ type: string
+ required:
+ - type
+ type: object
+ v1.SavedAgent:
+ properties:
+ created_at:
+ type: integer
+ id:
+ type: string
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ metadata:
+ additionalProperties:
+ type: string
+ type: object
+ model:
+ type: string
+ multi_agent:
+ $ref: '#/components/schemas/v1.MultiAgentConfig'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - agent
+ type: string
+ reasoning:
+ $ref: '#/components/schemas/v1.Reasoning'
+ service_tier:
+ enum:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ type: string
+ text:
+ $ref: '#/components/schemas/v1.SavedAgentText'
+ tools:
+ items:
+ type: object
+ type: array
+ updated_at:
+ type: integer
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentCore'
+ - type: 'null'
+ required:
+ - created_at
+ - id
+ - metadata
+ - model
+ - multi_agent
+ - object
+ - reasoning
+ - service_tier
+ - text
+ - tools
+ - updated_at
+ type: object
+ v1.SavedAgentCore:
+ properties:
+ harness:
+ enum:
+ - codex
+ - claude_sdk
+ - mcode
+ type: string
+ model_provider:
+ $ref: '#/components/schemas/v1.ModelProviderView'
+ type: object
+ v1.SavedAgentCoreInput:
+ properties:
+ harness:
+ enum:
+ - codex
+ - claude_sdk
+ - mcode
+ type: string
+ model_provider:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.ModelProviderInput'
+ - type: 'null'
+ type: object
+ v1.SavedAgentList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SavedAgent'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SavedAgentText:
+ properties:
+ format:
+ $ref: '#/components/schemas/v1.SavedAgentTextFormat'
+ verbosity:
+ enum:
+ - low
+ - medium
+ - high
+ type: string
+ required:
+ - format
+ - verbosity
+ type: object
+ v1.SavedAgentTextFormat:
+ properties:
+ schema:
+ type: object
+ type:
+ enum:
+ - text
+ - json_schema
+ type: string
+ required:
+ - type
+ type: object
+ v1.SavedAgentTextInput:
+ properties:
+ format:
+ anyOf:
+ - type: object
+ - type: 'null'
+ verbosity:
+ anyOf:
+ - enum:
+ - low
+ - medium
+ - high
+ type: string
+ - type: 'null'
+ type: object
+ v1.Session:
+ properties:
+ agent:
+ $ref: '#/components/schemas/v1.Agent'
+ created_at:
+ type: integer
+ environment:
+ $ref: '#/components/schemas/v1.SessionEnvironment'
+ error:
+ anyOf:
+ - type: string
+ - type: 'null'
+ id:
+ type: string
+ last_active_at:
+ type: integer
+ metadata:
+ additionalProperties:
+ type: string
+ type: object
+ object:
+ enum:
+ - agent.session
+ type: string
+ required_actions:
+ items:
+ $ref: '#/components/schemas/v1.RequiredAction'
+ type: array
+ status:
+ enum:
+ - idle
+ - in_progress
+ - requires_action
+ - failed
+ type: string
+ usage:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TokenUsage'
+ - type: 'null'
+ vault_ids:
+ items:
+ type: string
+ type: array
+ required:
+ - agent
+ - created_at
+ - environment
+ - id
+ - last_active_at
+ - metadata
+ - object
+ - required_actions
+ - status
+ - vault_ids
+ type: object
+ v1.SessionArtifact:
+ properties:
+ created_at:
+ type: integer
+ environment_id:
+ type: string
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.artifact
+ type: string
+ path:
+ type: string
+ session_id:
+ type: string
+ size_bytes:
+ type: integer
+ turn_id:
+ type: string
+ required:
+ - created_at
+ - environment_id
+ - id
+ - object
+ - path
+ - session_id
+ - size_bytes
+ - turn_id
+ type: object
+ v1.SessionArtifactDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.artifact.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SessionArtifactList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SessionArtifact'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SessionDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SessionEnvironment:
+ properties:
+ capability_directories:
+ items:
+ type: string
+ type: array
+ files:
+ items:
+ type: object
+ type: array
+ id:
+ type: string
+ network:
+ $ref: '#/components/schemas/v1.EnvironmentNetwork'
+ packages:
+ $ref: '#/components/schemas/v1.EnvironmentPackages'
+ plugins:
+ items:
+ type: object
+ type: array
+ remote_url:
+ type: string
+ skills:
+ items:
+ type: object
+ type: array
+ type:
+ enum:
+ - none
+ - self_hosted
+ - openai_hosted
+ type: string
+ workspace_directory:
+ type: string
+ required:
+ - type
+ type: object
+ v1.SessionEnvironmentState:
+ properties:
+ error:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.StreamError'
+ - type: 'null'
+ id:
+ type: string
+ status:
+ enum:
+ - pending
+ - ready
+ - connected
+ - disconnected
+ - failed
+ type: string
+ type:
+ type: string
+ required:
+ - id
+ - status
+ - type
+ type: object
+ v1.SessionEvent:
+ properties:
+ content_index:
+ type: integer
+ delta:
+ type: string
+ environment:
+ $ref: '#/components/schemas/v1.SessionEnvironmentState'
+ error:
+ $ref: '#/components/schemas/v1.StreamError'
+ event_id:
+ type: string
+ item:
+ $ref: '#/components/schemas/v1.Item'
+ item_id:
+ type: string
+ output_index:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ part:
+ $ref: '#/components/schemas/v1.ItemContent'
+ session:
+ $ref: '#/components/schemas/v1.Session'
+ session_id:
+ type: string
+ subagent:
+ $ref: '#/components/schemas/v1.Subagent'
+ text:
+ type: string
+ turn:
+ $ref: '#/components/schemas/v1.Turn'
+ turn_id:
+ type: string
+ type:
+ type: string
+ usage:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TokenUsage'
+ - type: 'null'
+ description: |-
+ Usage is present only on terminal Turn events, where it mirrors the Turn
+ snapshot and is null when unknown. Other events omit it.
+ required:
+ - event_id
+ - type
+ type: object
+ v1.SessionExecutionInput:
+ properties:
+ model_provider:
+ $ref: '#/components/schemas/v1.ModelProviderInput'
+ type: object
+ v1.SessionInput:
+ properties:
+ call_id:
+ type: string
+ error:
+ anyOf:
+ - type: string
+ - type: 'null'
+ input:
+ items:
+ $ref: '#/components/schemas/v1.InputMessage'
+ type: array
+ output:
+ anyOf:
+ - {}
+ - type: 'null'
+ success:
+ type: boolean
+ turn_id:
+ type: string
+ type:
+ enum:
+ - agent.session.input.message
+ - agent.session.input.cancel
+ - agent.session.input.tool_result
+ type: string
+ required:
+ - type
+ type: object
+ v1.SessionList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Session'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.Skill:
+ properties:
+ created_at:
+ type: integer
+ default_version:
+ type: string
+ description:
+ type: string
+ id:
+ type: string
+ latest_version:
+ type: string
+ name:
+ type: string
+ object:
+ enum:
+ - skill
+ type: string
+ required:
+ - created_at
+ - default_version
+ - description
+ - id
+ - latest_version
+ - name
+ - object
+ type: object
+ v1.SkillDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - skill.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SkillList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Skill'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SkillUpdateRequest:
+ properties:
+ default_version:
+ type: string
+ required:
+ - default_version
+ type: object
+ v1.SkillVersion:
+ properties:
+ created_at:
+ type: integer
+ description:
+ type: string
+ id:
+ type: string
+ name:
+ type: string
+ object:
+ enum:
+ - skill.version
+ type: string
+ skill_id:
+ type: string
+ version:
+ type: string
+ required:
+ - created_at
+ - description
+ - id
+ - name
+ - object
+ - skill_id
+ - version
+ type: object
+ v1.SkillVersionDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - skill.version.deleted
+ type: string
+ version:
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ - version
+ type: object
+ v1.SkillVersionList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SkillVersion'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SourceFile:
+ properties:
+ bytes:
+ minimum: 0
+ type: integer
+ created_at:
+ type: integer
+ expires_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ filename:
+ type: string
+ id:
+ type: string
+ object:
+ enum:
+ - file
+ type: string
+ purpose:
+ enum:
+ - user_data
+ type: string
+ status:
+ enum:
+ - processed
+ type: string
+ status_details:
+ anyOf:
+ - type: string
+ - type: 'null'
+ required:
+ - bytes
+ - created_at
+ - filename
+ - id
+ - object
+ - purpose
+ - status
+ type: object
+ v1.SourceFileDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - file
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.SourceFileList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.SourceFile'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.StreamError:
+ properties:
+ code:
+ type: string
+ message:
+ type: string
+ param:
+ anyOf:
+ - type: string
+ - type: 'null'
+ description: |-
+ Param is the pinned SessionError field. An error SessionEvent always
+ carries it, null when unset; Environment state errors and Core's own
+ stream_interrupted frame omit it.
+ type:
+ type: string
+ type: object
+ v1.Subagent:
+ properties:
+ closed_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ id:
+ type: string
+ instructions:
+ anyOf:
+ - items:
+ $ref: '#/components/schemas/v1.AgentContent'
+ type: array
+ - type: 'null'
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - agent.session.subagent
+ type: string
+ opened_at:
+ type: integer
+ parent_agent_id:
+ type: string
+ session_id:
+ type: string
+ status:
+ enum:
+ - active
+ - closed
+ type: string
+ required:
+ - id
+ - object
+ - opened_at
+ - parent_agent_id
+ - session_id
+ - status
+ type: object
+ v1.SubagentList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Subagent'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.SummaryText:
+ properties:
+ text:
+ type: string
+ type:
+ enum:
+ - summary_text
+ type: string
+ required:
+ - text
+ - type
+ type: object
+ v1.TextConfig:
+ properties:
+ format:
+ $ref: '#/components/schemas/v1.TextFormat'
+ verbosity:
+ enum:
+ - low
+ - medium
+ - high
+ type: string
+ required:
+ - format
+ - verbosity
+ type: object
+ v1.TextFormat:
+ properties:
+ schema:
+ type: object
+ type:
+ enum:
+ - text
+ - json_schema
+ type: string
+ required:
+ - type
+ type: object
+ v1.TokenUsage:
+ properties:
+ input_tokens:
+ type: integer
+ input_tokens_details:
+ $ref: '#/components/schemas/v1.InputTokenDetails'
+ output_tokens:
+ type: integer
+ output_tokens_details:
+ $ref: '#/components/schemas/v1.OutputTokenDetails'
+ total_tokens:
+ type: integer
+ required:
+ - input_tokens
+ - input_tokens_details
+ - output_tokens
+ - output_tokens_details
+ - total_tokens
+ type: object
+ v1.Turn:
+ properties:
+ agent_id:
+ type: string
+ completed_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ created_at:
+ type: integer
+ error:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TurnError'
+ - type: 'null'
+ id:
+ type: string
+ object:
+ enum:
+ - agent.session.turn
+ type: string
+ session_id:
+ type: string
+ started_at:
+ anyOf:
+ - type: integer
+ - type: 'null'
+ status:
+ enum:
+ - queued
+ - in_progress
+ - waiting
+ - completed
+ - failed
+ - cancelled
+ type: string
+ subagent_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ usage:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.TokenUsage'
+ - type: 'null'
+ required:
+ - agent_id
+ - created_at
+ - id
+ - object
+ - session_id
+ - status
+ type: object
+ v1.TurnError:
+ properties:
+ code:
+ enum:
+ - context_length_exceeded
+ - session_budget_exceeded
+ - usage_limit_exceeded
+ - rate_limit_exceeded
+ - server_overloaded
+ - cyber_policy
+ - connection_failed
+ - server_error
+ - authentication_error
+ - invalid_request
+ - resource_not_found
+ - sandbox_error
+ - executor_version_incompatible
+ - active_turn_not_steerable
+ - request_timeout
+ - internal_error
+ type: string
+ message:
+ type: string
+ required:
+ - code
+ - message
+ type: object
+ v1.TurnList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Turn'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.UpdateAgentRequest:
+ properties:
+ instructions:
+ anyOf:
+ - type: string
+ - type: 'null'
+ metadata:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ model:
+ type: string
+ multi_agent:
+ anyOf:
+ - type: object
+ - type: 'null'
+ name:
+ anyOf:
+ - maxLength: 128
+ type: string
+ - type: 'null'
+ reasoning:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.Reasoning'
+ - type: 'null'
+ service_tier:
+ anyOf:
+ - enum:
+ - auto
+ - default
+ - flex
+ - priority
+ - fast
+ type: string
+ - type: 'null'
+ text:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentTextInput'
+ - type: 'null'
+ tools:
+ anyOf:
+ - items:
+ type: object
+ type: array
+ - type: 'null'
+ x_agents_core:
+ anyOf:
+ - allOf:
+ - $ref: '#/components/schemas/v1.SavedAgentCoreInput'
+ - type: 'null'
+ type: object
+ v1.UpdateCredentialRequest:
+ properties:
+ auth:
+ $ref: '#/components/schemas/v1.CredentialAuthReplacement'
+ required:
+ - auth
+ type: object
+ v1.UpdateSessionRequest:
+ properties:
+ metadata:
+ anyOf:
+ - additionalProperties:
+ type: string
+ type: object
+ - type: 'null'
+ required:
+ - metadata
+ type: object
+ v1.Vault:
+ properties:
+ created_at:
+ type: integer
+ id:
+ type: string
+ metadata:
+ additionalProperties:
+ type: string
+ type: object
+ name:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - vault
+ type: string
+ required:
+ - created_at
+ - id
+ - metadata
+ - object
+ type: object
+ v1.VaultDeleted:
+ properties:
+ deleted:
+ type: boolean
+ id:
+ type: string
+ object:
+ enum:
+ - vault.deleted
+ type: string
+ required:
+ - deleted
+ - id
+ - object
+ type: object
+ v1.VaultList:
+ properties:
+ data:
+ items:
+ $ref: '#/components/schemas/v1.Vault'
+ type: array
+ first_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ has_more:
+ type: boolean
+ last_id:
+ anyOf:
+ - type: string
+ - type: 'null'
+ object:
+ enum:
+ - list
+ type: string
+ required:
+ - data
+ - has_more
+ - object
+ type: object
+ v1.WebSearchAction:
+ properties:
+ pattern:
+ type: string
+ queries:
+ items:
+ type: string
+ type: array
+ query:
+ type: string
+ type:
+ enum:
+ - search
+ - open_page
+ - find_in_page
+ - other
+ type: string
+ url:
+ type: string
+ required:
+ - type
+ type: object
+ securitySchemes:
+ BearerAuth:
+ type: http
+ scheme: bearer
+tags:
+ - name: Agents
+ description: 'Agents. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Environments
+ description: 'Environments. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Environment Templates
+ description: 'Environment Templates. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Sessions
+ description: 'Sessions. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Artifacts
+ description: 'Artifacts. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Events
+ description: 'Events. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Items
+ description: 'Items. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Subagents
+ description: 'Subagents. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Turns
+ description: 'Turns. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Files
+ description: 'Files. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Skills
+ description: 'Skills. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Vaults
+ description: 'Vaults. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
+ - name: Credentials
+ description: 'Credentials. Application API: Project API key. Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.'
diff --git a/apps/docs/openapi/runtime-api.yaml b/apps/docs/openapi/runtime-api.yaml
new file mode 100644
index 000000000..140773236
--- /dev/null
+++ b/apps/docs/openapi/runtime-api.yaml
@@ -0,0 +1,331 @@
+# Generated documentation projection; do not edit.
+info:
+ contact: {}
+ description: Machine connection routes under /api/v1. Sandbox nodes authenticate with a one-use enrollment token or their node credential; Project API keys and the Core key are not accepted. See each operation's security requirements.
+ license:
+ name: Apache 2.0
+ url: https://www.apache.org/licenses/LICENSE-2.0.html
+ title: OpenAgentCore Machine Connections
+ version: '1'
+paths:
+ /api/v1/sandbox-node/configuration:
+ get:
+ description: Authenticates with an unconsumed enrollment token, or a retained node credential with X-OAC-Node-ID. Does not consume the token or expose E2B credentials. Node files cannot override this specification.
+ parameters:
+ - schema:
+ type: string
+ description: Retained node UUID
+ in: header
+ name: X-OAC-Node-ID
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeNodeConfiguration'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - NodeEnrollmentAuth: []
+ summary: Read the active configuration for node installation
+ tags:
+ - Sandbox Node
+ /api/v1/sandbox-node/enroll:
+ post:
+ description: Node machine connection. Consumes a one-use enrollment token; grants no project or administrator access. Responses contain only explicit safe fields. core_url is required and must equal the installation public URL; a different address gets 409 sandbox_node_address_mismatch and leaves the token unused.
+ responses:
+ '201':
+ description: Created
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeNodeIdentity'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - NodeEnrollmentAuth: []
+ summary: Enroll a sandbox node
+ tags:
+ - Sandbox Node
+ requestBody:
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeNodeEnrollment'
+ description: Request
+ required: true
+ /api/v1/sandbox-node/identity:
+ get:
+ description: Node machine connection. Authenticates with the retained node credential; grants no project or administrator access. Responses contain only explicit safe fields.
+ parameters:
+ - schema:
+ type: string
+ description: Sandbox node UUID
+ in: query
+ name: node_id
+ required: true
+ responses:
+ '200':
+ description: OK
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/store.RuntimeNodeStatus'
+ '400':
+ description: Bad Request
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '401':
+ description: Unauthorized
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '404':
+ description: Not Found
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '409':
+ description: Conflict
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '500':
+ description: Internal Server Error
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ '503':
+ description: Service Unavailable
+ content:
+ application/json:
+ schema:
+ $ref: '#/components/schemas/v1.ErrorResponse'
+ security:
+ - NodeAuth: []
+ summary: Recover an enrolled sandbox node identity and observe its readiness
+ tags:
+ - Sandbox Node
+schemes:
+ - http
+ - https
+openapi: 3.1.0
+servers:
+ - url: https://core.example.com
+ description: Documentation placeholder; substitute your Core public origin.
+components:
+ schemas:
+ sandbox.DeploymentSpec:
+ properties:
+ resources:
+ $ref: '#/components/schemas/sandbox.Resources'
+ runtime:
+ $ref: '#/components/schemas/sandbox.RuntimeRelease'
+ type: object
+ sandbox.Resources:
+ properties:
+ cpus:
+ type: integer
+ environment_disk_mib:
+ type: integer
+ memory_mib:
+ type: integer
+ root_disk_mib:
+ type: integer
+ type: object
+ sandbox.RuntimeRelease:
+ properties:
+ firmware_sha256:
+ type: string
+ image_id:
+ type: string
+ image_manifest_digest:
+ type: string
+ microsandbox_ref:
+ type: string
+ runtime_sha256:
+ type: string
+ source_commit:
+ type: string
+ type: object
+ store.RuntimeNodeConfiguration:
+ properties:
+ core_url:
+ type: string
+ generation:
+ type: integer
+ installation_id:
+ type: string
+ max_active:
+ type: integer
+ max_retained:
+ type: integer
+ provider:
+ type: string
+ specification:
+ $ref: '#/components/schemas/sandbox.DeploymentSpec'
+ specification_digest:
+ type: string
+ type: object
+ store.RuntimeNodeEnrollment:
+ properties:
+ backend_fingerprint:
+ type: string
+ core_url:
+ description: The Core origin this node stores and connects to, such as https://core.example. It must equal the installation public URL; otherwise enrollment gets 409 sandbox_node_address_mismatch and the token stays unused.
+ type: string
+ credential:
+ type: string
+ deployment_generation:
+ type: integer
+ name:
+ type: string
+ node_id:
+ type: string
+ provider:
+ type: string
+ specification_digest:
+ type: string
+ type: object
+ store.RuntimeNodeIdentity:
+ properties:
+ deployment_generation:
+ type: integer
+ installation_id:
+ type: string
+ max_active:
+ type: integer
+ max_retained:
+ type: integer
+ node_id:
+ type: string
+ provider:
+ type: string
+ specification_digest:
+ type: string
+ type: object
+ store.RuntimeNodeStatus:
+ properties:
+ connected:
+ type: boolean
+ deployment_generation:
+ type: integer
+ installation_id:
+ type: string
+ max_active:
+ type: integer
+ max_retained:
+ type: integer
+ node_id:
+ type: string
+ provider:
+ type: string
+ provider_ready:
+ type: boolean
+ specification_digest:
+ type: string
+ type: object
+ v1.APIError:
+ properties:
+ code:
+ anyOf:
+ - type: string
+ - type: 'null'
+ message:
+ type: string
+ param:
+ anyOf:
+ - type: string
+ - type: 'null'
+ type:
+ type: string
+ required:
+ - message
+ - type
+ type: object
+ v1.ErrorResponse:
+ properties:
+ error:
+ $ref: '#/components/schemas/v1.APIError'
+ required:
+ - error
+ type: object
+ securitySchemes:
+ NodeAuth:
+ type: http
+ scheme: bearer
+ NodeEnrollmentAuth:
+ type: http
+ scheme: bearer
+tags:
+ - name: Sandbox Node
+ description: 'Sandbox Node. Machine connection API: Route-specific node enrollment, node, daemon, or executor credential. Generated local machine contract. These connections reach Core directly, never through Web.'
diff --git a/apps/docs/openapi/sources.json b/apps/docs/openapi/sources.json
new file mode 100644
index 000000000..ddcaef8ac
--- /dev/null
+++ b/apps/docs/openapi/sources.json
@@ -0,0 +1,51 @@
+{
+ "sources": {
+ "contracts/agents-api/openapi.yaml": "a8896e8b67d78ed0608c7cc5636077ec33f4b4895a04bd6cfb8b038afe941e01",
+ "contracts/agents-api/core.openapi.yaml": "488a50fa308194224ee5ee879b7f6e38166d1b08dedc2bbc5bb39f38a844c87f",
+ "contracts/agents-api/runtime.openapi.yaml": "b3fb025b20a0bcf4635d60339479b2b07f6dbed80f51e72f40a63b31e42b5f4a"
+ },
+ "outputs": {
+ "content/docs/api-reference/agents.mdx": "1f7697959e9c52c9d24fbe70e116e807f49912b9a27ac638b86782e2f326af44",
+ "content/docs/api-reference/environments.mdx": "6a9e08a67479a745983c45b9711137617c13fdf7599a64533997b00a26ac7282",
+ "content/docs/api-reference/environment-templates.mdx": "c1daeaec159aea40b0aa5ed72b1e77ac1715471431b558d0d89e05bfe9ad8945",
+ "content/docs/api-reference/sessions.mdx": "cbebb6a1705b486fb228cbe0065df893227132f9b3884180a3871a3e8c78f311",
+ "content/docs/api-reference/artifacts.mdx": "8df8d008126b49b1df88f28a676c4b237ccffc45a470b35ec30eab2daf1c0f7f",
+ "content/docs/api-reference/events.mdx": "83d7f56a788d073e0b19f0bc9197f39ec364375b9cc3a7ec69b79b54bc71e3eb",
+ "content/docs/api-reference/items.mdx": "fa4d248ac5623beb3f00a4ebb5a6c4ff477865ebee351e0c543a970a7ec3654d",
+ "content/docs/api-reference/subagents.mdx": "90e2a1a9129111332e36715de2078ee595590c4c25c7b355962e4185bdeb5388",
+ "content/docs/api-reference/turns.mdx": "46ee9e43aae4ec85b3ee335939dc1e0a238cfda0fb996c6af4d2831bef3377f1",
+ "content/docs/api-reference/files.mdx": "531c835c66b194ab8876c7663b79e42f0090e12420ae32fafeda24e3143740f4",
+ "content/docs/api-reference/skills.mdx": "774ea05a7a8250fe5af5164388b98dafca99d56dc2eae686e8f628acb4e7c10b",
+ "content/docs/api-reference/vaults.mdx": "d80eade1c2f8100030866abbb0fb6b7a09c94250a859c97cdf1abce32fdfdf1e",
+ "content/docs/api-reference/credentials.mdx": "f129a51f74a3c7021c016600518e2350023f8d29571666ac5d64b7584cd7c7d0",
+ "openapi/public-api.yaml": "84ae8a33717c817950d757990018edd10a4d31d5a343c8172baa099d101c5a1f",
+ "content/docs/api-reference/meta.json": "56bf1dc145ccc8adde38c5f956f1a06f18ef18b646122250d01a294c516f8f83",
+ "content/docs/api-reference/index.mdx": "9fd6b1c54149874d2999235067c101326f9e8a2e707a70b6672c81eb3ebfe63f",
+ "content/docs/api-reference/core/core-administration.mdx": "a1261c7998017de3761ca90ca073a0b89a6c528ff37a634f3d8b986ee9540fc5",
+ "content/docs/api-reference/core/deployment-model-providers.mdx": "b2795fadcc05a1e7195b6d34c3f234cb29cc5e3f580d1521e31965453e654347",
+ "content/docs/api-reference/core/administrator-projects.mdx": "8fd687bb2a7119fa16e938d7c71710c2816afb9cf959eb6849ef5e6300c5419b",
+ "content/docs/api-reference/core/agents.mdx": "b6c089fbea4b07bc4249e7c7ca5699d47f1512a4fc2d8ee01cb26910ae541a84",
+ "content/docs/api-reference/core/environment-templates.mdx": "05907f35d7f02d66af9da8b1efaa3765badb4cdf7b94d0f6c3ab0413a2cfa1a9",
+ "content/docs/api-reference/core/executor-credentials.mdx": "f73656c850bf982b187ac5586669dabae1e4d3b714293a7bae090127b25e479b",
+ "content/docs/api-reference/core/files.mdx": "05ce1d2296dbb3fc76cf4cf98d24dff249114c73529335214cee97363229f384",
+ "content/docs/api-reference/core/write-audit.mdx": "1298590ab7037077e56f6ab8adb020277763f7a670be796d24142323b2f95411",
+ "content/docs/api-reference/core/sessions.mdx": "e8519b5d3b21296b0ea1edede3160766ad6a49197b92b201ee1a845542d99619",
+ "content/docs/api-reference/core/artifacts.mdx": "620fe72cd41d19b9694fe7080eb8935ef44292852400c7be1c03a893613ca89c",
+ "content/docs/api-reference/core/execution-configuration.mdx": "f6a9ed4cdb73e87f5f4ce16b4f6fa14b756d07c97f2741bd5cade34a3472c548",
+ "content/docs/api-reference/core/items.mdx": "7c7b006536d155d003097a504e9931e3dc4d9fd0426852573244453634729250",
+ "content/docs/api-reference/core/runtime-history.mdx": "eec6384c6fd5a2bf223ec7dc7ee19332440936e18fc67f782fe81b96e299f634",
+ "content/docs/api-reference/core/runtime-observations.mdx": "db3a238ba77d1fcb887835d51b6f3f8f2b6bdf9cc5e14ac6dd11074a1c75ed48",
+ "content/docs/api-reference/core/turns.mdx": "e6f7a154b3c70e1555417e44a7888295afb8f8bd475621300e1b2bbd2fc94933",
+ "content/docs/api-reference/core/skills.mdx": "ed899ade7b032b1732b3097d81419a8fdbb9ead0594fca9f2107da644b97031f",
+ "content/docs/api-reference/core/vaults.mdx": "12d0aac96cd4a3472ef4eaa287f7945f78862017956e79bfc3ed0ef8e772e78a",
+ "content/docs/api-reference/core/credentials.mdx": "27d586b9a8fd738b9947802f32c80458195370252886bc26abedccf2125ee981",
+ "content/docs/api-reference/core/sandbox-manager.mdx": "36f2895b9969d27be843d5cebf935bf14ca9400ad076e56074a25905e3df5d1b",
+ "openapi/core-api.yaml": "9872f80cec2661c8539b6652a294c16c668f581399afd20f76b198279faae9ed",
+ "content/docs/api-reference/core/meta.json": "588b163ed2de2100b0f8376688274f80698a6bf6ac037eef56d17c93fa8a5062",
+ "content/docs/api-reference/core/index.mdx": "ddf0a8d87a6c21761beac703a32f2a40486a1c74abb341bd726f40c44a866405",
+ "content/docs/api-reference/machine/sandbox-node.mdx": "5b367a08d43cea82686345d2ba1c75cc90627a7b2970fe42a0df9d77fb3e1e80",
+ "openapi/runtime-api.yaml": "bca127037ffb1e132e7f26bffada5e3da70f4b2534926a04e1e65029d885afe9",
+ "content/docs/api-reference/machine/meta.json": "12a1a6534c1cae32918282da2110e4b64c43f17abb868486b5eff50ceb0b01c1",
+ "content/docs/api-reference/machine/index.mdx": "792c1ce86a6653943021eb5cd03a2c6b5c604f7f012da5ef06f3ffa027b12973"
+ }
+}
diff --git a/apps/docs/package.json b/apps/docs/package.json
new file mode 100644
index 000000000..d736c306b
--- /dev/null
+++ b/apps/docs/package.json
@@ -0,0 +1,46 @@
+{
+ "name": "@oac/docs",
+ "private": true,
+ "version": "0.1.0",
+ "type": "module",
+ "scripts": {
+ "postinstall": "fumadocs-mdx",
+ "dev": "next dev --port 4000",
+ "build": "fumadocs-mdx && next build && node scripts/verify-prerender.mjs",
+ "api:generate": "node scripts/generate-api-reference.mjs",
+ "verify": "pnpm verify:contracts && pnpm verify:routes && pnpm verify:copy && pnpm verify:docs && pnpm verify:links",
+ "verify:contracts": "node scripts/verify-contract-freshness.mjs",
+ "verify:routes": "node scripts/check-python.mjs routes",
+ "verify:copy": "node scripts/verify-api-copy.mjs",
+ "verify:docs": "node scripts/verify-docs-facts.mjs",
+ "verify:links": "node scripts/verify-links.mjs",
+ "check:prerender": "node scripts/verify-prerender.mjs",
+ "check:site": "node scripts/check-site.mjs",
+ "start": "next start",
+ "typecheck": "fumadocs-mdx && tsc --noEmit",
+ "test": "node --test && node scripts/check-python.mjs tests",
+ "guides:generate": "node scripts/generate-guides.mjs",
+ "generate": "pnpm guides:generate && pnpm api:generate",
+ "check:browser": "node scripts/check-browser.mjs"
+ },
+ "dependencies": {
+ "@scalar/openapi-upgrader": "0.1.1",
+ "fumadocs-core": "15.8.5",
+ "fumadocs-mdx": "^12.0.3",
+ "fumadocs-openapi": "9.5.1",
+ "fumadocs-ui": "15.8.5",
+ "js-yaml": "^4.1.0",
+ "next": "^15.5.16",
+ "next-themes": "^0.4.6",
+ "react": "^19.2.7",
+ "react-dom": "^19.2.7"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.3.2",
+ "@types/react": "^19.2.17",
+ "@types/react-dom": "^19.1.0",
+ "@types/node": "^22.0.0",
+ "tailwindcss": "^4.3.2",
+ "typescript": "^5.8.3"
+ }
+}
diff --git a/apps/docs/postcss.config.mjs b/apps/docs/postcss.config.mjs
new file mode 100644
index 000000000..2f8795a93
--- /dev/null
+++ b/apps/docs/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ "@tailwindcss/postcss": {},
+ },
+}
+
+export default config
diff --git a/apps/docs/public/images/architecture.svg b/apps/docs/public/images/architecture.svg
new file mode 100644
index 000000000..9f8abb1de
--- /dev/null
+++ b/apps/docs/public/images/architecture.svg
@@ -0,0 +1,10 @@
+
+OpenAgentCore API and execution boundaries Applications call Core with Project API keys. Administrator browsers sign into Web; Web holds the Core key. Nodes and Runtime daemons connect directly to Core using scoped machine credentials. Core owns execution and its PostgreSQL state.
+
+
+
+Application / SDK Administrator browser Web server Core Execution owner PostgreSQL Node / Runtime / native harness
+
+/v1 · Project API key Session /core/v1 Core key /api/v1 Scoped machine credentials
+Application API and administration API are separate. Machine connections never pass through Web.
+
diff --git a/apps/docs/public/images/source/apps/web/public/onboarding/monitor-en.webp b/apps/docs/public/images/source/apps/web/public/onboarding/monitor-en.webp
new file mode 100644
index 000000000..70aab640b
Binary files /dev/null and b/apps/docs/public/images/source/apps/web/public/onboarding/monitor-en.webp differ
diff --git a/apps/docs/scripts/check-browser.mjs b/apps/docs/scripts/check-browser.mjs
new file mode 100644
index 000000000..bf1781696
--- /dev/null
+++ b/apps/docs/scripts/check-browser.mjs
@@ -0,0 +1,44 @@
+#!/usr/bin/env node
+// Local documentation acceptance only: never enter credentials or call Core.
+const { chromium } = await import(process.env.DOCS_PLAYWRIGHT_MODULE ?? '@playwright/test')
+import assert from 'node:assert/strict'
+import fs from 'node:fs'
+import path from 'node:path'
+const origin = process.argv[2] ?? 'http://127.0.0.1:4275'
+assert.ok(['127.0.0.1', 'localhost'].includes(new URL(origin).hostname), 'Use a task-owned local documentation server')
+const browser = await chromium.launch({ headless: true, channel: 'chromium', ...(process.env.DOCS_BROWSER_EXECUTABLE ? { executablePath: process.env.DOCS_BROWSER_EXECUTABLE } : {}) })
+const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } })
+const failures = []
+const unexpected = []
+context.on('request', request => {
+ if (!request.url().startsWith(origin + '/') && request.url() !== origin) unexpected.push(request.url())
+})
+const page = await context.newPage()
+page.on('pageerror', error => failures.push(error.message))
+const screenshots = process.env.DOCS_SCREENSHOT_DIR
+if (screenshots) fs.mkdirSync(screenshots, { recursive: true })
+try {
+ for (const route of ['/', '/install', '/configure', '/console', '/execution-model', '/api-reference/agents', '/api-reference/core/sandbox-manager', '/api-reference/machine/sandbox-node', '/zh/install']) {
+ const response = await page.goto(origin + route, { waitUntil: 'networkidle' })
+ assert.equal(response.status(), 200, route)
+ assert.ok(await page.locator('h1').count(), 'Missing page title: ' + route)
+ assert.ok((await page.locator('body').innerText()).includes('OpenAgentCore Docs'), 'Wrong visible wordmark: ' + route)
+ assert.ok((await page.title()).includes('OpenAgentCore Docs'), 'Wrong page metadata: ' + route)
+ if (route.includes('/api-reference/')) {
+ assert.equal(await page.locator('input, form, textarea').count(), 0, 'Reference exposes request controls: ' + route)
+ assert.ok((await page.locator('body').innerText()).includes('Authorization'), 'Missing credential documentation: ' + route)
+ }
+ if (screenshots && ['/', '/console', '/execution-model', '/api-reference/core/sandbox-manager', '/zh/install'].includes(route)) {
+ await page.screenshot({ path: path.join(screenshots, (route.replaceAll('/', '-') || 'home') + '.png'), fullPage: false })
+ }
+ }
+ await page.setViewportSize({ width: 390, height: 844 })
+ await page.goto(origin + '/install', { waitUntil: 'networkidle' })
+ assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth + 1), 'Mobile page overflows viewport')
+ assert.deepEqual(failures, [], 'Browser runtime errors')
+ assert.deepEqual(unexpected, [], 'Documentation made external requests')
+ console.log('Nine desktop routes, both language paths, mobile layout and read-only API controls passed; no external requests.')
+} finally {
+ await context.close()
+ await browser.close()
+}
diff --git a/apps/docs/scripts/check-python.mjs b/apps/docs/scripts/check-python.mjs
new file mode 100644
index 000000000..96bb15afd
--- /dev/null
+++ b/apps/docs/scripts/check-python.mjs
@@ -0,0 +1,20 @@
+#!/usr/bin/env node
+// Decode YAML with the locked Node dependency; Python checks need only stdlib.
+import fs from 'node:fs'
+import { spawnSync } from 'node:child_process'
+import yaml from 'js-yaml'
+import { appRoot, surfaces, sourcePath } from './contracts.mjs'
+
+const mode = process.argv[2]
+if (!['routes', 'tests'].includes(mode)) throw new Error('Expected routes or tests')
+const args = mode === 'routes'
+ ? ['scripts/verify-contract-routes.py']
+ : ['-m', 'unittest', 'discover', '-s', 'scripts', '-p', 'test_*.py']
+const input = mode === 'routes' ? JSON.stringify(surfaces.map(surface => ({
+ label: surface.id, document: yaml.load(fs.readFileSync(sourcePath(surface), 'utf8')),
+}))) : undefined
+const result = spawnSync(process.env.OAC_TEST_OFFICIAL_SDK_PYTHON || 'python3', ['-S', ...args], {
+ cwd: appRoot, input, stdio: ['pipe', 'inherit', 'inherit'],
+})
+if (result.error) throw result.error
+process.exit(result.status ?? 1)
diff --git a/apps/docs/scripts/check-python.test.mjs b/apps/docs/scripts/check-python.test.mjs
new file mode 100644
index 000000000..e6bce3365
--- /dev/null
+++ b/apps/docs/scripts/check-python.test.mjs
@@ -0,0 +1,30 @@
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import fs from 'node:fs'
+import os from 'node:os'
+import path from 'node:path'
+import { spawnSync } from 'node:child_process'
+import { appRoot } from './contracts.mjs'
+
+test('route gate and Python regressions honor the selected interpreter without site packages', (t) => {
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'oac-docs-python-'))
+ t.after(() => fs.rmSync(directory, { recursive: true, force: true }))
+ const selected = path.join(directory, 'selected-python')
+ const log = path.join(directory, 'arguments.jsonl')
+ const python = spawnSync(process.env.OAC_TEST_OFFICIAL_SDK_PYTHON || 'python3', ['-S', '-c', 'import sys; print(sys.executable)'], { encoding: 'utf8' })
+ assert.equal(python.status, 0, python.stderr)
+ const executable = python.stdout.trim()
+ // This task-owned wrapper records selection, then delegates unchanged to Python.
+ fs.writeFileSync(selected, `#!${executable}\nimport json, os, sys\nwith open(${JSON.stringify(log)}, "a") as output:\n output.write(json.dumps(sys.argv[1:]) + "\\n")\nos.execv(${JSON.stringify(executable)}, [${JSON.stringify(executable)}, *sys.argv[1:]])\n`, { mode: 0o700 })
+ for (const mode of ['routes', 'tests']) {
+ const result = spawnSync(process.execPath, ['scripts/check-python.mjs', mode], {
+ cwd: appRoot, env: { ...process.env, OAC_TEST_OFFICIAL_SDK_PYTHON: selected }, encoding: 'utf8',
+ })
+ assert.equal(result.status, 0, result.stdout + result.stderr)
+ if (mode === 'routes') assert.match(result.stdout, /Contract operations:\s+130/)
+ else assert.match(result.stderr, /Ran 2 tests/)
+ }
+ const invocations = fs.readFileSync(log, 'utf8').trim().split('\n').map(line => JSON.parse(line))
+ assert.equal(invocations.length, 2)
+ assert.ok(invocations.every(args => args[0] === '-S'), 'Checks must not depend on ambient site packages')
+})
diff --git a/apps/docs/scripts/check-site.mjs b/apps/docs/scripts/check-site.mjs
new file mode 100644
index 000000000..310b01a42
--- /dev/null
+++ b/apps/docs/scripts/check-site.mjs
@@ -0,0 +1,81 @@
+#!/usr/bin/env node
+// Smoke-checks every documentation page against a running server.
+//
+// A page that exists on disk but 404s in the browser is the failure this catches:
+// a missing layout, a bad slug, or a locale route that was never generated.
+//
+// Both locales are probed for every page, including pages that have no translated
+// twin. That is deliberate: Fumadocs falls back to the default language for an
+// untranslated page, so `/zh/api-reference/files` is a real, reachable URL even
+// though only `api-reference/files.mdx` exists on disk. The Chinese manual links
+// into those pages freely, so both forms have to resolve.
+//
+// Not part of `pnpm verify`, because it needs a built site that is already
+// serving. Usage, after `pnpm build && pnpm start`:
+//
+// node scripts/check-site.mjs [origin] # default http://127.0.0.1:4001
+
+import fs from "node:fs"
+import path from "node:path"
+import { fileURLToPath } from "node:url"
+
+const here = path.dirname(fileURLToPath(import.meta.url))
+const contentRoot = path.resolve(here, "../content/docs")
+const origin = (process.argv[2] ?? "http://127.0.0.1:4001").replace(/\/+$/, "")
+
+function walk(dir) {
+ const out = []
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const full = path.join(dir, entry.name)
+ if (entry.isDirectory()) out.push(...walk(full))
+ else if (entry.name.endsWith(".mdx")) out.push(full)
+ }
+ return out
+}
+
+// content/docs/index.mdx -> /
+// content/docs/foo.mdx -> /foo
+// content/docs/foo.zh.mdx -> /foo (the zh twin of the same route)
+// content/docs/a/index.mdx -> /a
+// content/docs/a/b.mdx -> /a/b
+function slugFor(file) {
+ let rel = path.relative(contentRoot, file).split(path.sep).join("/")
+ rel = rel.replace(/\.zh\.mdx$/, "").replace(/\.mdx$/, "").replace(/(^|\/)index$/, "")
+ return rel.replace(/\/$/, "")
+}
+
+const slugs = [...new Set(walk(contentRoot).map(slugFor))].sort()
+const routes = []
+for (const slug of slugs) {
+ routes.push(slug ? `/${slug}` : "/")
+ routes.push(slug ? `/zh/${slug}` : "/zh")
+}
+
+const failures = []
+let checked = 0
+const concurrency = 8
+
+async function check(route) {
+ try {
+ const response = await fetch(`${origin}${route}`, { redirect: "manual" })
+ checked += 1
+ if (response.status !== 200) failures.push(`${response.status} ${route}`)
+ } catch (error) {
+ checked += 1
+ failures.push(`ERR ${route} (${error.message})`)
+ }
+}
+
+const queue = [...routes]
+await Promise.all(
+ Array.from({ length: concurrency }, async () => {
+ while (queue.length > 0) await check(queue.shift())
+ }),
+)
+
+if (failures.length > 0) {
+ console.error(`\n${failures.length} of ${checked} routes did not return 200:`)
+ for (const line of failures.sort()) console.error(` ${line}`)
+ process.exit(1)
+}
+console.log(`${checked} routes returned 200 at ${origin} (${slugs.length} pages, both locales).`)
\ No newline at end of file
diff --git a/apps/docs/scripts/contracts.mjs b/apps/docs/scripts/contracts.mjs
new file mode 100644
index 000000000..e8b6356e3
--- /dev/null
+++ b/apps/docs/scripts/contracts.mjs
@@ -0,0 +1,91 @@
+// The public schema is constrained by the pinned upstream route/field snapshots.
+// Core and machine schemas are local generated contracts, with separate authority.
+import { upgrade } from '@scalar/openapi-upgrader'
+import yaml from 'js-yaml'
+import fs from 'node:fs'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+export const appRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
+export const repoRoot = path.resolve(appRoot, '../..')
+export const methods = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
+export const surfaces = [
+ { id: 'public-api', file: 'openapi.yaml', directory: '', title: 'Application API', prefix: '/v1', credential: 'Project API key', authority: 'Public schema constrained by the pinned OpenAI Agents API baseline; documented x_agents_core fields remain Core extensions.' },
+ { id: 'core-api', file: 'core.openapi.yaml', directory: '/core', title: 'Core administration API', prefix: '/core/v1', credential: 'Core key held by Web’s server or an operator script', authority: 'Generated local management contract. This is not part of the public OpenAI API.' },
+ { id: 'runtime-api', file: 'runtime.openapi.yaml', directory: '/machine', title: 'Machine connection API', prefix: '/api/v1', credential: 'Route-specific node enrollment, node, daemon, or executor credential', authority: 'Generated local machine contract. These connections reach Core directly, never through Web.' },
+]
+export function sourcePath(surface) { return path.join(repoRoot, 'contracts/agents-api', surface.file) }
+export function normalise(surface, source = yaml.load(fs.readFileSync(sourcePath(surface), 'utf8'))) {
+ if (source.swagger !== '2.0' && !/^3\./.test(source.openapi ?? '')) throw new Error('Unsupported contract format: ' + surface.file)
+ const base = source.basePath === '/' ? '' : (source.basePath ?? '')
+ const document = source.swagger === '2.0' ? upgrade(structuredClone(source), '3.0') : structuredClone(source)
+ projectSchemas(document)
+ document.servers = [{ url: (surface.id === 'core-api' ? 'https://core-direct.example.com' : 'https://core.example.com') + base, description: surface.id === 'core-api' ? 'Operator-script placeholder: replace with the Core loopback origin, not the public Web entry.' : 'Documentation placeholder; substitute your Core public origin.' }]
+ for (const scheme of Object.values(document.components?.securitySchemes ?? {})) {
+ if (scheme.type === 'apiKey' && scheme.in === 'header' && scheme.name?.toLowerCase() === 'authorization') {
+ delete scheme.in; delete scheme.name; scheme.type = 'http'; scheme.scheme = 'bearer'
+ }
+ }
+ const tags = new Set()
+ for (const [route, item] of Object.entries(document.paths ?? {})) {
+ for (const method of methods) {
+ const operation = item[method]
+ if (!operation) continue
+ if (!(base + route).startsWith(surface.prefix + '/')) throw new Error('Route crosses credential boundary: ' + surface.id + ' ' + route)
+ for (const tag of operation.tags ?? ['Operations']) tags.add(tag)
+ }
+ }
+ document.tags = [...tags].map(name => ({ name, description: `${name}. ${surface.title}: ${surface.credential}. ${surface.authority}` }))
+ return document
+}
+
+// OpenAPI 3.1 can express nullable references and compositions without relying on
+// Swagger extensions that the reference renderer ignores. Visit schema locations
+// only: example payloads may themselves contain keys named type or x-nullable.
+function projectSchemas(document) {
+ const from30 = document.openapi.startsWith('3.0')
+ function schema(value) {
+ if (!value || typeof value !== 'object') return value
+ for (const key of ['properties', 'patternProperties', '$defs', 'dependentSchemas']) {
+ for (const [name, child] of Object.entries(value[key] ?? {})) value[key][name] = schema(child)
+ }
+ for (const key of ['items', 'additionalProperties', 'not', 'contains', 'propertyNames', 'if', 'then', 'else', 'unevaluatedProperties', 'unevaluatedItems']) {
+ if (key in value) value[key] = schema(value[key])
+ }
+ for (const key of ['allOf', 'anyOf', 'oneOf', 'prefixItems']) {
+ if (Array.isArray(value[key])) value[key] = value[key].map(schema)
+ }
+ if (from30) {
+ for (const bound of ['Minimum', 'Maximum']) {
+ const key = 'exclusive' + bound
+ if (value[key] === true) value[key] = value[bound.toLowerCase()]
+ else if (value[key] === false) delete value[key]
+ }
+ }
+ const nullable = value['x-nullable'] === true || (from30 && value.nullable === true)
+ delete value['x-nullable']
+ if (from30) delete value.nullable
+ if (!nullable) return value
+ // A union also permits null for enum, $ref and allOf schemas; adding only
+ // type: [..., 'null'] would leave enum/composition constraints excluding null.
+ const result = { anyOf: [value, { type: 'null' }] }
+ for (const key of ['description', 'readOnly', 'writeOnly', 'deprecated', 'example', 'examples', 'default']) {
+ if (key in value) { result[key] = value[key]; delete value[key] }
+ }
+ return result
+ }
+ function visit(value) {
+ if (!value || typeof value !== 'object') return
+ for (const [key, child] of Object.entries(value)) {
+ if (key === 'schema') value[key] = schema(child)
+ else if (!['example', 'examples', 'default', 'enum'].includes(key) && !key.startsWith('x-')) visit(child)
+ }
+ }
+ for (const [name, value] of Object.entries(document.components?.schemas ?? {})) {
+ document.components.schemas[name] = schema(value)
+ }
+ // Components schemas were already handled and must not be interpreted as
+ // document objects (a property can legally be named "schema").
+ const { schemas, ...otherComponents } = document.components ?? {}
+ visit({ ...document, components: otherComponents })
+ if (from30) document.openapi = '3.1.0'
+}
diff --git a/apps/docs/scripts/contracts.test.mjs b/apps/docs/scripts/contracts.test.mjs
new file mode 100644
index 000000000..bf191d9af
--- /dev/null
+++ b/apps/docs/scripts/contracts.test.mjs
@@ -0,0 +1,85 @@
+import test from 'node:test'
+import assert from 'node:assert/strict'
+import fs from 'node:fs'
+import os from 'node:os'
+import path from 'node:path'
+import yaml from 'js-yaml'
+import { createOpenAPI } from 'fumadocs-openapi/server'
+import { schemaToString } from '../node_modules/fumadocs-openapi/dist/utils/schema-to-string.js'
+import { appRoot, normalise, surfaces } from './contracts.mjs'
+
+test('public rendering preserves operation prose, schemas and project authentication', () => {
+ const fixture = { swagger: '2.0', info: { title: 'Example', version: '1' }, basePath: '/v1', paths: {
+ '/agents': { get: { tags: ['Agents'], description: 'A constraint that must remain visible.', security: [{ ProjectKey: [] }], responses: { 200: { description: 'Result', schema: { $ref: '#/definitions/Agent' } } } } },
+ }, definitions: { Agent: { type: 'object', properties: { id: { type: 'string' } } } }, securityDefinitions: { ProjectKey: { type: 'apiKey', in: 'header', name: 'Authorization' } } }
+ const rendered = normalise(surfaces[0], fixture)
+ assert.equal(rendered.paths['/agents'].get.description, fixture.paths['/agents'].get.description)
+ assert.deepEqual(rendered.paths['/agents'].get.security, [{ ProjectKey: [] }])
+ assert.equal(rendered.components.schemas.Agent.properties.id.type, 'string')
+ assert.equal(rendered.components.securitySchemes.ProjectKey.scheme, 'bearer')
+ assert.equal(rendered.servers[0].url, 'https://core.example.com/v1')
+})
+
+test('OpenAPI 3 is accepted without guessing its authority or changing its schemas', () => {
+ const fixture = { openapi: '3.0.3', info: { title: 'Example', version: '1' }, paths: {
+ '/core/v1/example': { get: { tags: ['Example'], responses: { 200: { description: 'OK' } } } },
+ }, components: { schemas: { Payload: { type: 'string', enum: ['exact'] } } } }
+ assert.deepEqual(normalise(surfaces[1], fixture).components.schemas, fixture.components.schemas)
+ assert.throws(() => normalise(surfaces[0], fixture), /credential boundary/)
+ assert.throws(() => normalise(surfaces[1], { ...fixture, openapi: '9.0' }), /Unsupported/)
+})
+
+test('actual Fumadocs renderer preserves nullable values, references and compositions', async (t) => {
+ const directory = fs.mkdtempSync(path.join(os.tmpdir(), 'oac-docs-nullable-'))
+ t.after(() => fs.rmSync(directory, { recursive: true, force: true }))
+ const example = { schema: { type: 'string', 'x-nullable': true } }
+ const fixture = { swagger: '2.0', info: { title: 'Example', version: '1' }, paths: {}, definitions: {
+ Text: { type: 'string' },
+ Payload: { type: 'object', properties: {
+ text: { type: 'string', 'x-nullable': true, description: 'May be null.' },
+ reference: { $ref: '#/definitions/Text', 'x-nullable': true },
+ composed: { allOf: [{ $ref: '#/definitions/Text' }], 'x-nullable': true },
+ choice: { type: 'string', enum: ['only'], 'x-nullable': true },
+ titled: { type: 'string', title: 'Label', 'x-nullable': true },
+ requiredText: { type: 'string' },
+ sample: { type: 'object', example },
+ } },
+ } }
+ const original = structuredClone(fixture)
+ const projected = normalise(surfaces[0], fixture)
+ assert.equal(projected.openapi, '3.1.0')
+ assert.deepEqual(fixture, original, 'Canonical source must not be mutated')
+ assert.deepEqual(projected.components.schemas.Payload.properties.sample.example, example)
+ assert.deepEqual(projected.components.schemas.Payload.properties.choice.anyOf, [
+ { type: 'string', enum: ['only'] }, { type: 'null' },
+ ], 'Null must be allowed outside the enum constraint')
+ const file = path.join(directory, 'fixture.yaml')
+ fs.writeFileSync(file, yaml.dump(projected))
+ const docs = await createOpenAPI({ input: [file], disablePlayground: true }).getSchemas()
+ const properties = docs[file].dereferenced.components.schemas.Payload.properties
+ for (const [field, expected] of Object.entries({ text: 'string | null', reference: 'Text | null', composed: 'Text | null', choice: 'string | null', titled: 'Label | null', requiredText: 'string' })) {
+ assert.equal(schemaToString(properties[field], docs[file]), expected, field)
+ }
+ assert.equal(properties.text.description, 'May be null.')
+})
+
+test('actual generated public reference renders the pinned nullable error fields', async () => {
+ const file = path.join(appRoot, 'openapi/public-api.yaml')
+ const docs = await createOpenAPI({ input: [file], disablePlayground: true }).getSchemas()
+ const schemas = docs[file].dereferenced.components.schemas
+ for (const [name, field] of [['v1.Session', 'error'], ['v1.APIError', 'code'], ['v1.APIError', 'param']]) {
+ assert.equal(schemaToString(schemas[name].properties[field], docs[file]), 'string | null', `${name}.${field}`)
+ }
+})
+
+test('OpenAPI 3.0 schema conversion preserves bounds and binary annotations', () => {
+ const fixture = { openapi: '3.0.3', info: { title: 'Example', version: '1' }, paths: {}, components: { schemas: {
+ Number: { type: 'number', minimum: 1, exclusiveMinimum: true, maximum: 10, exclusiveMaximum: false, nullable: true },
+ File: { type: 'string', format: 'binary' },
+ } } }
+ const schemas = normalise(surfaces[0], fixture).components.schemas
+ assert.deepEqual(schemas.Number.anyOf, [
+ { type: 'number', minimum: 1, exclusiveMinimum: 1, maximum: 10 }, { type: 'null' },
+ ])
+ assert.deepEqual(schemas.File, fixture.components.schemas.File)
+})
diff --git a/apps/docs/scripts/generate-api-reference.mjs b/apps/docs/scripts/generate-api-reference.mjs
new file mode 100644
index 000000000..b9b706b46
--- /dev/null
+++ b/apps/docs/scripts/generate-api-reference.mjs
@@ -0,0 +1,38 @@
+#!/usr/bin/env node
+// Render the actual contracts without copying prose from superseded API surfaces.
+import { generateFilesOnly } from 'fumadocs-openapi'
+import { createOpenAPI } from 'fumadocs-openapi/server'
+import yaml from 'js-yaml'
+import fs from 'node:fs'
+import path from 'node:path'
+import crypto from 'node:crypto'
+import { appRoot, repoRoot, surfaces, sourcePath, normalise } from './contracts.mjs'
+const root = path.join(appRoot, 'content/docs/api-reference')
+fs.rmSync(root, { recursive: true, force: true })
+fs.mkdirSync(root, { recursive: true })
+fs.rmSync(path.join(appRoot, 'openapi'), { recursive: true, force: true })
+fs.mkdirSync(path.join(appRoot, 'openapi'))
+const record = { sources: {}, outputs: {} }
+const digest = file => crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex')
+for (const surface of surfaces) {
+ const output = path.join(appRoot, 'openapi', surface.id + '.yaml')
+ fs.writeFileSync(output, '# Generated documentation projection; do not edit.\n' + yaml.dump(normalise(surface), { lineWidth: -1, noRefs: true }))
+ const files = await generateFilesOnly({ input: createOpenAPI({ input: [output] }), per: 'tag' })
+ const directory = root + surface.directory
+ fs.mkdirSync(directory, { recursive: true })
+ for (const file of files) {
+ const target = path.join(directory, file.path)
+ fs.writeFileSync(target, file.content.replace(/document=\{[^}]*\}/, `document={${JSON.stringify(surface.id)}}`))
+ record.outputs[path.relative(appRoot, target)] = digest(target)
+ }
+ const pages = ['index', ...files.map(file => file.path.replace(/\.mdx$/, ''))]
+ if (!surface.directory) pages.push('core', 'machine')
+ fs.writeFileSync(path.join(directory, 'meta.json'), JSON.stringify({ title: surface.title, pages }, null, 2) + '\n')
+ const body = `---\ntitle: ${surface.title}\ndescription: ${surface.prefix} — ${surface.credential}.\n---\n\n${surface.authority}\n\n**Credential:** ${surface.credential}. Examples use reserved \`example.com\` origins. This reference does not send requests or collect credentials.\n\n${surface.id === 'runtime-api' ? 'This schema covers node configuration, enrollment and identity. Daemon WebSockets and executor connection details are described in the [machine overview](/public-api#machine-connection-api).\n\n' : ''}${surface.id === 'core-api' ? 'Operator scripts use Core’s loopback port. The public entry routes management through Web, which requires its signed-in session and supplies the Core key on the server.\n\n' : ''}[API namespaces and credentials](/public-api) · [Application reference](/api-reference) · [Administration reference](/api-reference/core) · [Machine reference](/api-reference/machine)\n\n` + files.map(file => `- [${file.path.replace(/\.mdx$/, '')}](/api-reference${surface.directory}/${file.path.replace(/\.mdx$/, '')})`).join('\n') + '\n'
+ fs.writeFileSync(path.join(directory, 'index.mdx'), body)
+ record.sources[path.relative(repoRoot, sourcePath(surface))] = digest(sourcePath(surface))
+ record.outputs[path.relative(appRoot, output)] = digest(output)
+ for (const name of ['meta.json', 'index.mdx']) record.outputs[path.relative(appRoot, path.join(directory, name))] = digest(path.join(directory, name))
+ console.log(surface.id + ': ' + files.length + ' tag pages')
+}
+fs.writeFileSync(path.join(appRoot, 'openapi/sources.json'), JSON.stringify(record, null, 2) + '\n')
diff --git a/apps/docs/scripts/generate-guides.mjs b/apps/docs/scripts/generate-guides.mjs
new file mode 100644
index 000000000..a40b69592
--- /dev/null
+++ b/apps/docs/scripts/generate-guides.mjs
@@ -0,0 +1,57 @@
+#!/usr/bin/env node
+// The site renders current repository guides instead of maintaining a second manual.
+import fs from 'node:fs'
+import path from 'node:path'
+import crypto from 'node:crypto'
+import { fileURLToPath } from 'node:url'
+const app = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
+const repo = path.resolve(app, '../..')
+const guides = JSON.parse(fs.readFileSync(path.join(app, 'scripts/guides.json'), 'utf8'))
+const routes = new Map(guides.map(g => [g.source, g.slug === 'index' ? '/' : '/' + g.slug]))
+const sourceRevision = '75bf4484ba957344ae374354858bbdfe104f5c4f'
+const sourceURL = relative => `https://github.com/MiniMax-AI/parsar-core/blob/${sourceRevision}/${relative}`
+const digest = text => crypto.createHash('sha256').update(text).digest('hex')
+function mdx(text) {
+ let fence = false
+ return text.split('\n').map(line => {
+ if (/^\s*(```|~~~)/.test(line)) { fence = !fence; return line }
+ if (fence) return line
+ return line.split(/(`+[^`]*`+)/g).map((part, i) => i % 2 ? part : part.replaceAll('{', '{').replaceAll('}', '}').replaceAll('<', '<')).join('')
+ }).join('\n')
+}
+const record = { revision: sourceRevision, sources: {}, outputs: {} }
+// Only this generated asset directory is cleared; authored diagrams remain separate.
+fs.rmSync(path.join(app, 'public/images/source'), { recursive: true, force: true })
+for (const guide of guides) {
+ const original = fs.readFileSync(path.join(repo, guide.source), 'utf8')
+ let body = original.replace(/^# [^\n]*\n/, '').replace(//g, '').replace(/^```caddyfile$/gm, '```text')
+ body = body.replace(/(!?\[[^\]]*\])\(([^\s)]+)\)/g, (full, label, target) => {
+ if (/^(?:[a-z]+:|#|\/)/i.test(target)) return full
+ const [file, anchor] = target.split('#')
+ const relative = path.posix.normalize(path.posix.join(path.posix.dirname(guide.source), file))
+ if (!fs.existsSync(path.join(repo, relative))) throw new Error('Missing guide link: ' + guide.source + ' -> ' + target)
+ let url = routes.get(relative) ?? sourceURL(relative)
+ if (label.startsWith('!')) {
+ const imageSource = guide.images?.[relative] ?? relative
+ const asset = 'public/images/source/' + imageSource
+ const bytes = fs.readFileSync(path.join(repo, imageSource))
+ fs.mkdirSync(path.dirname(path.join(app, asset)), { recursive: true })
+ fs.writeFileSync(path.join(app, asset), bytes)
+ record.sources[imageSource] = digest(bytes)
+ record.outputs[asset] = digest(bytes)
+ url = '/' + asset.slice('public/'.length)
+ }
+ return label + '(' + url + (anchor ? '#' + anchor : '') + ')'
+ })
+ const en = `---\ntitle: ${JSON.stringify(guide.title)}\ndescription: ${JSON.stringify(guide.description)}\n---\n\n` + (guide.slug === 'execution-model' ? '\n\n' : '') + mdx(body.trim()) + `\n\n[Repository source](${sourceURL(guide.source)})\n`
+ const zh = `---\ntitle: ${JSON.stringify(guide.title + ' · 中文导读')}\ndescription: "当前版本的中文导读;完整操作步骤见英文正文。"\n---\n\n${guide.zh.replaceAll('<', '<')}\n\n此页为中文导读,完整且与当前源码同步的步骤请阅读[英文正文](/en/${guide.slug === 'index' ? '' : guide.slug})。\n\n[安装](/install) · [配置](/configure) · [API 与凭据](/public-api) · [节点](/hosted-providers) · [应用示例](/quickstart)\n`
+ for (const [suffix, text] of [['', en], ['.zh', zh]]) {
+ const relative = `content/docs/${guide.slug}${suffix}.mdx`
+ fs.writeFileSync(path.join(app, relative), text)
+ record.outputs[relative] = digest(text)
+ }
+ record.sources[guide.source] = digest(original)
+}
+record.sources['apps/docs/scripts/guides.json'] = digest(fs.readFileSync(path.join(app, 'scripts/guides.json')))
+fs.writeFileSync(path.join(app, 'content/guide-sources.json'), JSON.stringify(record, null, 2) + '\n')
+console.log('Rendered ' + guides.length + ' current guides and Chinese reading notes.')
diff --git a/apps/docs/scripts/guides.json b/apps/docs/scripts/guides.json
new file mode 100644
index 000000000..a842dfc8a
--- /dev/null
+++ b/apps/docs/scripts/guides.json
@@ -0,0 +1,124 @@
+[
+ {
+ "slug": "index",
+ "source": "docs/getting-started/README.md",
+ "title": "OpenAgentCore documentation",
+ "description": "Install Core and Web, create a Project API key, and add execution capacity.",
+ "zh": "安装 Core 与 Web,用 Core key 登录 Web,创建项目并签发应用 API key,然后添加执行节点。应用使用 /v1;Web 的服务端使用 /core/v1;机器连接使用 /api/v1。"
+ },
+ {
+ "slug": "concepts",
+ "source": "docs/design-principles.md",
+ "title": "Concepts and ownership",
+ "description": "Projects, credentials and the boundary between applications, administration and execution.",
+ "zh": "Project 拥有应用资源和执行身份。同一项目的 API keys 共享资源;Core key 只用于管理。Core 负责执行事实,Web 不复制执行所有权。"
+ },
+ {
+ "slug": "execution-model",
+ "source": "docs/web/architecture.md",
+ "title": "Execution and API architecture",
+ "description": "Applications call the public API; the administrator browser calls Web; Runtime connects to Core.",
+ "zh": "应用直接用 Project API key 调用 Core 的 /v1。管理员浏览器通过 Web 会话操作管理界面,Web 服务端持有 Core key。节点和 Runtime 用专属凭据直接连接 Core。"
+ },
+ {
+ "slug": "install",
+ "source": "docs/getting-started/install.md",
+ "title": "Install Core and Web",
+ "description": "Current distribution, host prerequisites, HTTPS, first sign-in and installation options.",
+ "zh": "安装器默认在 ~/.oac/core 安装 Core、Web 和专用 PostgreSQL,初始没有执行节点。先确定公网 HTTPS 地址,按当前版本的校验步骤下载并安装,再用 Core key 登录 Web。"
+ },
+ {
+ "slug": "configure",
+ "source": "docs/configuration.md",
+ "title": "Configuration reference",
+ "description": "The operator config.json, generated files and database-owned runtime settings.",
+ "zh": "安装配置的唯一编辑入口是 config.json;运行 oac apply 应用变更。generated 目录由安装器生成。运行时后端、节点和默认模型由 Web 管理并保存在 PostgreSQL 中。"
+ },
+ {
+ "slug": "bootstrap-projects-keys",
+ "source": "docs/web/core-connection.md",
+ "title": "Sign in and issue application keys",
+ "description": "Keep the Core key on the management side and issue Project API keys through Web.",
+ "zh": "使用 Core key 登录 Web。应用开发者在项目中获取 Project API key,设置 OPENAI_BASE_URL 与 OPENAI_API_KEY。Core key 不能用来调用 /v1,应用 key 也不能调用管理或机器接口。"
+ },
+ {
+ "slug": "quickstart",
+ "source": "docs/getting-started/quickstart.md",
+ "title": "Call the Agent API",
+ "description": "Use the pinned SDK, a Project API key and an explicit model provider to run a Session.",
+ "zh": "应用设置 OPENAI_BASE_URL=https://core.example.com/v1 和 OPENAI_API_KEY=,使用文档固定的 SDK 版本。先配置执行后端和模型,再创建 Session 并读取 Turn 结果。"
+ },
+ {
+ "slug": "public-api",
+ "source": "docs/api/README.md",
+ "title": "API namespaces and credentials",
+ "description": "The public application API, private administration API and machine connection interface.",
+ "zh": "/v1 使用 Project API key;/core/v1 使用 Core key,由 Web 服务端或管理员脚本调用;/api/v1 只接受对应的节点、注册、daemon 或 executor 凭据。这三类凭据不能互换。"
+ },
+ {
+ "slug": "agents-and-tools",
+ "source": "contracts/agents-api/execution-tools.md",
+ "title": "Agents, tools and harnesses",
+ "description": "Native harness tools, shared declarations and the limits of execution support.",
+ "zh": "保存 Agent 只保存配置,不启动执行。工具声明由所选原生 harness 执行;Codex、Claude Code 与 MiniMax Code 的能力和限制不同。x_agents_core 保留原有扩展字段名称。"
+ },
+ {
+ "slug": "sessions",
+ "source": "docs/api/public-agent-api.md",
+ "title": "Sessions and public resources",
+ "description": "Agents, Sessions, Turns, events and resources in the pinned public contract.",
+ "zh": "Session 是持久对话上下文,绑定一个 Environment。输入事件启动或引导 Turn。Session、Turn 和 Environment 的状态各有含义,不应互相替代;具体字段以公共参考为准。"
+ },
+ {
+ "slug": "environments-and-files",
+ "source": "contracts/agents-api/environments.md",
+ "title": "Environments and files",
+ "description": "Execution placement, Environment resources and workspace ownership.",
+ "zh": "Environment 与 Session 一起创建。none、self_hosted、openai_hosted 是协议中的执行位置名称;openai_hosted 在这里表示 Core 管理的计算资源。文件和工作区仍由对应资源及执行所有者管理。"
+ },
+ {
+ "slug": "hosted-providers",
+ "source": "docs/getting-started/nodes.md",
+ "title": "Nodes and hosted providers",
+ "description": "Add and remove Docker or microsandbox nodes; diagnose connection and readiness.",
+ "zh": "在 Web 的 Nodes 页面添加节点,并在目标主机执行经过校验的命令。sudo 模式使用 oac-node 系统账户;无 sudo 模式使用用户服务。旧名称的同一安装实例必须先按旧版本流程移除,再添加。"
+ },
+ {
+ "slug": "self-hosted-execution",
+ "source": "docs/getting-started/self-hosted.md",
+ "title": "Self-hosted executors",
+ "description": "Connect a user-owned Runtime to its Environment with a restricted executor credential.",
+ "zh": "self_hosted 执行器由用户拥有,Core 不管理它的容器。通过 Web 为对应 Environment 签发 executor 凭据,安装器通过隐藏输入或私有文件读取凭据;不要把凭据放入命令行。"
+ },
+ {
+ "slug": "console",
+ "source": "docs/web/README.md",
+ "title": "Administrator console",
+ "description": "Monitor Core, inspect resources and manage deployment settings through Web.",
+ "zh": "Web 是管理员控制台,提供 Monitor、Resources 和 Platform 操作。浏览器使用管理会话;Core key 作为上游凭据保留在 Web 服务端。应用 API 的执行请求不经过 Web。",
+ "images": {
+ "docs/web/images/overview.png": "apps/web/public/onboarding/monitor-en.webp"
+ }
+ },
+ {
+ "slug": "admin-api",
+ "source": "docs/api/web-management.md",
+ "title": "Core administration API",
+ "description": "Management operations and their server-side credential boundary.",
+ "zh": "管理员脚本通过 Core 的回环端口调用 /core/v1,并使用 Core key。公网入口的 /core/v1 由 Web 管理会话处理;不能把它当成应用 API 或机器连接的代理。"
+ },
+ {
+ "slug": "observability",
+ "source": "contracts/agents-api/runtime-observability-api.md",
+ "title": "Runtime observations",
+ "description": "Current Runtime observations, recorded history and their availability limits.",
+ "zh": "当前 Runtime 观测与已保存历史是不同的数据。离线或陈旧数据不代表当前健康状态;查看来源、时间和可用性,执行事实仍以 Core 为准。"
+ },
+ {
+ "slug": "troubleshooting",
+ "source": "docs/getting-started/operations.md",
+ "title": "Operations and troubleshooting",
+ "description": "oac lifecycle commands, backups, key rotation, upgrades and diagnostics.",
+ "zh": "使用 oac status 查看安装状态,oac doctor 诊断配置,oac apply 应用 config.json。备份必须同时保留数据库、配置和密钥。升级按当前指南执行;不要假设在线 generation 更新或 reset 已实现。"
+ }
+]
diff --git a/apps/docs/scripts/test_contract_routes.py b/apps/docs/scripts/test_contract_routes.py
new file mode 100644
index 000000000..243b9dd75
--- /dev/null
+++ b/apps/docs/scripts/test_contract_routes.py
@@ -0,0 +1,28 @@
+"""Regression coverage for nested router helpers and exact namespace inheritance."""
+import importlib.util
+from pathlib import Path
+import unittest
+
+spec = importlib.util.spec_from_file_location("routes", Path(__file__).with_name("verify-contract-routes.py"))
+routes = importlib.util.module_from_spec(spec)
+spec.loader.exec_module(routes)
+
+
+class RouteTests(unittest.TestCase):
+ def test_nested_registration_inherits_parent_scope(self):
+ region = routes.Region('''router.Route("/core/v1", func(r chi.Router) {
+ r.Group(func(inner chi.Router) {
+ h.registerNestedRoutes(inner)
+ })
+ })''')
+ self.assertEqual(region.calls({"router": ""}), [("registerNestedRoutes", "/core/v1")])
+ child = routes.Region('r.Get("/projects/{project_id}", h.getProject)')
+ self.assertEqual(child.routes({"r": "/core/v1"}), [("/core/v1/projects/{project_id}", "GET")])
+
+ def test_unrelated_router_does_not_acquire_a_prefix(self):
+ region = routes.Region('unknown.Get("/projects", handler)')
+ self.assertEqual(region.routes({"r": "/core/v1"}), [])
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/apps/docs/scripts/verify-api-copy.mjs b/apps/docs/scripts/verify-api-copy.mjs
new file mode 100644
index 000000000..743a100ef
--- /dev/null
+++ b/apps/docs/scripts/verify-api-copy.mjs
@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+// Compare every rendered operation and schema with the current contract projection.
+import fs from 'node:fs'
+import path from 'node:path'
+import yaml from 'js-yaml'
+import assert from 'node:assert/strict'
+import { appRoot, surfaces, normalise, methods } from './contracts.mjs'
+let count = 0
+for (const surface of surfaces) {
+ const actual = yaml.load(fs.readFileSync(path.join(appRoot, 'openapi', surface.id + '.yaml'), 'utf8'))
+ assert.deepEqual(actual, normalise(surface), surface.id + ': rendered contract differs')
+ const listed = []
+ const dir = path.join(appRoot, 'content/docs/api-reference') + surface.directory
+ for (const name of fs.readdirSync(dir).filter(n => n.endsWith('.mdx') && n !== 'index.mdx')) {
+ const content = fs.readFileSync(path.join(dir, name), 'utf8')
+ assert.ok(content.includes('document={' + JSON.stringify(surface.id) + '}'), 'Wrong reference credential surface: ' + name)
+ const operations = content.match(/operations=\{(\[[\s\S]*?\])\}/)
+ assert.ok(operations, 'Missing operations: ' + name)
+ for (const op of JSON.parse(operations[1])) listed.push(op.method + ' ' + op.path)
+ }
+ const expected = Object.entries(actual.paths).flatMap(([route, item]) => methods.filter(m => item[m]).map(m => m + ' ' + route))
+ assert.deepEqual([...new Set(listed)].sort(), expected.sort(), 'Missing or obsolete API page: ' + surface.id)
+ count += expected.length
+}
+console.log(count + ' operations, schemas, descriptions and credential surfaces match current contracts.')
diff --git a/apps/docs/scripts/verify-contract-freshness.mjs b/apps/docs/scripts/verify-contract-freshness.mjs
new file mode 100644
index 000000000..785c6dc3c
--- /dev/null
+++ b/apps/docs/scripts/verify-contract-freshness.mjs
@@ -0,0 +1,24 @@
+#!/usr/bin/env node
+// Source and output digests catch both stale generation and edited generated prose.
+import fs from 'node:fs'
+import path from 'node:path'
+import crypto from 'node:crypto'
+import assert from 'node:assert/strict'
+import { appRoot, repoRoot, surfaces } from './contracts.mjs'
+const record = JSON.parse(fs.readFileSync(path.join(appRoot, 'openapi/sources.json')))
+assert.deepEqual(Object.keys(record.sources).sort(), surfaces.map(s => 'contracts/agents-api/' + s.file).sort())
+function filesUnder(directory) {
+ return fs.readdirSync(directory, { withFileTypes: true }).flatMap(entry => {
+ const file = path.join(directory, entry.name)
+ return entry.isDirectory() ? filesUnder(file) : [path.relative(appRoot, file)]
+ })
+}
+const rendered = [...filesUnder(path.join(appRoot, 'openapi')), ...filesUnder(path.join(appRoot, 'content/docs/api-reference'))]
+ .filter(file => file !== 'openapi/sources.json')
+assert.deepEqual(rendered.sort(), Object.keys(record.outputs).sort(), 'Unexpected or missing generated reference files')
+for (const [root, entries] of [[repoRoot, record.sources], [appRoot, record.outputs]]) {
+ for (const [relative, digest] of Object.entries(entries)) {
+ assert.equal(crypto.createHash('sha256').update(fs.readFileSync(path.join(root, relative))).digest('hex'), digest, 'Regenerate API reference: ' + relative)
+ }
+}
+console.log('All three contract sources and generated references are current.')
diff --git a/apps/docs/scripts/verify-contract-routes.py b/apps/docs/scripts/verify-contract-routes.py
new file mode 100644
index 000000000..c9f51d2f5
--- /dev/null
+++ b/apps/docs/scripts/verify-contract-routes.py
@@ -0,0 +1,227 @@
+#!/usr/bin/env python3
+"""Check the generated OpenAPI contracts against the routes the code registers.
+
+The public contract is constrained by its pinned upstream baseline; Core and
+machine contracts are generated from handler annotations. This reads the real
+`chi` route table from services/agents-api/internal/api and compares all three
+contracts with the registered paths.
+
+Two checks:
+ [A] routes the code registers that no contract publishes
+ [B] paths a contract publishes that the code never registers <- the real bug
+
+Scope resolution is brace-matched rather than line-based, because chi nests
+`Route(prefix, func(r chi.Router) { ... })` and the same variable name is reused
+at each level.
+
+Read-only. Usage (from apps/docs):
+ node scripts/check-python.mjs routes
+"""
+
+from __future__ import annotations
+
+import json
+import pathlib
+import re
+import sys
+
+
+APP = pathlib.Path(__file__).resolve().parent.parent
+REPO = APP.parent.parent
+API = REPO / "services/agents-api/internal/api"
+
+
+VERBS = ("Get", "Post", "Put", "Patch", "Delete", "Head", "Options", "Trace")
+ROUTE = re.compile(r"\b(\w+)\.(%s)\(\s*([^,)]+?)\s*[,)]" % "|".join(VERBS))
+METHOD = re.compile(r"\b(\w+)\.Method\(\s*(\"[^\"]+\"|[\w.]+)\s*,\s*(\"[A-Za-z]+\"|http\.Method[A-Za-z]+)")
+BLOCK = re.compile(r"\b(\w+)\.(Route|Group)\(\s*(?:\"([^\"]*)\"\s*,\s*)?func\(\s*(\w+)\s+chi\.Router")
+CALL = re.compile(r"\bh\.(register\w+Routes)\(\s*(\w+)\s*\)")
+FUNC = re.compile(r"^func\s+(?:\([^)]*\)\s+)?(register\w+Routes)\(\s*(\w+)\s+chi\.Router", re.M)
+CONST = re.compile(r"\bconst\s+(\w+)\s*=\s*\"([^\"]*)\"")
+
+IGNORED = {("/healthz", "GET"): "liveness probe, not part of the Agent API"}
+
+# Differences between the contract and the registered route that the reference
+# accepts on purpose, keyed (path, method) as the contract spells them.
+ACCEPTED = {}
+
+# A HEAD route wired to `methodNotAllowed` is an explicit 405 guard, not an API.
+GUARD = re.compile(r"\.(Head|Options|Get|Post)\(\s*([^,]+),\s*methodNotAllowed\s*\)")
+
+
+def brace_end(text: str, start: int) -> int:
+ depth = 0
+ for index in range(start, len(text)):
+ if text[index] == "{":
+ depth += 1
+ elif text[index] == "}":
+ depth -= 1
+ if depth == 0:
+ return index
+ return len(text)
+
+
+class Region:
+ def __init__(self, text: str):
+ self.text = text
+ self.consts = {name: value for name, value in CONST.findall(text)}
+ self.blocks = []
+ for match in BLOCK.finditer(text):
+ brace = text.find("{", match.end())
+ if brace < 0:
+ continue
+ self.blocks.append(
+ {
+ "outer": match.group(1),
+ "inner": match.group(4),
+ "prefix": match.group(3) or "",
+ "own": match.group(2) == "Route",
+ "start": match.start(),
+ "open": brace,
+ "end": brace_end(text, brace),
+ }
+ )
+
+ def _enclosing(self, position: int):
+ inside = [b for b in self.blocks if b["open"] < position < b["end"]]
+ inside.sort(key=lambda b: b["open"])
+ return inside
+
+ def prefix_of(self, var: str, position: int, seed: dict[str, str]) -> str | None:
+ """Prefix for a router variable at a position, or None if unknown."""
+ for block in reversed(self._enclosing(position)):
+ if block["inner"] == var:
+ outer = self.prefix_of(block["outer"], block["start"], seed)
+ if outer is None:
+ return None
+ return outer + block["prefix"] if block["own"] else outer
+ return seed.get(var)
+
+ def literal(self, expr: str) -> str | None:
+ parts = re.split(r"\s*\+\s*", expr.strip())
+ out = ""
+ for part in parts:
+ part = part.strip()
+ if len(part) >= 2 and part[0] == '"' and part[-1] == '"':
+ out += part[1:-1]
+ elif part in self.consts:
+ out += self.consts[part]
+ else:
+ return None
+ return out or None
+
+ def routes(self, seed: dict[str, str]):
+ found: list[tuple[str, str]] = []
+ for match in ROUTE.finditer(self.text):
+ var, verb, expr = match.group(1), match.group(2), match.group(3)
+ path = self.literal(expr)
+ if path is None or not path.startswith("/"):
+ continue
+ prefix = self.prefix_of(var, match.start(), seed)
+ if prefix is None:
+ continue
+ found.append((prefix + path, verb.upper()))
+ for match in METHOD.finditer(self.text):
+ var, target, verb = match.group(1), match.group(2), match.group(3)
+ path = self.literal(target)
+ if path is None or not path.startswith("/"):
+ continue
+ prefix = self.prefix_of(var, match.start(), seed)
+ if prefix is None:
+ continue
+ method = verb.split(".")[-1] if verb.startswith("http.") else verb.strip('"')
+ found.append((prefix + path, method.upper()))
+ return found
+
+ def calls(self, seed: dict[str, str]):
+ out = []
+ for match in CALL.finditer(self.text):
+ prefix = self.prefix_of(match.group(2), match.start(), seed)
+ out.append((match.group(1), prefix if prefix is not None else ""))
+ return out
+
+
+def main() -> int:
+ files = [f for f in sorted(API.glob("*.go")) if not f.name.endswith("_test.go")]
+ texts = {f.name: f.read_text(encoding="utf-8").replace("\r\n", "\n") for f in files}
+
+ helpers = {}
+ for name, text in texts.items():
+ for match in FUNC.finditer(text):
+ brace = text.find("{", match.end())
+ helpers[match.group(1)] = (name, match.group(2), brace, brace_end(text, brace))
+
+ # Pass 1: everything except helper bodies.
+ code: dict[tuple[str, str], str] = {}
+ calls: list[tuple[str, str]] = []
+ for name, text in texts.items():
+ masked = list(text)
+ for fn, (hfile, _, brace, end) in helpers.items():
+ if hfile != name:
+ continue
+ for index in range(brace, min(end + 1, len(masked))):
+ if masked[index] != "\n":
+ masked[index] = " "
+ region = Region("".join(masked))
+ root = {"router": "", "r": ""}
+ for path, method in region.routes(root):
+ code.setdefault((path, method), name)
+ calls.extend(region.calls(root))
+
+ # Follow nested registration calls with the actual prefix from their caller.
+ seen = set()
+ while calls:
+ fn, prefix = calls.pop()
+ if (fn, prefix) in seen:
+ continue
+ seen.add((fn, prefix))
+ if fn not in helpers:
+ raise ValueError("Unknown route registration helper: " + fn)
+ name, param, brace, end = helpers[fn]
+ region = Region(texts[name][brace : end + 1])
+ for route, method in region.routes({param: prefix}):
+ code.setdefault((route, method), name)
+ calls.extend(region.calls({param: prefix}))
+
+ documented: dict[tuple[str, str], str] = {}
+ # YAML is decoded by the declared Node dependency; Python uses only stdlib.
+ for contract in json.load(sys.stdin):
+ document, label = contract["document"], contract["label"]
+ base = (document.get("basePath") or "").rstrip("/")
+ for route, item in (document.get("paths") or {}).items():
+ for method in item:
+ if method in ("get", "post", "put", "patch", "delete", "head", "options"):
+ documented.setdefault(((base + route) or "/", method.upper()), label)
+
+ guard_paths = set()
+ for name, text in texts.items():
+ for match in GUARD.finditer(text):
+ guard_paths.add(match.group(2).strip().strip('"'))
+
+ unpublished = sorted(k for k in code if k not in documented)
+ missing = sorted(k for k in documented if k not in code)
+
+ print(" Registered (method, path): %d" % len(code))
+ print(" Contract operations: %d" % len(documented))
+ print()
+ guards = [k for k in unpublished if k[1] == "HEAD" and any(k[0].endswith(suffix) for suffix in guard_paths)]
+ real = [k for k in unpublished if k not in guards]
+ undocumented = [k for k in real if k not in IGNORED]
+ print(" [A] Registered but not published: %d (%d explicit HEAD guards)" % (len(unpublished), len(guards)))
+ for path, method in real:
+ note = IGNORED.get((path, method), "")
+ print(" %-6s %-72s %s%s" % (method, path, code[(path, method)], (" <- " + note) if note else ""))
+ print()
+ accepted = [k for k in missing if k in ACCEPTED]
+ real = [k for k in missing if k not in ACCEPTED]
+
+ print(" [B] Published but not registered: %d (%d accepted differences)" % (len(missing), len(accepted)))
+ for path, method in real:
+ print(" %-6s %-72s [%s]" % (method, path, documented[(path, method)]))
+ for path, method in accepted:
+ print(" ok %-72s %s" % (path, ACCEPTED[(path, method)]))
+ return 1 if real or undocumented else 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/apps/docs/scripts/verify-docs-facts.mjs b/apps/docs/scripts/verify-docs-facts.mjs
new file mode 100644
index 000000000..d8c7522ae
--- /dev/null
+++ b/apps/docs/scripts/verify-docs-facts.mjs
@@ -0,0 +1,27 @@
+#!/usr/bin/env node
+// Guide source drift requires regeneration and a review of the Chinese reading notes.
+import fs from 'node:fs'
+import path from 'node:path'
+import crypto from 'node:crypto'
+import assert from 'node:assert/strict'
+import { fileURLToPath } from 'node:url'
+const app = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
+const repo = path.resolve(app, '../..')
+const record = JSON.parse(fs.readFileSync(path.join(app, 'content/guide-sources.json')))
+for (const [root, entries] of [[repo, record.sources], [app, record.outputs]]) {
+ for (const [relative, digest] of Object.entries(entries)) {
+ assert.equal(crypto.createHash('sha256').update(fs.readFileSync(path.join(root, relative))).digest('hex'), digest, 'Regenerate guides and review translations: ' + relative)
+ }
+}
+const source = slug => fs.readFileSync(path.join(app, 'content/docs', slug + '.mdx'), 'utf8')
+for (const token of ['/v1', '/core/v1', '/api/v1', 'Project API key', 'Core key', 'executor']) assert.ok(source('public-api').includes(token), 'Credential matrix omits ' + token)
+for (const token of ['config.json', 'oac apply']) assert.ok(source('configure').includes(token), 'Configuration guide omits ' + token)
+for (const token of ['oac-node', '~/.oac/nodes']) assert.ok(source('hosted-providers').includes(token), 'Node guide omits ' + token)
+for (const token of ['oac-selfhost', '~/.oac/self-hosted']) assert.ok(source('self-hosted-execution').includes(token), 'Executor guide omits ' + token)
+for (const file of fs.readdirSync(path.join(app, 'content/docs')).filter(n => n.endsWith('.mdx'))) {
+ const text = fs.readFileSync(path.join(app, 'content/docs', file), 'utf8')
+ for (const retired of ['/core/v1/admin', 'sandbox-manager.openapi.yaml']) assert.ok(!text.includes(retired), 'Obsolete claim in ' + file + ': ' + retired)
+}
+assert.ok(fs.readFileSync(path.join(app, 'FOLLOW-UP.md'), 'utf8').includes('design 50'), 'Missing deferred feature inventory')
+assert.deepEqual(fs.readFileSync(path.join(app, 'app/icon.png')), fs.readFileSync(path.join(repo, 'apps/web/public/favicon.png')), 'Docs favicon must match the approved Web asset')
+console.log('Guide copies, source authority and namespace/configuration facts are current.')
diff --git a/apps/docs/scripts/verify-links.mjs b/apps/docs/scripts/verify-links.mjs
new file mode 100644
index 000000000..bcef7a532
--- /dev/null
+++ b/apps/docs/scripts/verify-links.mjs
@@ -0,0 +1,132 @@
+#!/usr/bin/env node
+// Fails when a page links to a route that does not exist.
+//
+// The manual is a reading path: install, configure, bootstrap a Project, make a
+// request, add capacity. A link that 404s breaks that path silently, because the
+// page it lives on still renders. This resolves every root-relative link against
+// the page files themselves, so it needs no running server.
+//
+// `content/docs/api-reference/*` is generated in English only, and fumadocs
+// serves the default language for a missing translation, so a Chinese page may
+// link to the English route and have it resolve. A link is therefore accepted
+// when the target exists in either locale.
+//
+// Markdown that is not a page - the README - is checked too, against the
+// filesystem, because its links are relative file paths. Removing a file it
+// points at is otherwise invisible until a reviewer clicks it.
+//
+// Read-only. Run from apps/docs: node scripts/verify-links.mjs
+
+import fs from "node:fs"
+import path from "node:path"
+import { fileURLToPath } from "node:url"
+
+const here = path.dirname(fileURLToPath(import.meta.url))
+const contentRoot = path.resolve(here, "../content/docs")
+const appRoot = path.resolve(here, "..")
+
+function mdxFiles(dir) {
+ const out = []
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const full = path.join(dir, entry.name)
+ if (entry.isDirectory()) out.push(...mdxFiles(full))
+ else if (entry.name.endsWith(".mdx")) out.push(full)
+ }
+ return out
+}
+
+// index.mdx is the directory itself; index.zh.mdx is its Chinese counterpart.
+function routeOf(file) {
+ const rel = path.relative(contentRoot, file).replace(/\.mdx$/, "")
+ const parts = rel.split(path.sep)
+ let name = parts.pop()
+ const isZh = name.endsWith(".zh")
+ if (isZh) name = name.slice(0, -3)
+ const slug = name === "index" ? parts.join("/") : [...parts, name].join("/")
+ const base = slug ? `/${slug}` : "/"
+ return isZh ? (base === "/" ? "/zh" : `/zh${base}`) : base
+}
+
+const files = mdxFiles(contentRoot)
+const routes = new Set(files.map(routeOf))
+
+function resolves(target) {
+ const [rawPath, anchor] = target.split("#")
+ const clean = rawPath.split("?")[0].replace(/^\/en(?=\/|$)/, "") || "/"
+ if (clean.startsWith("/images/")) return fs.existsSync(path.join(appRoot, "public", clean))
+ const route = clean.length > 1 ? clean.replace(/\/+$/, "") : clean
+ const bare = route === "/zh" ? "/" : route.startsWith("/zh/") ? route.slice(3) : route
+ const targetFile = files.find(file => routeOf(file) === bare)
+ if (!targetFile) return false
+ if (!anchor) return true
+ const text = fs.readFileSync(targetFile, "utf8")
+ const seen = new Map()
+ const anchors = []
+ let fence = false
+ for (const line of text.split("\n")) {
+ if (/^\s*(```|~~~)/.test(line)) { fence = !fence; continue }
+ if (fence) continue
+ const heading = line.match(/^#{1,6} (.+)$/)
+ if (!heading) continue
+ const value = heading[1].replace(/`/g, "").replace(/\[([^\]]+)\]\([^)]+\)/g, "$1")
+ .toLowerCase().replace(/[^\p{L}\p{N}_\s-]/gu, "").replace(/\s/g, "-")
+ const previous = seen.get(value) ?? 0
+ seen.set(value, previous + 1)
+ anchors.push(value + (previous ? "-" + previous : ""))
+ }
+ return anchors.includes(decodeURIComponent(anchor))
+}
+
+const link = /\]\(([^)\s]+)/g
+let checked = 0
+let broken = 0
+
+for (const file of files) {
+ const source = fs.readFileSync(file, "utf8")
+ for (const [, target] of source.matchAll(link)) {
+ if (!target.startsWith("/")) continue
+ checked += 1
+ if (!resolves(target)) {
+ console.error(
+ `BROKEN ${path.relative(contentRoot, file)}\n` +
+ ` claim: every root-relative link points at a page that exists\n` +
+ ` link: ${target}`,
+ )
+ broken += 1
+ }
+ }
+}
+
+// This package also ships markdown that is not a page. Its links are relative
+// file paths, so they are resolved against the filesystem instead of the routes.
+const packageDocs = fs
+ .readdirSync(appRoot, { withFileTypes: true })
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".md"))
+ .map((entry) => path.join(appRoot, entry.name))
+
+let fileLinks = 0
+for (const file of packageDocs) {
+ const source = fs.readFileSync(file, "utf8")
+ for (const [, target] of source.matchAll(link)) {
+ if (target.startsWith("/") || /^[a-z][a-z0-9+.-]*:/i.test(target)) continue
+ fileLinks += 1
+ const resolved = path.resolve(path.dirname(file), target.split("#")[0])
+ if (!fs.existsSync(resolved)) {
+ console.error(
+ `BROKEN ${path.relative(appRoot, file)}\n` +
+ ` claim: every relative link points at a file that exists\n` +
+ ` link: ${target}`,
+ )
+ broken += 1
+ }
+ }
+}
+
+if (broken > 0) {
+ console.error(`\n${broken} of ${checked + fileLinks} links are unresolvable.`)
+ process.exit(1)
+}
+console.log(
+ `${checked} root-relative links resolve across ${files.length} pages, and ` +
+ `${fileLinks} relative links resolve across ${packageDocs.length} package files.`,
+)
diff --git a/apps/docs/scripts/verify-prerender.mjs b/apps/docs/scripts/verify-prerender.mjs
new file mode 100644
index 000000000..17f53b6cc
--- /dev/null
+++ b/apps/docs/scripts/verify-prerender.mjs
@@ -0,0 +1,84 @@
+#!/usr/bin/env node
+// Asserts that `next build` actually wrote a prerendered page for every route.
+//
+// This guards a regression that is invisible in the build log. A single
+// request-scoped API anywhere in the `[lang]/[[...slug]]` render tree — in the
+// page, in a layout, or in the `not-found` boundary — makes Next render every
+// docs page per request. The build summary still prints `● (SSG)` and still
+// lists all the paths, but no `.html`/`.rsc` files are emitted and every
+// response comes back `Cache-Control: no-store`.
+//
+// The cost lands hardest on the generated API reference: those pages render a
+// whole OpenAPI surface, so a navigation that should take 30 ms took 2–3 s.
+//
+// Usage, after `pnpm build`:
+//
+// node scripts/verify-prerender.mjs
+
+import fs from "node:fs"
+import path from "node:path"
+import { fileURLToPath } from "node:url"
+
+const here = path.dirname(fileURLToPath(import.meta.url))
+const root = path.resolve(here, "..")
+const contentRoot = path.join(root, "content/docs")
+const appRoot = path.join(root, ".next/server/app")
+
+if (!fs.existsSync(appRoot)) {
+ console.error(`No build output at ${path.relative(root, appRoot)}. Run \`pnpm build\` first.`)
+ process.exit(1)
+}
+
+function walk(dir) {
+ const out = []
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const full = path.join(dir, entry.name)
+ if (entry.isDirectory()) out.push(...walk(full))
+ else if (entry.name.endsWith(".mdx")) out.push(full)
+ }
+ return out
+}
+
+function slugFor(file) {
+ let rel = path.relative(contentRoot, file).split(path.sep).join("/")
+ rel = rel.replace(/\.zh\.mdx$/, "").replace(/\.mdx$/, "").replace(/(^|\/)index$/, "")
+ return rel.replace(/\/$/, "")
+}
+
+// Every page is generated in both locales: an untranslated page falls back to
+// the default language, so `/zh/` is a real route either way.
+const slugs = [...new Set(walk(contentRoot).map(slugFor))].sort()
+const locales = ["en", "zh"]
+
+const missing = []
+for (const locale of locales) {
+ for (const slug of slugs) {
+ // `.../en.html` for the locale root, `.../en/.html` otherwise.
+ const file = slug
+ ? path.join(appRoot, locale, `${slug}.html`)
+ : path.join(appRoot, `${locale}.html`)
+ if (!fs.existsSync(file)) missing.push(path.relative(root, file))
+ else if (slug.startsWith("api-reference/")) {
+ const html = fs.readFileSync(file, "utf8")
+ if (/