Emergent capability configuration. Missing fields are filled from DEFAULT_EMERGENT_CONFIG.
Optional db: EmergentRegistryStorageAdapterOptional SQLite storage adapter. When provided, agent and
shared tier tools are persisted to the agentos_emergent_tools table.
When omitted, all tiers use in-memory storage only.
Idempotent schema readiness guard.
Ensures ensureSchema() is called exactly once and all subsequent callers
await the same in-flight promise. This prevents the race condition where
concurrent DB operations start before DDL statements finish.
A promise that resolves when the schema is ready.
Initialize the database schema for emergent tool persistence.
Creates the agentos_emergent_tools, agentos_emergent_audit_log,
agentos_emergent_tool_state and agentos_emergent_effects tables along with their indexes. Safe to
call multiple times — all statements use CREATE TABLE IF NOT EXISTS /
CREATE INDEX IF NOT EXISTS.
This method is a no-op when no storage adapter was provided.
If the storage adapter's exec or run method rejects.
Register a new emergent tool at the given tier.
Session-tier tools are stored in the in-memory session map and mirrored to SQLite when available. Agent and shared tier tools are stored in the persisted map (and written to SQLite when a storage adapter is available).
The emergent tool to register. Must have a unique id.
The tier to register the tool at. The tool's tier property
is updated to match.
If the maximum tool count for the target tier is exceeded
(checked against maxSessionTools or maxAgentTools from config).
If a tool with the same ID is already registered.
Retrieve a tool by its unique identifier.
Searches all tiers (session first, then persisted agent/shared).
The tool ID to look up.
The tool if found, or undefined if no tool with that ID exists.
The held state of a tool, or undefined when none has been recorded.
Record a tool's state, awaiting the write.
options.request left out leaves the stored request alone: a new row
stores none, and an existing row's request_json is not named by the
update, so a request this process could not read (one a newer release
wrote, say) is still there afterwards. A request writes it; null clears
it.
options.setBy records who set the state ('host' when left out). The
library re-checks only its own suspensions at the next load; a host's
stays until the host clears it, whatever words its reason uses.
Writes for one tool run in call order, one after another. The isActive
convention property of a tool held in memory follows the state.
A restriction (suspended, demoted) is held in memory at call time, so
the running process honours it whatever its write does. A reactivation
(active) is held only once both writes have succeeded, and only when no
other state change arrived while they ran: a suspension or demotion made
meanwhile is the newer word, it stays, and this call returns that record
instead of its own.
options.ifRow makes the write conditional: an existing row is changed
only while its state, setter and time are still the ones given, and
'absent' lets the write create a row but never change one. The loader
uses it for its active write, so a restriction another process stored
after the row was read is never written over; a refused write returns the
row's own record, and a restriction read that way is held here as well.
Every write, conditional or not, reads the row back after it: a state
another process stored meanwhile is what is returned and held, and a tool
another process removed reads as demoted with the reason removed.
options.yieldToHost makes the write give way to the host's word: a row
that holds a host's suspension, or a demotion, is not changed, and its
record is returned and held instead; so is an active row whose tool row
the host turned off with its own SQL (is_active = 0 with the mark
clear), which the caller records as the host's demotion. The library's
suspensions from a run or a promotion check use it, so they never replace
a restriction a host set, in this process or in another.
Optional request?: null | StoredRequestOptional setOptional ifOptional yieldthe record now in force for the tool.
If the storage adapter rejects.
Whether a restriction this process requested for a tool may be missing
from a row read that began at readAt (a beginRead point): its
write is still queued or running, or settled after the read began, or it
is a host's restriction whose write failed. A held restriction for which
none of these is true is older than the row the read saw, whatever the
clocks of the processes that wrote them say: the row decides.
Finish a state write whose flag write did not: the legacy flag from the state row, then the row's mark cleared, under the record the row holds so a newer write's mark is left alone. The loader calls it for a row whose mark is pending, before anything about the row is decided.
Suspend a tool on the host's word: an awaited write of its state and of
is_active = 0, recorded as the host's, so no load lifts it whatever the
reason says; only a reactivation does. Usage statistics are left alone.
The caller unregisters the executable.
If the tool is unknown or the write fails.
Drop a tool from memory without touching its rows: for a load whose registration with the host failed, so the row is there for the next load and nothing here claims a tool the executor does not run; and for a state held for a tool that has neither a row nor an object here any more.
End a read started with beginRead.
The writer of effect records over this registry's storage, or undefined without a storage adapter. The schema is made ready before its first write.
Optional retainWrite a tool's row from the object given, awaited. For a host that
hydrates a tool from its own store through syncPersistedTool and has no
row for it yet; a tool that has a row is loaded from the row, never
rewritten by a load.
Take a tool read from storage into memory without rewriting its row.
upsert re-serialises the source; a loaded tool must keep the row it has.
Optional ifUnchangedSince: numberThe point the caller's read started at (beginRead), or a generation reading. The adoption is refused when the tool changed in this process after it, or a removal or a rewrite of the tool's rows is still pending: the row the caller holds may then be one that is gone or about to change.
The stored rows of the given tiers, in creation order, with their state
rows: every shared row, the agent rows of scope.agentId and the
session rows of scope.sessionId. A tier named without its selector
contributes no rows; the engine refuses such a call before it gets here.
Optional agentOptional sessionOne stored row with its state row, or undefined.
A tool's state: the held one, else the stored one, else undefined.
Hold a stored state for a tool this process holds no object for: a load that finds a restriction already stored on a row it does not activate takes it in here, so listStates carries it (a registration of a composition's missing step tool re-checks the composition from it). Nothing is written.
Record the session a stored session-tier tool belongs to (its row's
created_by_session), before a state is held for it here: a load's
admission, or a host's restriction of a tool this process never loaded.
cleanupSession then lets go of a state held for it without the
tool, and deletes its rows.
Every state held in memory: the tools this process forged, loaded or suspended.
A tool's state as its row reads now, or undefined; what this process
holds is not consulted. The loader reads it before adopting a row it has
nothing to write for, since a restriction another process stored after
the row was read is the newer word.
Replace the in-memory copy of a tool and mirror it to storage.
The row is rewritten from the object given, in the tool's write queue.
To bring stored tools back after a restart, call the engine's
loadPersistedTools, which reads each row, checks it and never rewrites
it. A tool whose state this process does not hold keeps the is_active
its row has.
Get all tools registered at a specific tier, optionally filtered by scope.
The tier to query ('session', 'agent', or 'shared').
Optional scope: { Optional scope filter. When provided, results are narrowed:
sessionId: Match tools whose source string contains the session ID.agentId: Match tools whose createdBy equals the agent ID.Optional sessionOptional agentAn array of matching tools (may be empty).
Record a tool invocation, updating rolling usage statistics.
Updates the tool's ToolUsageStats in place:
totalUses.successCount or failureCount based on the success flag.avgExecutionTimeMs as a running average.confidenceScore as successCount / totalUses.lastUsedAt to the current ISO-8601 timestamp.The ID of the tool that was invoked.
The input arguments passed to the tool (logged for audit).
The output returned by the tool (logged for audit).
Whether the invocation completed successfully.
Wall-clock execution time in milliseconds.
false when the tool is suspended or demoted and nothing was recorded.
If no tool with the given ID is registered.
Retrieve usage statistics for a registered tool.
The tool ID to look up.
The tool's ToolUsageStats, or undefined if the tool
is not registered.
Promote a tool to a higher lifecycle tier.
Moves the tool from its current tier to targetTier. If the tool was at
session tier, it is removed from the session map and added to the persisted
map. If a storage adapter is available and the target tier is agent or
shared, the tool is persisted to the database. The promotion is a change
point, and its row write runs in the tool's write queue: an admission that
read the row before it reads the row again, and a removal that comes
after it deletes the promoted row.
The ID of the tool to promote.
The target tier to promote to. Must be strictly higher than the tool's current tier.
Optional approvedBy: stringOptional identifier of the human or system entity that approved the promotion.
If the tool is not found.
If targetTier is not higher than the tool's current tier.
Demote or deactivate a tool.
Marks the tool as inactive by setting a sentinel on its usage stats
(confidenceScore set to 0), records the demoted state and logs the
demotion event with a reason.
Inactive tools are still retrievable via get() but should be filtered
out by callers when building tool lists for the LLM.
Returns a promise for the state write, which rejects when the write fails; await it when the demotion must be durable. An un-awaited call cannot become an unhandled rejection. The in-memory effects have happened by the time this returns.
The ID of the tool to demote.
Human-readable explanation for why the tool is being demoted.
If the tool is not found (thrown synchronously).
Remove all session-tier tools associated with a specific session.
Iterates the session map and deletes every tool whose source names the
given session; the rows go after the tool's queued writes. Logs a cleanup
audit event for each removed tool. The session's stored tools whose state
this process holds without the tool (a load admitted them suspended or
demoted, or the host restricted one it never loaded) go too: the state
before this returns, so no registration from then on re-checks one and a
re-check already under way does not adopt it, and the rows after their
queued writes, while they are still the session's. Rows of the session
this process never admitted or restricted are left to the process that
holds them.
The session identifier to match against tool source
strings.
The number of live tools removed.
Retrieve audit log entries, optionally filtered by tool ID.
Optional toolId: stringWhen provided, only entries for this tool are returned.
An array of AuditEntry objects in chronological order.
Create a new EmergentToolRegistry.