Oshun Platform · Features

Agent Registry, Job Orchestration, and Multi-Agent Plans

A focused page within the Oshun Platform Features documentation. The full map and every sibling page live in the Features hub.

9sections22 minread5tables

On this page

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.tsregistry, 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 (admitToolCalldispatchGuardedToolCallrunGuardedToolPlan).
  • In-memory / in-process, not durable. The persistence is deliberately a data-and-logic layer, not yet wired to durable infrastructure. AgentRegistry is backed by a Map<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 at libs/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 (a RunCheckpoint snapshots envelope state plus an artifact manifest with persistedKeys 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 under apps/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 is apps/oshun/tenant-admin/src/app/agents/page.tsx. (Companion-doc note: ARCHITECTURE.md's reference to an operator dashboard at apps/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 toolGrantCeilingvalidateEntry 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 — the agentId is already registered.
  • unknown-familyfamily is not in AGENT_FAMILIES.
  • tool-grant-exceeds-family-ceiling — a declared tool is outside the family's toolGrantCeiling (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 in DATA_SCOPES.
  • lifecycle-experimental-mismatch — an experimental agent claims the champion lifecycle state (an experiment cannot be the production champion).
  • duplicate-capability-id — two capabilities share a capabilityId.
  • champion-without-disclosure — a champion agent declares no disclosureRequirements (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:

  • RunToolCallRecordcallId, toolId and toolSemver, an argsHash (never raw args), start/complete timestamps, an outcome (pending | ok | error | denied | cancelled), costUnits, latencyMs, a scopeCheckResultId, an auditEventId, and emittedArtifactIds. The argsHash and result-redaction discipline means transcripts are auditable without leaking payloads.
  • RunEvidenceTrailEntry — per stage, the evidencePackId, a groundingState (grounded | partial | ungrounded | abstained | retracted-source), and a citationCount. This is how a run proves it stayed grounded (see Sophia Grounding).
  • RunDecisionRationale — per stage, a free-text rationale plus a decisionLabel (plan-step-completed | plan-branch-chosen | plan-replanned | gate-passed | gate-failed | tool-selected | tool-skipped | abstained) and an optional evidencePackId. An agent's why is first-class, not reconstructed.
  • RunPlanStepstepId / stageId, the bound agentId and agentSemver, a status (pending | running | completed | failed | skipped), timestamps, and dependsOnStepIds.

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):

text
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:

  • takeCheckpoint snapshots the envelope into a RunCheckpoint: a checkpointId, the checkpointedAfterStepId (the last completed step), an envelopeSnapshotFingerprint, the artifactManifest (the intermediate artifacts, each with a persistedKey reachable from a side object store), and a resumeBudgetUnits.
  • resumeFromCheckpoint returns the envelope with completed steps preserved, every non-completed step reset to pending, and status set to queued. It refuses with unknown-checkpoint if the checkpoint's runId does not match, or fingerprint-mismatch if 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.
  • preservePartialOutputs captures whatever artifacts a failed, cancelled, or killed run produced into a partial-on-failure output 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/:

  1. 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 a safeDegradationModeId. An exhausted budget (checkBudgetexceeded) likewise terminates; warn/grace verdicts admit the call but surface as BudgetWarnings. A failed token acquire (acquireToken) is back-pressure: the call is throttled with a retryAfterSeconds and the run is not terminated. Otherwise the call is admitted.
  2. dispatchGuardedToolCall (runs/dispatcher.ts) wraps the injected tool function so that runTool executes 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.
  3. runGuardedToolPlan (runs/orchestrator.ts) threads the envelope through a sequence of PlannedToolCalls; 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 planned toolId must be a registered tool; with no tools configured (notConfiguredAgentToolRegistry returns {}), every plan is refused. The route never fabricates a run.
  • 400 invalid_request — missing runId / tenantId / rootAgentId, or an empty/malformed plan.
  • 403 tool_scope_missing — a tool with a toolScopeChecks requirement runs only for callers whose real authContext.scopes satisfy it; the executor never invents an operator tier for whoever shows up.
  • 409 run_id_conflict — a runId already persisted by another member is never overwritable; re-execution would replace their envelope and approval history.
  • Server-authoritative identity — the actorId is the authenticated principal and the tenantId comes 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 / region come from server-side toolTargets metadata, 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 adminAgenticOperationsStore on every request and mapped via toExecutionKillSwitch, 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_selectionblocked). 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.ts source flags the overlap itself. VERITAS_WEEKLY_BRIEFING_PACK is a creator-tier pipeline that aggregates the week's stories into a multi-format briefing pack; VERITAS_STORY_DRAFTING is 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.