FunctionManager
maintains the catalog of everything the actor can execute. Its abstract
contract lives in
base.py
(BaseFunctionManager), with a simulated in-memory twin in
simulated.py
for tests.
The Function model
Defined in
types/function.py.
There is no function_type enum — primitive vs. compositional is expressed
by storage context plus the is_primitive flag. Fields worth knowing:
Storage contexts
Four contexts, wired with foreign keys and auto-counting inFunctionManager.Config.required_contexts:
Functions/Compositional— user/agent-authored functions (auto-incrementfunction_id).Functions/Primitives— per-assistant rows for provider-backed integration tools only; static platform primitives live in the global read-only builtins catalogue instead.Functions/VirtualEnvs—VirtualEnvrows; many functions can share onevenv_id.Functions/Meta— a singletonFunctionsMetarow holding sync hashes (custom_functions_hash,integration_tool_hash_by_app, …).
Where primitives come from
The single source of truth isToolSurfaceRegistry:
a list of ManagerSpec entries describing each state manager’s alias,
class path, exclusions, and prompt metadata. Three sync paths keep the
catalog current, each hash-guarded so unchanged rows are never rewritten:
- Platform builtins —
seed_builtin_primitives()in builtins_catalog.py introspectsToolSurfaceRegistry.collect_primitives()and seeds the public builtins project, with per-manager hashes fromstable_hash_for_rows(...)and stable int IDs derived from(class_name, method_name). - Provider integration tools —
FunctionManager.sync_provider_integration_tools()materializes a connected app’s tools into the assistant’sFunctions/Primitiveswith per-app hashes. - Custom source functions — the
@custom_functiondecorator (custom/__init__.py) plusFunctionManager.sync_custom(), which collects decorated functions and venv manifests from thecustom/tree and overwrites same-named user rows.
Writing functions: add_functions
The learned-skill write path, used by both the doing agent and the
storage pass:
_parse_implementation), builds
depends_on via dependency analysis
(dependency_analysis.py),
rejects third-party imports without a venv_id, executes the source in a
sandbox to extract the signature and docstring, and writes batched rows
with recompute_derived=True so embeddings refresh. Shell functions carry
their metadata as # @name: / # @args: / # @description: comments.
Deletion is dependency-aware: delete_function(function_id, delete_dependents=True) walks the depends_on graph breadth-first and
removes every compositional function that transitively calls the target.
Primitives can’t be deleted — they’re system-owned.
Reading: search_functions vs filter_functions
search_functions(query, n=5)— semantic, viafederated_ranked_searchagainstembedding_text, spanning personal + team compositional contexts plus the primitive specs (builtins project + provider-backed rows). Results are compacted for the LLM (_compact_function_search_rows), and with_return_callable=Truethe matching callables are injected into the sandbox.filter_functions(filter=...)— exact, a boolean Python expression overFunctionfields evaluated throughfederated_filter, composed with any instance-levelfilter_scopeandexclude_compositional_ids.
Executing: execute_function
The preferred single-call execution path (the actor’s execute_function
tool synthesizes a call and delegates here). Routing inside
FunctionManager.execute_function:
- Resolve the name — compositional row, then primitive registry, then stored primitive rows.
is_primitive=True→_execute_primitive→get_primitive_callable(); returns raw results, which may themselves beSteerableToolHandles (steering composes through stored calls).- Python compositional →
_execute_python_function: in-process, inside a pooled venv (VenvPool, keyed by(venv_id, session_id)withstate_modeofstateless/stateful/read_only), or on the Windows VM whenwindows_os_required. - Shell →
_execute_shell_functionvia theShellPool.
{local_root}/.unity/venvs/{context}/{venv_id}/ and are managed through
add_venv / set_function_venv / delete_venv (FK SET NULL on
delete, so functions survive a deleted venv).
Actor-facing tool names
Registered in code_act_actor.py with the class-name prefix:FunctionManager_search_functions,
FunctionManager_filter_functions, FunctionManager_list_functions,
FunctionManager_add_functions, FunctionManager_delete_function, and the
venv suite (FunctionManager_add_venv, FunctionManager_set_function_venv,
…). The read wrappers are what the
discovery-first gate
requires before the rest of the surface unlocks.