The Agentic AI Studio is V1's first-class surface for autonomous content development, research, tutoring, curation, education, and operations. Agents here are not hidden orchestration: they are observable, governed, budgeted, attributable products consumed by customers, creators, operators, and Metis institutional partners across Oshun Web, Mobile, Studio, Admin, and the Tenant Console. This page is the foundational third of the Studio — the canonical agent registry (who exists and what authority they carry), the job orchestration layer that executes long-lived agent runs durably, and the multi-agent plan / hand-off / approval-gate machinery that keeps a research agent dispatching a Sophia retriever and an Isis generator from silently spinning into a runaway run. Its siblings cover the rest: Agent Invocation, Budgets, Memory, and Feedback Loops and Tool Catalog, Grant Semantics, and Multi-Agent Protocol.
Where this sits, and what is real versus gated#
Unusually for V1's substrate libraries, the agentic Studio's data and logic
plane is overwhelmingly real and value-tested, not aspirational. The package
@oshun/agentic-studio (v0.1.0, libs/oshun/agentic-studio) re-exports twelve
implemented subsystems from its src/index.ts — registry, runs, plans,
dashboard, invocation, budgets, capabilities, feedback, modes,
pipelines, grants, and handoff — and ships 271 passing tests across 16
test files. The state machines, the SSRF egress guards, the grant-resolution
specificity ordering, the champion-challenger z-test, and the hand-off
budget/authority/cycle/depth validation are all genuine domain logic. It is
honest, though, to draw the line precisely:
- Real, in-repo, unit-tested. Everything described below as a function,
enum, schema, or validation rule. The registry's integrity checks, the
AgentRunEnvelope, the run-control state transitions, the checkpoint/resume fingerprinting, the replay timeline, the streaming-progress visibility tiers, the plan DAG validator (cycle detection and topological order), the approval gates, the branching selection policies, the re-plan provenance, the operator dashboard query/audit/action data plane, and the runtime governance seam (admitToolCall→dispatchGuardedToolCall→runGuardedToolPlan). - In-memory / in-process, not durable. The persistence is deliberately a
data-and-logic layer, not yet wired to durable infrastructure.
AgentRegistryis backed by aMap<string, AgentRegistryEntry>; the BFF run-lifecycle store (apps/oshun/bff/src/agentic/run-lifecycle-store.ts) is an in-process store. The Studio does not import@oshun/queue(the durable-jobs lib lives atlibs/shared/queue), so claims of "durable jobs with priority classes / DLQ / SLA monitor" are aspirational at the Studio layer. The runs modules (runs/dispatcher.ts,runs/orchestrator.ts,runs/checkpoint.ts,runs/replay.ts) are pure-function logic with no queue binding. Checkpoints model durability (aRunCheckpointsnapshots envelope state plus an artifact manifest withpersistedKeys reachable from a side object store), but the side store itself is a deploy-time integration. - Data/query layers, not a rich admin UI. "Replay and time-travel
debugging," "streaming progress," and the "operator dashboard" are
implemented as query/projection modules (
runs/replay.ts,runs/streaming-progress.ts,dashboard/dashboard-query.ts,dashboard/run-detail-page.ts). There is no rich admin React dashboard page: the admin app exposes only API routes underapps/oshun/admin/src/app/api/admin/agentic-operations/(kill-switches,snapshot,gold-sets/promote,champion-challengers/rollout), and the only agent page in the codebase isapps/oshun/tenant-admin/src/app/agents/page.tsx. (Companion-doc note:ARCHITECTURE.md's reference to an operator dashboard atapps/oshun/admin/src/.../agents/does not resolve — no such directory exists.) - Governance brain real; live tool execution boundary-gated. Tool grants and
capability modifiers are declared, validated, and enforced as ceilings, but
the actual tool execution (the real
web.fetch,sophia.ground, and so on) is injected at the app boundary and fail-closed by default. The Studio is the enforcement and accounting engine; "autonomous content gets produced end-to-end through live providers" is a seam-and-fail-loud story, not a wired one. See Tool Catalog, Grant Semantics, and Multi-Agent Protocol for the catalog and grant semantics.
This page documents the registry, jobs, and plans honestly against that line.
Agent Registry and Catalog#
Every agent that can run is a row in the canonical AgentRegistry
(registry/agent-registry.ts). The registry is the single source of truth for
an agent's identity and authority: a stable agentId, a semver, the agent
family, versioned capabilities, declared toolGrants and dataScopes, a
modelBinding, a personaBinding, a tonePolicyId / groundingPolicyId /
auditPolicyId, a costClass, a lifecycleState, and the visibility /
disclosureRequirements that drive catalog exposure.
The AgentRegistryEntry schema#
| Field | Type | Meaning |
|---|---|---|
agentId |
string |
Stable identifier; duplicates rejected at register. |
semver |
string |
Agent version; must match the semver regex. |
family |
AgentFamily |
One of the 20 canonical families (below). |
displayName / summary |
string |
Catalog-card copy. |
ownerTeamId |
string |
Owning team — a dashboard filter dimension. |
tenantScope |
'platform' | { tenantId } |
Platform-wide or tenant-bound. |
capabilities |
AgentCapability[] |
Each with its own semver, input/output schema names, supported locales/domains. |
toolGrants |
string[] |
Tool ids the agent may call — hard-capped by the family ceiling. |
dataScopes |
DataScope[] |
From the 8 declared scopes (below). |
modelBinding |
AgentModelBinding |
providerId/modelId/semver + defaultMaxTokens/defaultTemperature + fallback provider/model. |
personaBinding |
string |
Lilith persona (see Lilith Persona Policy). |
costClass |
AgentCostClass |
micro | small | medium | large | flagship. |
lifecycleState |
AgentLifecycleState |
draft | rehearsal | shadow | champion | challenger | deprecated | retired. |
experimental |
boolean |
Gates catalog visibility behind a feature flag. |
visibility |
('platform' | 'tenant' | 'creator' | 'customer')[] |
Which roles see the agent. |
disclosureRequirements |
string[] |
Mandatory for a champion. |
The 20 canonical agent families#
AGENT_FAMILIES (registry/agent-families.ts) declares exactly twenty
families. Each family carries AgentFamilyMeta with a summary,
primaryDomains, a defaultGroundingPolicy
(strict-cite | cite-where-available | no-citation), a defaultPersonaPolicy
(lilith.teacher | guide | scribe | host), and — the load-bearing field — a
toolGrantCeiling that the registry enforces as a hard ceiling on an
agent's declared toolGrants.
| Family | Summary | Tool-grant ceiling |
|---|---|---|
research |
Find and triage sources for a topic. | web.fetch, source.fetch, sophia.ground, memory.read |
drafting |
Produce an initial draft against a brief. | source.fetch, sophia.ground, memory.read, persona.invoke |
editing |
Edit a draft for clarity, tone, structure, and accuracy. | memory.read, sophia.fact_check, persona.invoke |
fact-checking |
Verify claims against evidence packs. | sophia.fact_check, sophia.ground, source.fetch |
citation-verification |
Re-anchor citations and verify quotes. | source.fetch, sophia.ground |
illustration |
Generate or compose illustrative imagery. | generate.image, composition.suggest |
narration |
Render audio narration with a voice profile. | generate.audio, persona.invoke |
translation |
Translate text/transcripts between locales. | memory.read |
course-generation |
Generate a full course from a brief/BYOM. | source.fetch, sophia.ground, persona.invoke, handoff |
lesson-scaffolding |
Per-lesson scaffolds with objectives and checks. | source.fetch, memory.read, persona.invoke |
assessment-generation |
Items with answer keys and rubrics. | source.fetch, themis.adjudicate |
study-plan-synthesis |
Personalized study plan from mastery state. | memory.read |
recommendation-explanation |
Human-readable rationales for a recommendation. | memory.read |
moderation-triage |
Pre-screen content for safety/abuse/policy. | themis.adjudicate |
support-triage |
Route support requests to the right human queue. | memory.read, handoff |
ritual-scriptwriting |
Draft ritual scripts with lineage-aware tone. | source.fetch, persona.invoke, approval.request |
sky-event-briefing |
Explainer set for an upcoming celestial event. | source.fetch, sophia.ground, generate.image |
claim-extraction |
Extract atomic claims from a passage/draft. | memory.read |
source-ingestion |
Onboard a new source with metadata. | web.fetch, source.fetch, file.read |
kg-promotion |
Promote concepts into the knowledge graph. | memory.read, memory.write, approval.request |
The family ceiling is a structural guarantee: an illustration agent literally
cannot be registered with a web.fetch grant, because that tool is absent
from the family's toolGrantCeiling — validateEntry emits a
tool-grant-exceeds-family-ceiling error and register refuses the entry. This
is why the family taxonomy is more than documentation: it is the first authority
boundary an agent passes through.
The eight data scopes and five cost classes#
DATA_SCOPES enumerates exactly eight declared scopes, partitioned by owner and
sensitivity: tenant.public, tenant.private, user.public, user.private,
editorial.draft, editorial.published, platform.metadata, and
platform.curated. AGENT_COST_CLASSES are micro, small, medium,
large, and flagship — the same five-step ladder the dashboard cost bands
mirror.
Registry integrity checks#
AgentRegistry.register(entry) returns a
readonly AgentRegistryValidationError[] (empty array = accepted). Nothing is
stored unless validation passes. The validation is a real
allowlist-and-consistency pass, not a shape check; it rejects:
duplicate-agent-id— theagentIdis already registered.unknown-family—familyis not inAGENT_FAMILIES.tool-grant-exceeds-family-ceiling— a declared tool is outside the family'stoolGrantCeiling(one error per offending tool).invalid-semver— the agent, a capability, or the model binding has a version that fails^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$.unknown-data-scope— a declared scope is not inDATA_SCOPES.lifecycle-experimental-mismatch— anexperimentalagent claims thechampionlifecycle state (an experiment cannot be the production champion).duplicate-capability-id— two capabilities share acapabilityId.champion-without-disclosure— achampionagent declares nodisclosureRequirements(the public must always be told an agent assisted).
upsert runs the same validation but allows replacing an existing entry; only
register enforces the duplicate-id rule.
Catalog visibility — per-tenant, per-role, experimental segregation#
isEntryVisibleTo(entry, viewer) resolves the browsable catalog for a
CatalogViewer ({ tenantId, role, featureFlags }), where role is one of
platform-operator | tenant-operator | creator | customer. The rules layer in
order: a retired agent is never visible; a tenant-scoped agent is invisible to
other tenants; an experimental agent is hidden unless the viewer holds the
flag agent.experimental.<agentId> (platform operators always see experimental
agents); and otherwise visibility is keyed off the entry's visibility array
for the viewer's role. This is what realizes the "per-tenant catalog views,
per-role visibility, and feature-flagged experimental agents segregated from the
default catalog" promise. Capability cards, sample runs, evaluation scores, cost
ranges, latency ranges, supported domains/locales, required entitlements, and
reviewer comments are projected over this entry set; the entry itself carries
the locale/domain bindings (per AgentCapability) and disclosure requirements
that those cards display.
Job Orchestration and Long-Running Runs#
A run is captured in the durable AgentRunEnvelope (runs/agent-run.ts) — the
record that makes a run observable, attributable, and resumable rather than a
black box.
The AgentRunEnvelope#
The envelope threads the run's whole life: runId, rootAgentId /
rootAgentSemver, parentRunId (set on a fork or sub-agent), pipelineId /
pipelineSemver, status, tenantId, the inputs, the plan
(RunPlanStep[]), the toolCalls (RunToolCallRecord[]),
intermediateArtifacts, the evidenceTrail, the costLedger, the
decisionRationale, the outputs, timestamps, and a provenanceBundleId. Each
sub-record is purpose-built:
RunToolCallRecord—callId,toolIdandtoolSemver, anargsHash(never raw args), start/complete timestamps, anoutcome(pending | ok | error | denied | cancelled),costUnits,latencyMs, ascopeCheckResultId, anauditEventId, andemittedArtifactIds. The argsHash and result-redaction discipline means transcripts are auditable without leaking payloads.RunEvidenceTrailEntry— per stage, theevidencePackId, agroundingState(grounded | partial | ungrounded | abstained | retracted-source), and acitationCount. This is how a run proves it stayed grounded (see Sophia Grounding).RunDecisionRationale— per stage, a free-text rationale plus adecisionLabel(plan-step-completed | plan-branch-chosen | plan-replanned | gate-passed | gate-failed | tool-selected | tool-skipped | abstained) and an optionalevidencePackId. An agent's why is first-class, not reconstructed.RunPlanStep—stepId/stageId, the boundagentIdandagentSemver, astatus(pending | running | completed | failed | skipped), timestamps, anddependsOnStepIds.
The run cost ledger#
RunCostLedger is a multi-dimensional accounting structure, not a single cost
number. It tracks totalCostUnits plus two ReadonlyMaps — perStageCostUnits
and perToolCostUnits — and the raw resource dimensions: inputTokenCount,
outputTokenCount, gpuMinutes, voiceSeconds, avatarSeconds,
retrievalCount, and externalCallCount. recordToolCallInLedger folds a tool
call into the ledger, attributing its cost to both the stage and the tool and
incrementing externalCallCount. Cost is denominated in cost-units (a
token-cost-equivalent normalized across providers) so cross-provider hand-offs
and budgets are comparable — the same unit the budget and hand-off envelopes
speak. (For the budget enforcement that consumes this ledger, see
Agent Invocation, Budgets, Memory, and Feedback Loops.)
The real run-status enum#
RUN_STATUSES is the implemented state vocabulary, and it is worth naming
exactly, because it is richer than the simplified mermaid sketch in the
companion architecture doc (which uses pending/planning/executing — states
that do not exist in code):
queued · starting · running · paused · awaiting_approval ·
awaiting_tool · awaiting_branch_selection · cancelled · killed ·
failed · completed
The TERMINAL_STATUSES set is { completed, failed, cancelled, killed }.
killed is intentionally distinct from cancelled: a cancelled run is a
deliberate operator stop, a failed run hit an error, and a killed run was
terminated mid-flight by the governance executor (an active kill switch or an
exhausted budget). isTerminal(status) is the single guard every control
function consults before mutating a run.
Long-running execution — checkpoint, resume, partial-output preservation#
For pipelines that run for hours or days (multi-source course generation, weekly
Veritas briefing batches, multi-locale dubbing of a Tara series),
runs/checkpoint.ts provides the durability primitives:
takeCheckpointsnapshots the envelope into aRunCheckpoint: acheckpointId, thecheckpointedAfterStepId(the last completed step), anenvelopeSnapshotFingerprint, theartifactManifest(the intermediate artifacts, each with apersistedKeyreachable from a side object store), and aresumeBudgetUnits.resumeFromCheckpointreturns the envelope with completed steps preserved, every non-completed step reset topending, and status set toqueued. It refuses withunknown-checkpointif the checkpoint'srunIddoes not match, orfingerprint-mismatchif the persisted envelope has diverged from the snapshot. The fingerprint (fingerprintEnvelope) is computed over the deterministic fields — sorted completed-step ids, artifact ids, output ids, and total cost — so resume cannot silently re-attach to a mutated run.preservePartialOutputscaptures whatever artifacts afailed,cancelled, orkilledrun produced into apartial-on-failureoutput bundle (<runId>.partial), so a long run that dies near the end does not lose its work.
(Durability here is modeled in pure functions; the side object store and durable queue are deploy-time integrations, not bound inside the Studio library.)
Streaming progress visibility#
runs/streaming-progress.ts is the append-only progress stream. Events
(RunProgressEvent) carry a monotonic sequence, a tenant scope, a
RunProgressKind (the 15 kinds include plan-published,
plan-step-started/completed, tool-call-started/completed/denied,
artifact-produced, evidence-pack-bound, cost-update,
gate-paused/released, output-bundle-ready, replan-issued, and error),
and — crucially — a ProgressVisibility tier:
| Visibility tier | Delivered to |
|---|---|
operator-only |
platform + tenant operators only |
creator-and-operator |
operators and the creator |
all-including-customer |
everyone, including the customer |
shouldDeliverToSubscriber enforces both the tenant boundary (only a platform
operator crosses tenants) and the visibility tier, so internal tool-call
transcripts never leak to a customer subscriber. summarizeForCustomer derives
the plain narrative a customer-facing run card shows — "outline drafted, sources
gathered, claims verified, illustrations generated, narration rendered, review
pending, publish scheduled" — by filtering to all-including-customer events
and ordering by sequence. appendProgressEvent is the log helper that fails
closed on non-monotonic-sequence, tenant-mismatch, or run-mismatch.
Run controls — pause, resume, branch, fork, retry, operator-step#
runs/run-controls.ts implements the operator controls as pure
(envelope, command) → ControlResult functions. A success returns the next
envelope plus a RunControlAuditEvent; an invalid transition returns a typed
ControlDenialReason. Every control emits an audit event of a matching kind:
| Control | Audit kind | Behavior / guard |
|---|---|---|
pause |
pause |
Only from a pausable status (queued, starting, running, awaiting_tool, awaiting_branch_selection); refuses if terminal. |
resume |
resume |
Only from paused; → running. |
cancel |
cancel |
Deliberate operator stop → terminal cancelled; refuses if already terminal. |
kill |
kill |
Enforced termination → terminal killed; the governance executor's stop, distinct from cancel. |
chooseBranch |
branch-chosen |
Only from awaiting_branch_selection; the chosen stepId must exist in the plan. |
fork |
fork |
Clones the envelope under a fresh newRunId (re-using the source id is rejected with fork-without-fresh-id); completed steps preserved, status reset to queued, parentRunId set. |
retryWithChanges |
retry-with-changes |
Rewinds to rewindToStepId, marks it and all later steps pending, applies caller stepOverrides, restarts at queued. |
operatorStep |
operator-step |
Only when paused; advances exactly one pending step to running for manual step-through debugging. |
Replay and time-travel debugging#
runs/replay.ts builds an investigation view over a saved run.
buildReplayTimeline merges plan steps, tool calls, intermediate artifacts, and
progress events into one chronologically sorted ReplayTimeline.
reconstructStateAt(atUnixSeconds) is genuine time-travel: it reconstructs the
envelope as of a cutoff instant. Completed steps with completedAt ≤ t are kept
as-is, steps merely started by t are shown running, and later steps are
reset to pending. Tool calls, artifacts, and outputs are filtered to those at
or before the cutoff, and the status is taken from the latest applicable
progress event. This is the "full intermediate state and tool-call transcripts"
the source promises — and it is, honestly, a query layer, not an interactive UI.
Multi-Agent Plans, Hand-Offs, and Approval Gates#
A run's behavior is governed by a declared plan, which is independent of the
scheduled RunPlanStep[] execution.
The plan DAG#
plans/plan-dag.ts models a plan as a PlanDag of PlanStages. Each stage
declares its agentId and agentSemver, inputSchemaName /
outputSchemaName, an optional evidenceRequirementId, an optional
mandatoryCheckpointId, qualityGateIds, dependsOnStageIds, and a
branchingKind (sequential | parallel-fanout | parallel-merge). This is the
visible "research → claim-extraction → fact-check → drafting → editing →
illustration → narration → safety-review → publish-gate" DAG the source
describes. validatePlanDag is a real graph validator that emits
duplicate-stage-id, unknown-dependency, unknown-quality-gate, and
cycle-detected (the cycle detector is an iterative white/gray/black DFS that
reconstructs and returns the offending cycle path). topologicalOrder returns a
Kahn's-algorithm ordering where dependencies always precede dependents — the
scheduler's execution order.
Per-stage hand-off contracts#
plans/handoff-contracts.ts binds (stageA.outputSchema → stageB.inputSchema)
plus the evidence and checkpoint guarantees that must hold at the boundary. A
HandoffContract declares fromOutputSchemaName / toInputSchemaName, an
optional requiredEvidenceRequirementId, a requiresCheckpoint flag, and
qualityGateIds. evaluateHandoff returns { ok: true } only when all hold;
otherwise it returns a structured list of every failure — schema-mismatch
(with expected/actual), missing-evidence-pack, missing-checkpoint, and one
unsatisfied-gate per gate. A failed hand-off forces the plan to re-plan or
pause; it never silently proceeds.
Operator-defined approval gates#
plans/approval-gates.ts implements the four gate kinds:
human-required-before-stage (pause before a named stage), before-publish
(pause before any publishable output bundle),
expert-required-on-claim-density-above-threshold (pause when the produced
claim density crosses claimDensityThresholdPer1000Words), and tenant-policy
(a tenant predicate gate). gatesApplicableNow decides which gates apply at the
current execution moment from a GateEvaluationContext. approveGate /
rejectGate enforce that the approver's role is in requiredApproverRoles
(else role-not-permitted), the gate is still pending (else
already-resolved), and the rationale is non-empty (else empty-rationale).
overrideGate is the elevated-authority escape hatch: it bypasses the
required-approver check but records overridden: true and demands a non-empty
rationale — the override is captured honestly and does not pretend the
required approver actually approved.
Branching plans for parallel exploration#
plans/branching.ts realizes "three illustration drafts, two outline
structures, multiple translation candidates." A BranchingPlanSegment fans out
at a stage into N PlanBranches that all execute, then merges with a
BranchSelectionPolicy: explicit-selection (operator/customer picks the
winner), score-merge (branches scored by a declared scoringRubricId, highest
wins), or merge-all (all outputs merged via a mergeStrategyId).
selectBranchOutcome validates the choice — rejecting unknown-branch,
explicit-choice-missing, score-missing, or missing-branch-outcome — and
returns a typed BranchSelection.
Re-planning with audited provenance#
plans/replan.ts makes mid-run re-planning first-class and audited.
REPLAN_TRIGGERS enumerates ten triggers: gate-failed, gate-rejected,
new-evidence, retracted-source, policy-change, budget-exceeded,
unsupported-claim-detected, contradiction-detected, tool-revoked, and
operator-directed. issueReplan builds a ReplanEvent binding the prior plan
id, the new plan id, the trigger, a typed ReplanEvidence discriminated union
(e.g., retracted-source carries sourceIds, policy-change carries
policyId/policyVersion, tool-revoked carries the toolId), the rationale,
and the actor. It refuses with unknown-trigger, plan-unchanged (the new plan
id equals the prior), or evidence-mismatch (the evidence kind contradicts the
declared trigger). So a re-plan can never lose why it happened.
The Runtime Governance Seam#
Plans, budgets, and kill switches decide (pure verdicts), but a decision is
inert until something acts on it. The Studio's enforcement seam — the concrete
machinery the source gestures at abstractly — is three layers in runs/:
admitToolCall(runs/executor.ts) is the admission gate evaluated at every tool-call boundary, running governance checks in safety → cost → rate order. An active kill switch (decideExecution) terminates the run (killed, terminal) with the operator's user-visible status copy and asafeDegradationModeId. An exhausted budget (checkBudget→exceeded) likewise terminates;warn/graceverdicts admit the call but surface asBudgetWarnings. A failed token acquire (acquireToken) is back-pressure: the call isthrottledwith aretryAfterSecondsand the run is not terminated. Otherwise the call is admitted.dispatchGuardedToolCall(runs/dispatcher.ts) wraps the injected tool function so thatrunToolexecutes iff the call is admitted — a kill switch or exhausted budget genuinely prevents the tool from running (and ends the run), a throttle defers it, and a terminal run refuses it.runGuardedToolPlan(runs/orchestrator.ts) threads the envelope through a sequence ofPlannedToolCalls; the moment a call is killed or throttled, it stops — the remaining planned calls never execute. This is what makes "budgets honored, kill switches enforced" hold across a whole run, not just one call.
Where the seam is mounted — the BFF run surface#
The guard had no runtime caller until it was mounted at
POST /v1/agentic/runs/execute (apps/oshun/bff/src/agentic/runs-route.ts).
The route is the concrete enforcement story, and it is fail-closed by
construction:
503 agent_tools_not_configured— every plannedtoolIdmust be a registered tool; with no tools configured (notConfiguredAgentToolRegistryreturns{}), every plan is refused. The route never fabricates a run.400 invalid_request— missingrunId/tenantId/rootAgentId, or an empty/malformed plan.403 tool_scope_missing— a tool with atoolScopeChecksrequirement runs only for callers whose realauthContext.scopessatisfy it; the executor never invents an operator tier for whoever shows up.409 run_id_conflict— arunIdalready persisted by another member is never overwritable; re-execution would replace their envelope and approval history.- Server-authoritative identity — the
actorIdis the authenticated principal and thetenantIdcomes only from the verified claim (authContext.tenantId), never the body, so a body tenant cannot dodge or hijack a tenant-scoped kill switch. - Server-side kill-switch targets — each tool's
family/providerId/regioncome from server-sidetoolTargetsmetadata, never the request body (a missing field defaults to'default'), so an armed family switch is not evadable by omitting a field. - Every-request kill-switch resolution — operator-armed switches are read
from
adminAgenticOperationsStoreon every request and mapped viatoExecutionKillSwitch, honoring the kill switch's ≤ 5 s "arm → next tool-call boundary" propagation budget. Client-supplied switches and budgets are merged in addition — a request can tighten governance but never loosen it.
On a fully executed, unterminated plan, the route finalizes the envelope as
completed and persists it via agentRunLifecycleStore.recordExecutedRun
rather than discarding it after replying, so the operator dashboard sees real
runs.
Operator Job Dashboard, Replay, and Audit#
The operator surface is a data and query plane (dashboard/), consumed by
the admin agentic-operations API routes — not a bespoke React dashboard.
Cross-tenant filtered run listing#
dashboard/dashboard-query.ts buckets runs into active, queued, paused,
blocked, and recently-completed (bucketForStatus maps statuses:
awaiting_approval/awaiting_branch_selection → blocked). A
DashboardFilter filters by bucket, agentFamily, ownerTeamId, tenantId,
costBand (low < 100 ≤ medium < 1000 ≤ high < 10000 ≤ flagship, via
costBandFor), latencyBand (fast < 1000ms ≤ normal < 10000ms ≤ slow, via
latencyBandFor), severity, approvalState, and a time window — plus a
recentlyCompletedWindowSeconds cutoff. materializeDashboardEntry projects an
AgentRunEnvelope into a DashboardEntry with its bucket, cost, latency, and
approval flags.
Per-run page data#
dashboard/run-detail-page.ts's buildRunDetailPage materializes the per-run
view: dagNodes (each stage with its status, inbound stages, per-stage cost
from the ledger, and its gate states), a toolCallTranscript with redacted
args and result previews (via injected redactArgs / redactResult — raw
payloads never appear), the evidence trail, sampled outputs (capped by
sampleOutputLimit), intermediate artifacts, evaluationScores, an
escalationHistory, bound reviewPackageIds, and the replanEvents.
Operator actions and immutable audit#
dashboard/operator-actions.ts authorizes the bulk and per-run actions —
pause, kill, escalate, override-gate, mark-reviewed, defer,
reassign — against an ACTION_AUTHORIZATION map (override-gate and
reassign are platform-operator-only; the rest allow tenant operators too).
authorizeOperatorAction refuses no-targets, role-not-permitted (creators
and customers are blocked outright), and rationale-required, and emits an
OperatorActionAudit. dashboard/audit-events.ts is the immutable per-run
audit log: AUDIT_EVENT_KINDS covers run lifecycle, plan steps, tool calls,
gate decisions, re-plans, budget-exceeded, tool-revoked, and output emission.
appendAuditEvent enforces monotonic per-run sequence and tenant/run scope, and
searchAudit powers cross-run search (a tenant operator is scoped to their own
tenant). SavedInvestigation persists a saved query, and exportRunBundle
produces a redaction-policy-stamped, sequence-sorted ExportableRunBundle for
postmortems and run-replay hand-off between operators.
Cross-Domain Autonomous Pipelines#
The Studio ships seven named V1 pipelines as PipelineSpecs in
pipelines/v1-pipelines.ts, all with lifecycleState: 'live'. Each declares
its participating agents, expected hand-off depth, a total budget cap, mandatory
approval gates, expected outputs, a provenance shape, and an evaluation fixture
set:
| Pipeline id | Invocation tier | Budget cap |
|---|---|---|
veritas.weekly_briefing_pack |
creator |
5000 |
veritas.story_drafting |
platform-operator |
12000 |
tara.seasonal_program |
platform-operator |
10000 |
nisaba.edition_study_guide |
customer |
3000 |
nyx.event_explainer_set |
tenant-operator |
2500 |
arete.weekly_review_draft |
customer |
500 |
metis.course_from_byom |
customer |
20000 |
Two distinct Veritas pipelines. There are genuinely two — and the
v1-pipelines.tssource flags the overlap itself.VERITAS_WEEKLY_BRIEFING_PACKis a creator-tier pipeline that aggregates the week's stories into a multi-format briefing pack;VERITAS_STORY_DRAFTINGis a separate platform-operator-only pipeline that drafts a single candidate story (enumerate sources → ingestion verification → claim extraction → fact-check → contradiction check → counterclaim generation → Lilith tone review → editorial inbox). They are not the same recipe and should not be conflated.
PIPELINE_TIERS, effectivePipelineForTenant, and
TenantPipelineCustomization (in pipeline-registry.ts) implement per-tenant
customization, and pipelines/observability.ts provides the scheduling and SLA
layer: nextFireFor / PipelineSchedule compute the next run for tenant-scoped
publication cadences, buildPipelineObservabilityReport reports SLA / evidence
completeness / human-touch points per stage, and detectPipelineDeviation
routes deviations from the declared pipeline shape to operator review.
Implementation location — @oshun/agentic-studio, not @oshun/agent-pipelines#
The real pipeline specs live in libs/oshun/agentic-studio/src/pipelines/
(v1-pipelines.ts and pipeline-registry.ts). The package
@oshun/agent-pipelines (v0.1.0) is only a four-file re-export shim: its
src/index.ts re-exports ./grants/resolver and ./pipelines/index, both of
which simply export { ... } from '@oshun/agentic-studio'. It is a
compatibility surface, not an independent implementation — readers looking for
the pipeline logic should open @oshun/agentic-studio. (Companion-doc note: the
features-hub's reference to pipelines living "in libs/oshun/agent-pipelines/"
points at the shim, not the implementation.)
The Brief-to-Content Engine: @oshun/creative-orchestrator#
Adjacent to the Studio — and worth naming because it is real (58 passing tests)
yet undocumented in the broader feature set — is @oshun/creative-orchestrator
(v0.0.1, libs/oshun/creative-orchestrator, depends on @oshun/ai). Where the
Studio governs and accounts for runs, this is the actual brief →
produced-content engine. It exports decomposeBrief (a brief → schema-validated
CreativePlan DAG via CREATIVE_PLAN_SCHEMA), routePlan /
CreativeOrchestrator / orchestrateBrief (governed dispatch over the plan),
reviseArtifact (a Reflexion critique→revise loop), the critics
createMetricCritic / createLlmJudgeCritic / createContentEvalCritic,
domain generators createYemayaAgentGenerator / createMetisNarrator, the
governance gates BudgetGovernanceGate / ALLOW_ALL_GATE, and DAG utilities
validateDagStructure / detectCycle / topologicalOrder. It builds on
@oshun/ai/agent-loop's runStructuredOutput / runReflexion. As with the
rest of the autonomous-content story, the structured-LLM and domain-generation
calls are injectable seams that are fail-loud when no provider is wired — the
orchestration loop is real; live end-to-end generation is provider-gated.
Related#
- Tool Catalog, Grant Semantics, and Multi-Agent Protocol — the enumerated 21-tool catalog, grant scope/revocation cascade, and the hand-off protocol's budget/authority/cycle/depth rules.
- Agent Invocation, Budgets, Memory, and Feedback Loops — customer/creator invocation, the budget and kill-switch enforcement, modes, capability audits, and champion-challenger rollout.
- Sophia Grounding — the grounding pipeline behind
sophia.ground/sophia.fact_checkand the evidence trail. - Isis Generation Control — the fail-closed dispatch pattern that the agentic runtime seam mirrors.
- Lilith Persona Policy — the persona/tone bindings the registry declares.
- Admin Products — Web and Mobile and Tenant, Institution, and Operator Toolkit — where the operator and tenant agentic surfaces are exposed.
- Architecture, Platform Foundations, and Security — the durable-queue and infrastructure layers the Studio's in-memory runtime would bind to.
- Back to V1 Features hub.