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.
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 pair. They funnel into
resumeGenerate() -> resume(), so they are the same entry point wearing a
different return type.
Signature caveat for both halves: 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.
Note what this override buys beyond that call-site audit: the member inventory in durable-agent-surface.test.ts sees a NEW member, never a new core call site on an EXISTING one. So the override is the standing guard — it converts core's best-effort cleanup into a throw the moment a future release calls it on a path FlowSafe drives. None does at 1.53.0.
ProtectedexecuteDrive 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 two
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, any> & { output?: undefined; experimental_output?: any; model?: DynamicArgument<MastraModelConfig> | 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 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. It is the one direct
data-returning discovery member the base Agent surface adds; the other
additions funnel through this.resumeStream() and so fail closed on that
override.
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: unknownThe same host-owned run-ID guard as FlowsafeDurableAgent.stream.
prepare()
is the third inherited minting entry point: it forwards options?.runId into
core's prepareForDurableExecution (agent/durable/index.js:5980), which mints
an unowned crypto.randomUUID() when it is absent
(agent/durable/index.js:589) AND REGISTERS a run under that id
(agent/durable/index.js:5984 — all three are 1.50.0-vintage offsets, since
that file is a re-export shim from 1.53.0) — so a later
resume(runId)/executeWorkflow sees a bare UUID PATH_SAFE_ID_PATTERN
already accepts, past every downstream guard. Enforce the caller-minted ID
here, while "absent" is still visible. A prepared id remains live until core
cleans up the run, so later stream(), generate(), prepare(), and
streamUntilPersisted() calls carrying that id are refused.
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 this is the one blocked entry point a host can reach
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. Until 1.53.0 this merely read the in-process
run registry, which is why it was left inherited-but-unwired; now 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.
FlowsafeDurableAgent.resumeViaRuntime is the only resume path, and
it is reached from the approval decision, never from a client.
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. Hosts expose this only from their trusted approval-decision topology.
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 — the one supertype that fits every instantiation. Re-check on
every peer bump.
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: any
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.