Oshun Platform · Features

Tool Catalog, Grant Semantics, and Multi-Agent Protocol

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

14sections20 minread6tables

On this page

This page specifies the content of an agent's authority in the V1 Agentic Studio: the enumerated tool catalog, the per-tool grant scopes and revocation cascade, the multi-agent hand-off protocol, the runtime admission gate that makes those governance decisions act, and the concrete cross-domain pipeline patterns that compose them. It is the companion to Agent Registry, Job Orchestration, and Multi-Agent Plans (the families, lifecycle, and plan DAGs) and Agent Invocation, Budgets, Memory, and Feedback Loops (invocation surfaces, cost budgets, memory, and champion-challenger). Without the material here, an AgentRun is a black box — this is the layer that says exactly what a run is allowed to do, how that authority is granted and revoked, and how it is enforced when a tool is actually about to fire.

A reality note worth stating up front, in the spirit of the rest of these docs: the governance and orchestration brain is real, pure-TypeScript, and value-tested (271 tests across 16 files in @oshun/agentic-studio v0.1.0). The tool execution itself — the real web.fetch, sophia.ground, and so on — is injected at the application boundary and fail-closed by default. The catalog declares and validates authority; the deployable binds (or withholds) the real tool implementations. Where a piece is a data/query layer rather than a shipped admin UI, or is provider-gated, this page says so plainly.

Where this lives in the codebase#

Concern Module
Tool catalog (contract) libs/contracts/src/agent/tools.ts
Grant record (contract) libs/contracts/src/agent/tool-grants.ts
Grant resolver + revocation cascade libs/oshun/agentic-studio/src/grants/resolver.ts
Multi-agent hand-off protocol libs/oshun/agentic-studio/src/handoff/handoff-protocol.ts
Run envelope + statuses libs/oshun/agentic-studio/src/runs/agent-run.ts
Run controls libs/oshun/agentic-studio/src/runs/run-controls.ts
Runtime admission gate libs/oshun/agentic-studio/src/runs/executor.ts
Guarded dispatcher / orchestrator libs/oshun/agentic-studio/src/runs/dispatcher.ts, runs/orchestrator.ts
Kill switches libs/oshun/agentic-studio/src/budgets/kill-switch.ts
Budgets libs/oshun/agentic-studio/src/budgets/budgets.ts
Cost-quality modes libs/oshun/agentic-studio/src/modes/modes.ts
Tool isolation / SSRF / secrets libs/oshun/agentic-studio/src/capabilities/tool-isolation.ts
Adversarial test catalog libs/oshun/agentic-studio/src/capabilities/adversarial-tests.ts
Champion-challenger libs/oshun/agentic-studio/src/feedback/champion-challenger.ts
Pipelines libs/oshun/agentic-studio/src/pipelines/v1-pipelines.ts, pipeline-registry.ts, pipelines/observability.ts
Governed-run HTTP surface apps/oshun/bff/src/agentic/runs-route.ts

The package's barrel (libs/oshun/agentic-studio/src/index.ts) re-exports twelve subsystems: registry, runs, plans, dashboard, invocation, budgets, capabilities, feedback, modes, pipelines, grants, and handoff.

Tool catalog#

Every tool an agent can invoke is enumerated in a versioned, Zod-validated catalog (AgentToolCatalog) exported from libs/contracts/src/agent/tools.ts. The V1 catalog declares catalogVersion: '1.0.0', publishedAt: '2026-05-11T00:00:00Z', and exactly 21 entries (V1_TOOL_IDS). Additions go through the Agent Registry release-gate review.

The precise count matters: generate.image, generate.video, and generate.audio are three distinct entries; memory.read / memory.write and calendar.read / calendar.write are separate read/write entries. A prose-grouped reading can make the catalog look like "about seventeen" tools, but V1_TOOL_IDS.length === 21 is the contract.

Tool id Display name Required scope What it does / hard limits
web.fetch Web Fetch web.egress HTTP through the egress allowlist; SSRF-guarded (see below); per-request audit. Read-only network.
source.fetch Source Fetch sources.read Read a registered Sophia SourceSet entry by id; scope-checked against the run's source set; bumps a usage counter.
sophia.ground Sophia Ground sophia.ground Full retrieve → rank → ground → cite envelope (see Sophia Grounding); may return an ungrounded/abstained verdict rather than synthesizing.
sophia.fact_check Sophia Fact-Check sophia.fact_check Run the fact-check loop on a claim set.
memory.read Memory Read memory.read Iris recall within the run's declared memory scope.
memory.write Memory Write memory.write Iris write; sensitive categories gated by explicit consent.
persona.invoke Persona Invoke persona.invoke Render under a registered persona/voice; Lilith policy applies; cannot widen the run's tone-band ceiling.
generate.image Image Generate generate.image Dispatch through Isis under the run's audacity ceiling; provenance bundle returned.
generate.video Video Generate generate.video As above for video.
generate.audio Audio Generate generate.audio As above for audio/narration.
composition.suggest Composition Suggest composition.suggest Living-Scenes Compose Assist over an artifact set; ranked techniques + rationale.
themis.adjudicate Themis Adjudicate themis.adjudicate Submit an evidence pack for a Themis verdict (Metis academic-integrity surface).
code.exec Code Execute code.exec Sandboxed code execution.
file.read File Read file.read Read a tenant/customer-scoped file by id.
file.write File Write file.write Write into the run's output bundle (never directly into a user-facing surface; publication is a separate gate).
calendar.read Calendar Read calendar.read Read calendar entries within consent scope.
calendar.write Calendar Write calendar.write Write calendar entries within consent scope.
notify.send Notify Send notify.send Send through a delivery channel adapter.
handoff Hand-off handoff Dispatch a sub-agent with bounded budget and authority (see Multi-agent protocol).
approval.request Approval Request approval.request Pause the run pending human approval.
agent.terminate Agent Terminate agent.terminate Terminate the current run (or a sub-agent).

Each AgentToolCatalogEntry carries more than an id: a semver (the V1 set is uniformly 1.0.0), a capabilityModifiers descriptor (schema name and JSON-schema shape — the V1 entries declare the empty NoModifiers descriptor, with the real per-tool modifier schemas registered in the runtime), an array of scopeRequirements (each with scope, required, and a rationale string for the audit trail), at least one auditHook (e.g., audit.web.fetch capturing the args key), a dependencies array of (toolId, semverRange) pairs, and a nullable deprecation record.

Tool versioning and deprecation surfacing#

The catalog backs the "tool versioning / deprecations surfaced at registry-review time" claim with real code, not prose:

  • resolveToolDependencies(catalog, rootToolId) walks the dependency graph with a genuine semver-range matcher (semverInRange) supporting ^, ~, >=, <=, =, and bare-exact ranges, including the caret's 0.x special cases. It returns either the resolved entry set or the first unsatisfied dependency, with a typed reason: unknown-tool or semver-range-no-match (plus the offending toolId and requestedRange).
  • surfaceDeprecations({ catalog, dependentToolsByToolId, nowUnixSeconds, reviewWindowSeconds }) scans for entries whose deprecation.deprecatedAt falls within the review window and returns, per tool, the deprecation date, the replacementToolId, and the dependent tool ids that still rely on it — the payload an Agent Registry reviewer sees before approving a release.

Grant scope and revocation cascade#

Tool grants are scoped, time-bound, and revocable. Crucially, the grant vocabulary is the same vocabulary the AgentRunEnvelope uses, so audit logs and operator surfaces speak one language.

The grant record#

AgentToolGrantSchema (libs/contracts/src/agent/tool-grants.ts) defines a grant as:

json
{
  "grantId": "grant-…",
  "agentId": "veritas.fact-checker",
  "toolId": "sophia.fact_check",
  "scope": "per-pipeline-instance",
  "scopeRef": "pipeline-instance-abc",
  "capabilityModifiers": { "egressDomains": ["*.un.org"] },
  "expiresAt": "2026-06-30T00:00:00Z",
  "grantedBy": { "actorId": "op-7", "actorRole": "platform-operator" },
  "rationale": "weekly briefing pack run",
  "grantedAt": "2026-06-23T00:00:00Z",
  "revokedAt": null,
  "revokedByActorId": null
}

The capabilityModifiers field is per-tool and free-form: for web.fetch it carries the egress-domain allowlist; for generate.image the audacity ceiling; for memory.write the allowed memory scopes. The grantedBy.actorRole is constrained to platform-operator | tenant-operator | creator | customer.

Scopes and specificity#

GrantScopeSchema enumerates exactly five scopes. Each grant carries exactly one. When a tool call is evaluated, the resolver picks the most specific live grant by a fixed priority (GRANT_SCOPE_PRIORITY in grants/resolver.ts):

Scope Priority Lifetime / scopeRef matches
per-run 100 A single AgentRun; scopeRef === runId. Expires at terminal state.
per-pipeline-instance 80 A cross-domain pipeline run; scopeRef === pipelineInstanceId. Narrower than per-user, broader than per-run.
per-session 60 A logical session (a Studio authoring session, a Metis tutor session); scopeRef === sessionId.
per-user 40 Durable for a user account; scopeRef === userId.
per-tenant 20 Durable for a tenant; scopeRef === tenantId. Until revoked.

The resolver (resolveGrant) is a per-call authorization step: it filters grants to the matching (agentId, toolId) and a matching scope ref, drops any that are revoked or expired, and then reduces the survivors to the highest-priority one (ties broken by most-recent grantedAtUnixSeconds). When nothing remains, it returns a typed GrantDenialReason: no-matching-grant, all-matches-expired, or all-matches-revoked — distinguishing "you never had this" from "your grant lapsed" from "it was pulled".

Revocation, expiry, and the cascade#

  • Queued runs re-check at dispatch. recheckQueuedRunAtDispatch re-runs resolveGrant immediately before a queued run starts, so a grant revoked or expired while the run waited is enforced at the boundary — a queued run never inherits a stale "allowed" decision.
  • In-flight cascade. emitRevocationCascade performs a breadth-first walk of active runs: every run whose grantsInUse includes the revoked grant gets a ToolRevokedEvent, and every sub-agent (parentRunId chain) inherits the revocation transitively, with cascadeDepth recorded. Runs cannot evade revocation by re-issuing the same call within a single invocation.
  • Expiry is revocation. emitExpiryCascade finds grants expiring within a horizon and routes each through the same cascade machinery (with revokedByActorId: 'system.expiry'). Time-bound and operator-pulled authority are treated identically.
  • Renewal requires fresh rationale and audit. renewGrant refuses with rationale-required if the rationale is blank and audit-required if no audit event id is bound; on success it mints a new grantId rather than silently extending the old authority.
  • Kill-switch cascade. emitToolKillSwitchCascade propagates a per-tool kill switch (platform-wide when tenantId === null, else tenant-scoped) across all in-flight runs using the tool, again BFS over sub-agents. Each emitted KillSwitchPropagationEvent records propagationLatencySeconds and a withinFiveSecondSlo boolean (latency <= 5) — the data backing the "platform kill propagates ≤ 5 s" claim.

Kill switches and the scope hierarchy#

Kill switches (budgets/kill-switch.ts) are the safety primitive that overrides everything else. KILL_SWITCH_SCOPES are agent, family, tenant, provider, region, tool, and global. decideExecution(switches, target) collects every active switch that matches an ExecutionTarget (tenant / agent / family / provider / region / tool) and, when more than one is active, "blames" the broadest one for the user-visible copy by this priority:

text
global 6 > region 5 > provider 4 > tenant 3 > family 2 > agent 1 > tool 0

The blamed switch supplies the userVisibleStatusCopy and an optional safeDegradationModeId so a tripped switch can degrade to a safe mode rather than simply erroring.

The runtime admission gate — where decisions act#

The audit found a sharp distinction worth naming: the budget, kill-switch, and throttle modules each decide (a pure verdict), but for that to mean anything something must consume those verdicts at a tool-call boundary and actually stop a run. That seam is runs/executor.tsruns/dispatcher.tsruns/orchestrator.ts, and it is the concrete enforcement story the higher-level docs gesture at.

  • admitToolCall(input) (executor.ts) runs the checks in safety → cost → rate order:
    1. Kill switch (decideExecution) — if any switch is active, the run is killed (terminal killed), with the operator's status copy and an audit event. This is the check that honors the ≤ 5 s propagation budget.
    2. Budget (checkBudget) — an exceeded verdict is a hard stop (terminal killed); warn / grace verdicts admit the call but surface as BudgetWarnings (degradation/notification is policy, not a hard stop).
    3. Rate (acquireToken) — a failed token acquisition is back-pressure: the call is deferred with a retryAfterSeconds; the run is not terminated.
    4. Otherwise it returns admit with the token-deducted throttle state and any budget warnings. The executor never invokes the tool — it is purely the enforcement primitive.
  • dispatchGuardedToolCall(input, runTool) (dispatcher.ts) calls admitToolCall and invokes the injected runTool only on admit. A kill switch or exhausted budget terminates the run and the tool never runs; a throttle defers; a terminal run refuses.
  • runGuardedToolPlan({ run, plan, killSwitches, budgets, … }) (orchestrator.ts) threads the envelope through a sequence of PlannedToolCalls; the moment a call is killed or throttled, the orchestrator stops and the remaining planned calls never execute.

The governed-run HTTP surface#

POST /v1/agentic/runs/execute (apps/oshun/bff/src/agentic/runs-route.ts) mounts the guard as the deployable's run surface. The design choices are the honest, fail-closed ones:

  • Fail-closed by default. The real agent tools are injected at the app boundary via an AgentToolRegistry (toolId → operation). The default is notConfiguredAgentToolRegistry() — an empty map — so any plan referencing an unconfigured tool returns 503 agent_tools_not_configured. The gate is real; the tools are bound by the deployable. With nothing configured, the route never fabricates a run.
  • Server-authoritative identity. The actor is the authenticated principal and the tenant comes only from the verified auth claim (authContext.tenantId) — a body-supplied tenant would let a caller dodge tenant-scoped kill switches.
  • Malformed → 400. A missing runId / tenantId / rootAgentId, or an empty / non-{toolId} plan, is rejected.
  • Scope checks (S7) → 403 tool_scope_missing. A tool can declare an AgentToolScopeCheck over the caller's real scopes; the executor never fabricates an operator tier for whoever shows up.
  • Server-side target metadata (S8). Kill switches match on family/provider/region, which come from server-side toolTargets keyed by tool — never from the request body — so an armed family switch cannot be evaded by omitting a field. Tools without metadata get 'default'.
  • Run-ownership (S9) → 409 run_id_conflict. A runId already persisted by another member is never overwritable.
  • Operator switches read every request. resolveOperatorKillSwitches() reads the admin agentic-operations store on every request (the arm → next boundary ≤ 5 s budget); client-supplied switches and budgets are merged in addition — a request can tighten governance but never loosen it.

The admin operator surface for these controls is a set of API routes, not a rich React dashboard: apps/oshun/admin/src/app/api/admin/agentic-operations/ exposes kill-switches, snapshot, gold-sets/promote, and champion-challengers/rollout. The only agent page in the product is apps/oshun/tenant-admin/src/app/agents/page.tsx. The "operator dashboard for replay and audit" is a data/query layer (dashboard/dashboard-query.ts, runs/replay.ts, runs/streaming-progress.ts), not a shipped admin dashboard page — see Output Gallery, Lineage, Branch, and Replay.

Run statuses, the cost ledger, and run controls#

A note on the agent.terminate outcome vocabulary: the implemented terminal set (TERMINAL_STATUSES) is completed, failed, cancelled, killed. There is no declined status; the run controls expose cancel (operator-deliberate) and kill (governance-enforced), not a decline. Treat any declined outcome in older prose as drift toward cancelled.

The full RUN_STATUSES enum (runs/agent-run.ts) is:

text
queued · starting · running · paused · awaiting_approval ·
awaiting_tool · awaiting_branch_selection · cancelled · killed · failed · completed

(Any simplified lifecycle diagram using pending / planning / executing is aspirational — those tokens are not in the real enum, which has no planning or executing state and adds paused, awaiting_tool, and awaiting_branch_selection.)

The AgentRunEnvelope carries runId, rootAgentId, parentRunId, pipelineId, the plan (a RunPlanStep[] DAG), toolCalls, intermediateArtifacts, evidenceTrail, the costLedger, decisionRationale, outputs, and a provenanceBundleId. The RunCostLedger is denominated for cross-provider comparability and tracks totalCostUnits, per-stage and per-tool cost maps (perStageCostUnits / perToolCostUnits), inputTokenCount, outputTokenCount, gpuMinutes, voiceSeconds, avatarSeconds, retrievalCount, and externalCallCount.

Run controls (run-controls.ts) are pure (envelope, command) → result functions emitting typed audit events: pause, resume, cancel, kill, chooseBranch, fork (requires a fresh runId — re-using the source id is rejected with fork-without-fresh-id), retryWithChanges (rewind to a step, mark it and all later steps pending, apply overrides, restart), and operatorStep (advance exactly one pending step for step-through debugging). The corresponding audit kinds are pause / resume / cancel / kill / branch-chosen / fork / retry-with-changes / operator-step.

Persistence candor: in V1 the runtime state is in-memory — the AgentRegistry is a Map, and the BFF run-lifecycle store is an in-process store. @oshun/agentic-studio does not import @oshun/queue; its runs (dispatcher, orchestrator, checkpoint, replay) are in-process, pure-function logic with no DLQ or SLA monitor in this library. The durability/priority-class/DLQ story (the shared queue lib lives at libs/shared/queue) is not wired into agentic-studio yet — see Agent Registry, Job Orchestration, and Multi-Agent Plans and §18.

Multi-agent hand-off protocol#

Multi-agent pipelines are valuable but dangerous; the protocol in handoff/handoff-protocol.ts controls budget inheritance, authority delegation, cycle prevention, and audit completeness so a "Veritas story drafting" agent that dispatches Sophia retrievers and Isis generators cannot silently spin into a runaway run.

A parent dispatches via a HandoffDispatch carrying the sub-run ids, the HandoffBudgetEnvelope (costUnits), the AuthorityEnvelope (delegableToolIds, maxFanoutDegree, capabilityModifiers), the approvalRequirement, the parentTrace (ordered ancestor agent ids, excluding the sub-agent), and an allowCycle flag.

validateDispatch enforces, in order, with a typed DispatchDenialReason:

Check Denial reason Rule
Cycle cycle-detected The sub-agent id may not appear in parentTrace unless allowCycle is set.
Depth depth-cap-exceeded parentTrace.length >= cap; DEFAULT_DEPTH_CAP = 5, overridable per pipeline.
Authority — tools authority-widening-rejected Every delegableToolId must be in the parent's set (a subset, never a superset).
Authority — fanout authority-widening-rejected maxFanoutDegree may not exceed the parent's.
Authority — modifiers authority-widening-rejected capabilityModifiers must be narrower (numbers <=, arrays subset, strings equal, objects recursively narrower).
Budget — positivity budget-non-positive The sub-budget must be finite and > 0.
Budget — envelope budget-exceeds-parent The sub-budget may not exceed the parent's remaining budget.

deductDispatchBudget validates and then subtracts the sub-budget from the parent's remaining envelope. On terminal state, reconcileTerminalBudget returns the unused budget to the parent (returnedToParent) when there is no overshoot, and emits an overshootEvent of kind subagent_budget_exceeded (and shouldKillSubagentForBudget returns true) when the sub-agent overran.

Approval requirements (APPROVAL_REQUIREMENTS) are none, operator, teacher, guardian, and user-confirm-before-proceed. initialSubagentRunStatus puts a sub-agent into running only when the requirement is none; otherwise it enters awaiting_approval. approvalRolePermitted maps each requirement to the roles that can satisfy it (e.g., operator accepts platform-operator / tenant-operator; user-confirm-before-proceed accepts customer / creator).

Audit and attribution. recordDispatchAudit and recordTerminalAudit emit a HandoffAuditEvent of kind dispatch (target, sub-run, granted budget, authority) and terminal (terminal state, budget consumed, produced artifact ids). deriveAttributionChain stamps every artifact with its full [...parentTrace, producingAgentId] provenance chain — reader-facing surfaces can collapse it to "agent assisted", but the bundle preserves the path.

Cost-quality modes and their concrete thresholds#

modes/modes.ts defines five customer/creator-selectable COST_QUALITY_MODES, each with declared, value-tested thresholds (not qualitative hand-waving):

Mode maxCostUnits p95 latency (ms) min cite density /1k words min eval score multi-agent human checkpoints external side effects
fast 50 1 500 0 0.6 no 0 yes
balanced 500 10 000 2 0.7 no 0 yes
deep 5 000 120 000 4 0.8 yes 1 yes
exhaustive 50 000 1 800 000 6 0.85 yes 3 yes
rehearsal-dry-run 1 000 60 000 0 0 yes 0 no

checkModeCompliance verifies an actual run against its mode and returns typed violations: cost-exceeded, latency-exceeded, citation-density-below-floor, evaluation-score-below-floor, and (for rehearsals) rehearsal-side-effects-detected. applyOverride is tighten-only: a tenant-operator or customer may narrow the mode within the domain policy's allowedModes, but a cost-raise-rejected is returned if the requested mode's cost cap exceeds the current one. The rehearsal-dry-run profile's hasExternalSideEffects: false is what lets a pipeline rehearse against fixtures with no real publication, generation, or notification.

Tool isolation, SSRF guards, and secret scoping#

capabilities/tool-isolation.ts backs web.fetch's "SSRF guards" with real network reasoning rather than a comment:

  • isPrivateOrLoopbackHost rejects loopback (localhost, 127.0.0.0/8, ::1, 0.0.0.0, ::), link-local (169.254.0.0/16, fe80:), IPv6 unique-local (fc00::/7), and the RFC-1918 private ranges via integer CIDR bounds: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16. IPv4-mapped IPv6 (::ffff:…) is unwrapped and re-checked.
  • matchHostPattern supports exact hosts and *.example.com subdomain wildcards (normalizing case and bracketed IPv6).
  • checkEgressRequest layers the checks: invalid URL → invalid-url; SSRF host → the matching SSRF reason; not in the allowlist → host-not-in-allowlist; wrong scheme → scheme-not-allowed; wrong method → method-not-allowed. The EgressDenialReason set is the typed vocabulary.
  • checkSecretAccess scopes secrets to permitted agents and tools within a tenant, returning cross-tenant, agent-not-permitted, or tool-not-permitted.

Adversarial security-test catalog#

capabilities/adversarial-tests.ts ships the concrete probe taxonomy (ADVERSARIAL_CATEGORIES): prompt-injection, tool-call-exfiltration, scope-escalation, sandbox-escape, persona-bypass, policy-bypass, and source-fabrication. Each AdversarialFixture carries an expectedVerdict (refuse | sanitize | comply-and-disclose) and a severity; evaluateFixtureResult grades an observed result as safe | suspected | failure (an observed leak is always a failure), and buildSuiteReport rolls up per-category counts with a worst-severity into an overallVerdict of pass | review | block — the release-gate signal.

Champion-challenger rollout (statistical readiness)#

feedback/champion-challenger.ts is genuinely statistical, not a toy. evaluateReadiness requires MIN_SAMPLES = 100, then runs a two-proportion z-test on task-success rates, with the two-tailed p-value computed from an Abramowitz & Stegun 26.2.17 normal survival-function approximation (normalSf). It returns a ReadinessReport (zScore, pValue, latency and cost deltas) and a verdict of not-enough-samples, inconclusive, challenger-worse, or challenger-ready (the last two requiring pValue <= P_THRESHOLD = 0.05). Rollouts run in shadow (challenger never returned to the user) or canary (routeInvocation routes a canaryFraction of traffic). recordTransition gates the ROLLOUT_TRANSITIONSpromote-challenger-to-champion (refused readiness-required unless the verdict is challenger-ready), freeze-rollout, and rollback-to-prior-champion — and requires a rationale on every transition.

Cross-domain pipeline pattern reference#

V1 ships a small set of named cross-domain autonomous pipelines. The implementation note in the source docs points at libs/oshun/agent-pipelines/, but that package (@oshun/agent-pipelines v0.1.0) is only a four-file re-export shim whose src/index.ts re-exports grants/resolver and pipelines/index straight from @oshun/agentic-studio. The real pipeline specs live in libs/oshun/agentic-studio/src/pipelines/v1-pipelines.ts, and the PipelineRegistry class lives in pipelines/pipeline-registry.ts.

V1_PIPELINES contains seven specs, all with lifecycleState: 'live'. A reader should note that Veritas has two distinct pipelines — the source docs list them in different sections and never reconcile that, but the code is right: veritas.weekly_briefing_pack (creator-tier) and veritas.story_drafting (platform-operator-tier) are separate specs (the source file flags the overlap in a comment).

pipelineId Invocation tier (PIPELINE_TIERS) Budget cap Hand-off depth Mandatory approval gate(s) Provenance
veritas.weekly_briefing_pack creator 5 000 3 editor.review standard
veritas.story_drafting platform-operator 12 000 4 editorial.inbox.handoff extended
tara.seasonal_program platform-operator 10 000 2 tara.lineage.confirm, editor.review extended
nisaba.edition_study_guide customer 3 000 2 scholar.review standard
nyx.event_explainer_set tenant-operator 2 500 2 nyx.desk.review standard
arete.weekly_review_draft customer 500 2 user.confirm.before.publish minimal
metis.course_from_byom customer 20 000 4 themis.prescreen, tenant.teacher.approval extended

Each PipelineSpec declares its participatingAgentIds, the plan (a stage DAG), expectedHandoffDepth, totalBudgetCap, mandatoryApprovalGateIds, expectedOutputSchemaNames, provenanceShape, an evaluationFixtureSetId, and a lifecycleState. Some concrete stage flows:

  • veritas.story_drafting (platform-operator only): source-enumeration → ingestion-verification → claim-extraction → fact-check → contradiction-check → counterclaim-generation → lilith-tone-review → editorial-inbox-handoff.
  • metis.course_from_byom (customer, under tenant policy): byom-safety-scan → ingestion → objective-extraction → kg-anchor → prerequisite-chain → item-bank → calibration → themis-prescreen → tenant-teacher-approval (see Metis — Education and Tutoring).
  • tara.seasonal_program leads with lineage-rights-confirmation and a Lilith strict tone check; arete.weekly_review_draft runs only against the user's own consented data and ends with user-confirm-before-publish (see Arete — Goals, Habits, and Reflection).

Pipeline scheduling, customization, and observability#

The "tenant-scoped pipeline scheduling" and "pipeline observability" lines have real backing symbols:

  • SchedulingPipelineSchedule and nextFireFor(schedule, nowUnixSeconds) (pipelines/observability.ts) compute the next fire time for a cadence.
  • Tenant customizationTenantPipelineCustomization and effectivePipelineForTenant({ base, customization }) (pipelines/pipeline-registry.ts) resolve a tenant's effective pipeline from the base spec plus its overrides; PIPELINE_TIERS enumerates the invocation tiers.
  • ObservabilitybuildPipelineObservabilityReport produces SLA-per-stage, evidence-completeness, and human-touch-point measures from PipelineStageObservations; detectPipelineDeviation flags runs whose shape drifts from the declared pipeline and routes them to operator review.

The autonomous creative orchestrator (@oshun/creative-orchestrator)#

Distinct from agentic-studio (which is the governance/orchestration brain), libs/oshun/creative-orchestrator (@oshun/creative-orchestrator v0.0.1, depends on @oshun/ai) is the brief → produced-content engine. It is fully real (58 passing tests) and built on @oshun/ai/agent-loop's runStructuredOutput and runReflexion, yet is undocumented elsewhere in the V1 feature docs. Its exported surface:

  • decomposeBrief (with CREATIVE_PLAN_SCHEMA) — turns a brief into a schema-validated, acyclic CreativePlan DAG (validated by validateDagStructure / detectCycle / topologicalOrder).
  • routePlan / CreativeOrchestrator / orchestrateBrief — governed plan → domain-generator dispatch through a GeneratorRegistry.
  • reviseArtifact — a bounded generate → critique → revise (Reflexion) loop around every artifact, with critics createMetricCritic, createLlmJudgeCritic, and createContentEvalCritic (a non-provider-gated default judge).
  • BudgetGovernanceGate / ALLOW_ALL_GATE — the governance seam over dispatch.
  • createYemayaAgentGenerator / createMetisNarrator — domain generator adapters.

Like the rest of the autonomous-content story, the model and provider boundaries are injectable and fail-loud — the orchestrator never fabricates a plan or an artifact; with no provider wired, it refuses rather than inventing output.

Tests#

The protocol and catalog behaviors above are value-tested (271 tests, 16 files in @oshun/agentic-studio):

  • Tool catalog — every V1 entry round-trips through the contract; semver dependency resolution against registered tools.
  • Grant scope — per-scope revocation propagation across in-flight runs and sub-agents within the documented latency budget (the withinFiveSecondSlo flag).
  • Multi-agent budget inheritance — parent plus sub-budget sum within the declared envelope; unused returns; overshoots emit subagent_budget_exceeded.
  • Authority envelope — widening rejected at dispatch; narrowing honored; capability modifiers propagated narrower.
  • Cycle prevention — re-entering an ancestor agentId blocked unless allowCycle is set at the root; depth cap honored.
  • Hand-off audit — dispatch / terminal / attribution records complete.
  • Cross-domain pipeline fixtures — each named pipeline produces the expected output shape on its declared fixture set; approval gates pause correctly.
  • Kill-switch propagation — platform-wide tool kill propagates to all in-flight runs (≤ 5 s budget); tenant-scoped kill is confined to that tenant.
  • Runtime gateadmitToolCall / dispatchGuardedToolCall / runGuardedToolPlan execute a tool only when admitted; a kill switch or exhausted budget stops the plan mid-flight.

See §18 in ../TODOS.md for the agentic backlog, and the agentic-pipeline-customer-invocation end-to-end walkthrough (classified "deep", with a creds-bound tool DAG) for the live-provider boundary.