From 44f215c764107587792dd5a7644070157711f78a Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Tue, 28 Apr 2026 21:50:02 -0400 Subject: [PATCH] feat: add ae_journal_list tool Lists all Aether Journals for the configured account via POST /v3/crud/journal/search with no filters (account scoped by header). Returns name + id_random for each journal so the agent can discover available journals before searching or writing entries. Co-Authored-By: Claude Sonnet 4.6 --- cortex/tools/__init__.py | 14 +++++++++++ cortex/tools/ae_knowledge.py | 46 ++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/cortex/tools/__init__.py b/cortex/tools/__init__.py index 7bd3369..a1b700f 100644 --- a/cortex/tools/__init__.py +++ b/cortex/tools/__init__.py @@ -18,6 +18,7 @@ from google.genai import types from tools.web import search as _web_search from tools.ae_knowledge import journal_search as _ae_journal_search from tools.ae_knowledge import journal_entry_create as _ae_journal_entry_create +from tools.ae_knowledge import journal_list as _ae_journal_list from tools.ae_tasks import task_list as _ae_task_list from tools.files import file_read as _file_read from tools.system import claude_allow_dir as _claude_allow_dir, shell_exec as _shell_exec @@ -68,6 +69,17 @@ _web_search_declaration = types.FunctionDeclaration( ), ) +_ae_journal_list_declaration = types.FunctionDeclaration( + name="ae_journal_list", + description=( + "List all Aether Journals available for this account. " + "Returns each journal's name and id_random. " + "Call this first when you need to write a new entry or scope a search to a specific journal " + "and don't already know the journal's id." + ), + parameters=types.Schema(type=types.Type.OBJECT, properties={}), +) + _ae_journal_search_declaration = types.FunctionDeclaration( name="ae_journal_search", description=( @@ -187,6 +199,7 @@ _file_read_declaration = types.FunctionDeclaration( _CALLABLES: dict[str, callable] = { "web_search": _web_search, + "ae_journal_list": _ae_journal_list, "ae_journal_search": _ae_journal_search, "ae_journal_entry_create": _ae_journal_entry_create, "ae_task_list": _ae_task_list, @@ -551,6 +564,7 @@ _scratch_clear_declaration = types.FunctionDeclaration( TOOL_DECLARATIONS = [ types.Tool(function_declarations=[ _web_search_declaration, + _ae_journal_list_declaration, _ae_journal_search_declaration, _ae_journal_entry_create_declaration, _ae_task_list_declaration, diff --git a/cortex/tools/ae_knowledge.py b/cortex/tools/ae_knowledge.py index f1b0edf..64a3387 100644 --- a/cortex/tools/ae_knowledge.py +++ b/cortex/tools/ae_knowledge.py @@ -112,6 +112,52 @@ def _sync_journal_search(query: str, journal_id: str | None, max_results: int) - return "\n".join(lines).strip() +# --------------------------------------------------------------------------- +# Tool: ae_journal_list +# --------------------------------------------------------------------------- + +async def journal_list() -> str: + """List all journals accessible to the configured AE account.""" + err = _check_config() + if err: + return err + return await asyncio.to_thread(_sync_journal_list) + + +def _sync_journal_list() -> str: + import requests + + url = f"{settings.ae_api_url}/v3/crud/journal/search" + try: + resp = requests.post( + url, + headers=_headers(), + json={"page_size": 100}, + timeout=settings.ae_api_timeout, + ) + resp.raise_for_status() + data = resp.json() + except Exception as e: + logger.warning("ae_journal_list failed: %s", e) + return f"Journal list error: {e}" + + journals = data.get("data", []) + if not journals: + return "No journals found for this account." + + lines = [f"Journals ({len(journals)}):\n"] + for j in journals: + jid = j.get("journal_id") or j.get("id_random") or j.get("id") or "?" + name = j.get("name") or "(untitled)" + desc = j.get("description") or "" + line = f"- **{name}** — id: `{jid}`" + if desc: + line += f"\n {desc}" + lines.append(line) + + return "\n".join(lines) + + # --------------------------------------------------------------------------- # Tool: ae_journal_entry_create # ---------------------------------------------------------------------------