Readonly[The runtime-driven brand the thread-Durable-Object signal wake requires (see
RUNTIME_DRIVEN_AGENT). A unique symbol field, so it cannot collide
with an inherited property and a plain Agent never carries it.
The member FlowsafeDurableAgent.__setMastra forwards to, which
Agent.listAgents calls directly on a static sub-agent.
Refuse declaring schedules. The base forwards them to the wrapped agent, which a Mastra schedule worker fires outside RunnerRuntime: the fourth ground reached by installation.
Signature caveat: the parameter is unknown rather than core's
DeclaredAgentSchedule[], which it satisfies without depending on method
bivariance.
Refuse Mastra registration. Mastra.addAgent calls this first, so the
refusal throws before the registry write and before the loop workflow is
added to that Mastra.
Signature caveat: the parameter is unknown rather than core's Mastra,
which it satisfies without depending on method bivariance.
Refuse memory installation after construction under the fifth ground.
Signature caveat: unknown accepts core's memory parameter without
depending on method bivariance.
Refuse pub/sub installation after construction under the fifth ground.
Signature caveat: unknown accepts core's pub/sub parameter without
depending on method bivariance.
Refuse the thread-runtime target swap. It sets one private field that the
thread-runtime paths resolve their target through, falling back to this;
the refusal in BLOCKED_RUN_ENTRIES names those paths, for the core it is
written against. So the containment those inherited members rely on is
virtual dispatch on this, and one call to this setter moves every run they
start, and their replay targets with them, onto an agent with none of these
overrides in the chain. That is the fourth ground reached by installing a
second execution surface rather than by calling one.
Signature caveat: the parameter is unknown rather than core's
Agent<any, any, any, any>. unknown satisfies that base without
depending on method bivariance.
Refuse direct run cancellation under the sixth ground.
Refuse direct thread cancellation under the sixth ground.
Refuse the network tool-approval resume. It is a one-line forward to FlowsafeDurableAgent.resumeNetwork; blocking it here closes the same door from the side a tool-approval caller reaches for.
Refuse Mastra's own tool-approval resume. Tool approval in FlowSafe is a
decided ApprovalRecord resumed through the approval-decision path, which
mints the leg's connector grant; approveToolCall() funnels into
resumeStream() -> resume() and mints nothing.
The 1.53.0 generate-shaped tool-approval entries. They funnel into
resumeGenerate() -> resume(), so they are the same entry point with a
different return type.
Signature caveat for each: the base method is GENERIC in its OUTPUT
type, which Parameters<> cannot carry, so the parameter type below is
hand-written rather than derived. Re-check it against the base on every
peer bump — nothing here fails if core changes the shape.
The decline half of FlowsafeDurableAgent.approveNetworkToolCall.
The decline half of FlowsafeDurableAgent.approveToolCall.
The decline half of FlowsafeDurableAgent.approveToolCallGenerate, including its hand-written-signature caveat.
ProtecteddeleteRefuse core's terminal snapshot cleanup. Its only call sites are the base
executeWorkflow (overridden here), the blocked resume() and the blocked
recover(), so nothing this class drives reaches it; blocking keeps the
snapshot rows — which deployment-scoped retention purge owns — from being
dropped out from under that owner by a future internal caller.
The override keeps core's best-effort deletion from taking ownership of these rows if an internal execution path reaches the cleanup method.
Refuse unscoped thread identity discovery under the second ground.
Optional_options: unknownProtectedexecuteDrive the durable-agentic-loop through RunnerRuntime instead of the base
createRun + run.start. stream()/generate() have already parked the
non-serializables (model/tools/messageList) on the in-process run registry
keyed by this runId, so the loop the runtime starts resolves them in-isolate
while the runtime mints the per-leg grant context. The runId guard here is
defense in depth — the public boundary (stream/generate) already enforced
the host-owned run-ID rule; this catches any future internal caller.
The same host-owned run-ID guard as FlowsafeDurableAgent.stream —
generate()
re-implements the durable setup and mints its own runId the same way when
one is not supplied.
Optionaloptions: DurableAgentStreamOptions<TOutput>Refuse the AI SDK v4 legacy execution path. generateLegacy() forwards
into AgentLegacyHandler, which converts and RUNS the agent's tools while
bypassing RunnerRuntime entirely, mints its own run id when the caller
omits one, and skips requireAgentExecutionFGA — the authorization gate
every SUPPORTED entry point calls, so this would run the agent without it.
(The network family skips that gate too; neither is unique in doing so.) It
persists no workflow run state, which is why it is refused on those
grounds rather than as a re-drive.
Signature caveat: overloaded and generic in OUTPUT on the base, so this signature is hand-derived and must be re-checked on every peer bump.
Optional_args: AgentGenerateOptions<undefined, JSONSchema7 | ZodSchema> & { output?: undefined; experimental_output?: JSONSchema7 | ZodSchema | undefined; model?: DynamicArgument<...> | undefined; }Refuse core's recovery discovery API. listActiveRuns() enumerates
listWorkflowRuns({ workflowName: 'durable-agentic-loop', status: 'running' }) narrowed by agentId plus the optional
threadId/resourceId filters the CALLER supplies, so it never consults
the host topology's per-principal run-ownership checks
(resourceAccess().owner('run', …)) and hands the caller run ids, thread
ids and resource ids for runs it does not own. Host run listing is the
topology's job, where ownership is checked.
Optional_options: DurableAgentListActiveRunsOptionsRefuse the thread-level run enumerator.
listActiveThreadRuns() takes no arguments and returns a runId plus the
resourceId and threadId parsed out of the key for every thread on the pubsub
instance with a run in flight, and core keys that state by pubsub instance
rather than by agent — so it is the same discovery ground as
FlowsafeDurableAgent.listActiveRuns with the last scoping gone.
The in-process sibling getActiveThreadRunId() stays inherited because it
makes the caller name the (resourceId, threadId) pair it confirms.
Signature caveat, and the reason this one is not async: the base declares
it SYNCHRONOUS (listActiveThreadRuns(): ActiveThreadRun[]), so an
async … Promise<never> refusal would not be assignable to it. A re-check
for a peer bump.
Refuse the Agent-level analogue of
FlowsafeDurableAgent.listActiveRuns. listSuspendedRuns() reads
the workflows store directly —
listWorkflowRuns({ workflowName: 'agentic-loop', status: 'suspended' })
— and narrows by agentId plus the optional threadId/resourceId
filters the CALLER supplies, the same scoping listActiveRuns() applies.
Same ground too: an unfiltered call returns run, thread and resource ids
across every principal that shares the agent.
Optional_options: AgentListSuspendedRunsOptionsRefuse the multi-agent network start. network() does not touch the
durable-agentic-loop at all: it compiles a SEPARATE workflow and drives it
with createRun + run.stream on the default engine, so the whole
collaboration — every sub-agent leg and every tool call inside it — runs
with no per-leg grant context, no snapshot provenance and no RunSummary.
It also mints options.runId || mastra.generateId() || randomUUID(), the
exact unowned fallback this runner forbids: the host mints every run id.
Signature caveat: the base method is OVERLOADED and generic in OUTPUT, so
Parameters<> sees only the LAST overload and is too narrow to satisfy the
first. The options parameter is therefore widened to unknown — the one
supertype that satisfies every overload at once. Re-check on every peer
bump; nothing here fails if core changes the shape.
Optional_options: unknownEnforce the caller-owned id before DurableAgent.prepare forwards it to prepareForDurableExecution, which mints an absent id and registers the run. That UUID satisfies downstream path checks and stays live until cleanup.
Optionaloptions: AgentExecutionOptions<TOutput>Refuse core's single-run recovery. DurableAgent.recover() loads the
persisted durable-agentic-loop snapshot, rebuilds model/memory/processors
from it and re-drives the run with createRun + run.restart() — a second
execution path that never enters executeWorkflow, so no leg of it is
grant-derived, run-owned or snapshot-provenanced. Throw BEFORE the storage
read, so a mistaken call cannot even enumerate a run.
Optional_options: DurableAgentRecoverOptions<TOutput>Refuse core's bulk recovery. recoverActiveRuns() is
FlowsafeDurableAgent.listActiveRuns plus a recover() per row, and
it is what Mastra.recoverAllDurableAgents() calls on every registered
durable agent — so a host reaches this blocked entry point without a
FlowSafe call site by opting into recovery: { durableAgents: 'auto' }.
That loop isolates each agent in its own try/catch, so this
refusal is logged there rather than failing boot. Refuse with an explicit
runId too: a single target is still a re-drive off RunnerRuntime.
Optional_options: DurableAgentRecoverActiveRunsOptionsRefuse core's durable resume. From 1.53.0 a registry MISS makes it load the
persisted durable-agentic-loop snapshot, rehydrate through prepare()
with the full application processor chain, and re-drive the run with
createRun + run.resume — below executeWorkflow, so the leg carries no
minted grant and no snapshot provenance. The runner's resume path is
FlowsafeDurableAgent.resumeViaRuntime.
Optional_options: DurableAgentResumeOptions<TOutput>Refuse the drain-to-completion resume. resumeGenerate() forwards straight
to resume(), so it inherits the same below-the-seam re-drive.
Optional_options: DurableAgentResumeOptions<TOutput>Refuse the network resume — same loop and same ground as
FlowsafeDurableAgent.network, plus one fact of its own: under
autoResumeSuspendedTools it RECOVERS a suspended run id out of thread
memory, so a caller need not even name the run it re-drives.
Refuse the base-Agent-shaped resume. Core overrides resumeStream() on
DurableAgent precisely so an Agent-API caller lands on the durable
resume(); blocking it here closes that same door from the other side.
Optional_streamOptions: anyRehydrate a suspended durable-agent run after isolate eviction, restore its
active thread registration, then resume through RunnerRuntime so approval
grant derivation and snapshot provenance remain authoritative.
A resumed leg observes from the current run stream position. Its thread
registration completes on the run's terminal outcome, or on a resume
failure after rehydration when the wrapper publishes a terminal error:
the run is no longer suspended, a cancellation or timeout refused or cut
the leg, or the run's status cannot be read. A failure that leaves the
run stored suspended with nothing recorded to end it keeps blocking the
thread until the next resume, as another suspension does.
Hosts expose this only from their trusted approval-decision topology, and
spread a decided approval's expectedSuspensionFor(record) into the
options, so a decision for an earlier suspension of the step is refused
with SuspensionChangedError; without it the resume is unchecked.
Refuse the thread-level tool approval. The name reads like a resume, but
only its tail is one. Called with messages and approved, it routes to
the thread runtime's continuation, which falls back to randomUUID() when
the caller named no run id and then STARTS a run under it — an unowned id
the host-owned run-id guard cannot distinguish from a real one, because it
is path-safe. Called with no active thread run, it reaches the blocked
suspended-run discovery instead. FlowSafe's own tool approval is a decided
ApprovalRecord resumed through the approval-decision path, which mints the
leg's grant; this mints nothing and owns nothing.
Signature caveat: the base method is generic in OUTPUT, which
Parameters<> instantiates to its undefined default and so types too
narrowly to satisfy the base. The options parameter is widened to
unknown so it fits each instantiation. Re-check on a peer bump.
Refuse binding channels. The base forwards them to the wrapped agent, which becomes their dispatch target outside RunnerRuntime: the fourth ground reached by installation.
Enforce a caller-minted run ID before the inherited durable
stream() runs: without this the
optional options.runId would let core mint an unowned
crypto.randomUUID() upstream. The private run-ID guard rejects a missing or
non-path-safe value before delegating to core.
A host mints an opaque path-safe id and passes it as options.runId.
Optionaloptions: DurableAgentStreamOptions<TOutput>The streaming half of FlowsafeDurableAgent.generateLegacy.
Optional_args: AgentStreamOptions<JSONSchema7 | ZodSchema | undefined, JSONSchema7 | ZodSchema | undefined> & { ...; }
A DurableAgent whose loop runs through RunnerRuntime rather than the base
createRun + run.start. Construct via createFlowsafeDurableAgent so the loop workflow is registered on the runtime.