{
  "version": 1,
  "generated_at": "2026-06-26T18:10:24.680083+00:00",
  "agent": "janusz",
  "nodes": [
    {
      "id": "agent:janusz",
      "type": "agent",
      "label": "Janusz",
      "description": "AI coworker for Asecurio, living in Slack."
    },
    {
      "id": "entry:slack_gateway",
      "type": "entry",
      "label": "Slack Gateway",
      "description": "DM / mention / thread-claim gate, dedup, per-user concurrency cap."
    },
    {
      "id": "loop:agent",
      "type": "agent_loop",
      "label": "Agent Loop",
      "description": "Orchestrates tools, prompt caching, appends the cost footer."
    },
    {
      "id": "model:orchestrator",
      "type": "model",
      "label": "claude-sonnet-4-6",
      "description": "Orchestrator — reasoning, tool routing.",
      "group": "model",
      "meta": {
        "role": "orchestrator"
      }
    },
    {
      "id": "model:content",
      "type": "model",
      "label": "gpt-5.5",
      "description": "Content / analysis. \"Claude orchestruje, GPT pracuje nad treściami.\"",
      "group": "model",
      "meta": {
        "role": "content"
      }
    },
    {
      "id": "group:core",
      "type": "tool_group",
      "label": "Core (bash / files)",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "tool:bash",
      "type": "tool",
      "label": "bash",
      "description": "Execute a bash command inside the workspace. Returns stdout, stderr, exit_code. Use for running scripts, listing files, git, anything shell-y. REFUSES commands that would WRITE to protected paths: /app/repo/ (use repo_propose_edit instead),",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "group:browser",
      "type": "tool_group",
      "label": "Browser (Browserbase)",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_click",
      "type": "tool",
      "label": "browser_click",
      "description": "Click on a coordinate in the open browser. Use after browser_goto + browser_snapshot to know what to click. `x` and `y` are pixel coords from the screenshot's top-left. Returns a fresh snapshot after the click.",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_close",
      "type": "tool",
      "label": "browser_close",
      "description": "Close a browser session and free Browserbase quota. Call this when you're done with a multi-step task (e.g. after extracting LinkedIn profile data). Returns the session's recording URL — useful for audit / debugging.",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_close_all",
      "type": "tool",
      "label": "browser_close_all",
      "description": "Close EVERY open browser session in the current Slack thread, including persistent ones (recruit-/linkedin-/persist-/long- prefixes). Use when Bartosz says 'zamknij browser', 'koniec recrutingu', or otherwise signals the workflow is done. R",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_get_text",
      "type": "tool",
      "label": "browser_get_text",
      "description": "Return the visible TEXT content of the current page (no HTML). Cheaper than browser_snapshot when you just want to read a long article. Capped at 50k chars.",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_goto",
      "type": "tool",
      "label": "browser_goto",
      "description": "Open a URL in a cloud Chromium browser. Use when web_search isn't enough — JS-heavy single-page apps (KRS, KNF rejestr, LinkedIn), pages behind login, form filling, or scraping dynamic content. `session_name` lets you keep the same browser ",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_press_key",
      "type": "tool",
      "label": "browser_press_key",
      "description": "Press a keyboard key in the browser (e.g. 'Enter', 'Tab', 'Escape', 'Control+A'). Use to submit forms, close dialogs, or shortcuts.",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_scroll",
      "type": "tool",
      "label": "browser_scroll",
      "description": "Scroll the page. `direction` is 'up' or 'down', `amount` is rough wheel ticks.",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "tool:browser_type",
      "type": "tool",
      "label": "browser_type",
      "description": "Type text into the currently-focused element in the browser. Use after browser_click on an input field. For Enter or shortcuts, use browser_press_key.",
      "group": "tool",
      "meta": {
        "module": "browser"
      }
    },
    {
      "id": "group:pd_calendar",
      "type": "integration",
      "label": "Google Calendar",
      "description": "Per-user OAuth via Pipedream Connect; tokens injected at run time.",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_create_event",
      "type": "tool",
      "label": "calendar_create_event",
      "description": "Create a Google Calendar event in the calling user's calendar. `calendar_id` defaults to 'primary'. `summary` is the event title. `start` and `end` are ISO 8601 datetimes (e.g. '2026-06-07T14:00:00+02:00'). `description` and `location` are ",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_delete_event",
      "type": "tool",
      "label": "calendar_delete_event",
      "description": "Delete a Google Calendar event. `calendar_id` defaults to 'primary'. `event_id` from calendar_list_events. Sends cancellation notice to attendees by default. DESTRUCTIVE/IRREVERSIBLE: confirm via send_approval_request before calling, especi",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_get_event",
      "type": "tool",
      "label": "calendar_get_event",
      "description": "Fetch full details of a single Google Calendar event. `calendar_id` defaults to 'primary'. `event_id` from calendar_list_events or calendar_search_events. Returns the full event object including description, recurrence, conference data, att",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_list_calendars",
      "type": "tool",
      "label": "calendar_list_calendars",
      "description": "List the calendars the calling user has access to (their own + shared). Returns id, summary, primary flag, accessRole, timeZone. Use the calendar id with the other calendar_* tools to operate on a non-primary calendar.",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_list_events",
      "type": "tool",
      "label": "calendar_list_events",
      "description": "List Google Calendar events in a time range from the calling user's calendar. `calendar_id` defaults to 'primary' (the user's main calendar). `time_min` and `time_max` are ISO 8601 datetimes (e.g. '2026-06-07T00:00:00Z'). `max_results` is c",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_quickadd",
      "type": "tool",
      "label": "calendar_quickadd",
      "description": "Create a Google Calendar event from a free-text description using Google's quick-add parser. `text` example: 'Lunch z Markiem jutro o 13:00' or 'Dinner Friday 7pm'. Google parses out the date/time/title. `calendar_id` defaults to 'primary'.",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_search_events",
      "type": "tool",
      "label": "calendar_search_events",
      "description": "Search Google Calendar events by free-text query (matches in summary, description, location, attendee email). `query` is the search string. `time_min` and `time_max` are optional ISO 8601 datetimes to bound the search. `max_results` capped ",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "tool:calendar_update_event",
      "type": "tool",
      "label": "calendar_update_event",
      "description": "Update an existing Google Calendar event (partial — only fields you pass change). `calendar_id` defaults to 'primary'. `event_id` is the event's id. Optional updates: `summary`, `start`, `end`, `description`, `location`, `attendees_json` (r",
      "group": "integration",
      "meta": {
        "module": "pd_calendar"
      }
    },
    {
      "id": "group:connect_tools",
      "type": "integration",
      "label": "Connect (OAuth)",
      "description": "Per-user OAuth via Pipedream Connect; tokens injected at run time.",
      "group": "integration",
      "meta": {
        "module": "connect_tools"
      }
    },
    {
      "id": "tool:connect_integration",
      "type": "tool",
      "label": "connect_integration",
      "description": "Generate a personal link the current Slack user clicks to connect their own third-party account (Gmail, Notion, Google Drive, Google Calendar, …) through Pipedream's hosted OAuth flow. After they finish, Janusz can act on their behalf with ",
      "group": "integration",
      "meta": {
        "module": "connect_tools"
      }
    },
    {
      "id": "group:doc_gen",
      "type": "tool_group",
      "label": "Document generation",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "tool:docx_create_blank",
      "type": "tool",
      "label": "docx_create_blank",
      "description": "Generate a clean .docx in Asecurio brand style (Calibri body, blue #1E4D9B headers) from a list of sections. Each section is `{heading, body}`. Use when no template was found in Drive. Returns the saved workspace path.",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "tool:docx_from_template",
      "type": "tool",
      "label": "docx_from_template",
      "description": "Open a .docx template from the workspace, apply {placeholder: value} replacements, save as a new .docx file in workspace/generated/. Use after `gdrive_download` (for native .docx in Drive) or `gdrive_export_as` (for Google Docs templates ex",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "tool:file_edit",
      "type": "tool",
      "label": "file_edit",
      "description": "Exact-string replace in a file. Set replace_all=true for every occurrence.",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "tool:file_read",
      "type": "tool",
      "label": "file_read",
      "description": "Read a file from the workspace. For large files pass offset (line number) and limit (lines).",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "group:file_convert",
      "type": "tool_group",
      "label": "File convert",
      "group": "tool",
      "meta": {
        "module": "file_convert"
      }
    },
    {
      "id": "tool:file_to_markdown",
      "type": "tool",
      "label": "file_to_markdown",
      "description": "Convert any document to markdown text the LLM can read. Supports: PDF, DOCX, XLSX, PPTX, RTF, HTML, CSV, TSV, TXT, MD, JSON, YAML. Use this BEFORE trying to parse a file yourself with bash — it picks the right library and handles encoding f",
      "group": "tool",
      "meta": {
        "module": "file_convert"
      }
    },
    {
      "id": "tool:file_write",
      "type": "tool",
      "label": "file_write",
      "description": "Write content to a file in the workspace. Creates parent directories. Overwrites if exists.",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "group:pd_drive",
      "type": "integration",
      "label": "Google Drive",
      "description": "Per-user OAuth via Pipedream Connect; tokens injected at run time.",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_create_folder",
      "type": "tool",
      "label": "gdrive_create_folder",
      "description": "Create a new folder in Google Drive under the calling user's account. `parent_id` is empty to create at the root of My Drive, or a folder id to create inside another folder. Returns the new folder's id and webViewLink.",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_delete",
      "type": "tool",
      "label": "gdrive_delete",
      "description": "Move a Google Drive file to the trash (reversible — file restorable within ~30 days from Drive UI). `file_id` is the file's id. DESTRUCTIVE: confirm via send_approval_request before calling. Use empty trash from the UI for a hard delete.",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_download",
      "type": "tool",
      "label": "gdrive_download",
      "description": "Download a Google Drive file into the workspace (workspace/downloads/). Use for binary files (PDF, images, xlsx) you want to process with bash/file tools. `file_id` from gdrive_search. Google-native docs are exported (Docs→PDF, Sheets→xlsx)",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_export_as",
      "type": "tool",
      "label": "gdrive_export_as",
      "description": "Export a Google-native Drive doc (gdoc/gsheets/gslides) into a specific Office format and save it to the workspace. `target_format` is one of: docx, xlsx, pptx, pdf, txt, rtf. Use this when you need an editable copy of a Google Docs/Slides ",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_list_folder",
      "type": "tool",
      "label": "gdrive_list_folder",
      "description": "List the contents of a Google Drive folder. `folder_id` is the folder's ID. WORKS FOR ANY folder — My Drive folders AND shared drive folders (Companies, People, etc.) — no special syntax needed. To list a shared drive's root, use the shared",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_move",
      "type": "tool",
      "label": "gdrive_move",
      "description": "Move a Google Drive file or folder to a different parent folder. `file_id` plus `new_parent_id`. The file's old parents are removed. Returns the updated metadata.",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_read_file",
      "type": "tool",
      "label": "gdrive_read_file",
      "description": "Read the text content of a Google Drive file. Google Docs/Sheets/Slides are exported as text/CSV. Plain-text files are returned directly. For binary files (PDF, images) use gdrive_download instead. `file_id` from gdrive_search/gdrive_list_f",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_rename",
      "type": "tool",
      "label": "gdrive_rename",
      "description": "Rename a Google Drive file or folder. `file_id` plus `new_name`. Returns the updated metadata.",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_search",
      "type": "tool",
      "label": "gdrive_search",
      "description": "Search Google Drive for files and folders. SEARCHES BOTH My Drive AND ALL shared drives the user has access to (Companies, People, project-specific shared drives, etc.) — you do NOT need to specify a drive ID, `supportsAllDrives=true` is se",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_share",
      "type": "tool",
      "label": "gdrive_share",
      "description": "Share a Google Drive file or folder with another user. `file_id` is the file's id. `email` is the recipient. `role` is 'reader', 'commenter', or 'writer'. `notify` sends a Drive notification email. DESTRUCTIVE/EXTERNAL: confirm via send_app",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_update_file",
      "type": "tool",
      "label": "gdrive_update_file",
      "description": "Replace the content of an existing Drive file with a new version from the workspace. `file_id` is the Drive file id. `workspace_path` is a path under workspace/. Preserves the file id and shareable link — only the content changes. DESTRUCTI",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "tool:gdrive_upload_file",
      "type": "tool",
      "label": "gdrive_upload_file",
      "description": "Upload a file from the workspace to Google Drive under the calling user's account. `workspace_path` is a path under workspace/ (e.g. 'uploads/report.pdf'). `parent_id` is an empty string for My Drive root, or a folder id. `name` overrides t",
      "group": "integration",
      "meta": {
        "module": "pd_drive"
      }
    },
    {
      "id": "group:slack_tools",
      "type": "tool_group",
      "label": "Slack",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "tool:get_slack_reactions",
      "type": "tool",
      "label": "get_slack_reactions",
      "description": "Read the emoji reactions on a Slack message. Returns each reaction with its emoji name, count, and the user IDs who reacted. Useful to check 👍/👎 feedback or whether someone acknowledged a message. `message_ts` is the ts of the message you w",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "tool:glob_search",
      "type": "tool",
      "label": "glob_search",
      "description": "Find files matching a glob pattern (supports ** for recursive). path is workspace-relative.",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "group:pd_gmail",
      "type": "integration",
      "label": "Gmail",
      "description": "Per-user OAuth via Pipedream Connect; tokens injected at run time.",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_add_label_to_email",
      "type": "tool",
      "label": "gmail_add_label_to_email",
      "description": "Add one or more labels to a Gmail message. `message_id` from gmail_find_email; `label_ids` is comma-separated (get them from gmail_list_labels). System labels include 'STARRED', 'IMPORTANT', 'UNREAD'.",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_create_draft",
      "type": "tool",
      "label": "gmail_create_draft",
      "description": "Create a draft (NOT send) in the user's Gmail. Safer than gmail_send_email — Bartosz reviews in Gmail and sends manually. Same arguments as gmail_send_email. `attachment_paths` works the same way (comma-separated file paths, including the f",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_download_attachment",
      "type": "tool",
      "label": "gmail_download_attachment",
      "description": "Download a Gmail attachment to the current thread's `downloads/` directory and return its file path. NEVER returns raw base64 into the conversation — that would blow the context limit on any reasonably-sized PDF. For PDFs the tool also auto",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_find_email",
      "type": "tool",
      "label": "gmail_find_email",
      "description": "Search the user's Gmail mailbox. `q` uses Gmail search syntax (e.g. 'from:client@x.com newer_than:7d', 'has:attachment subject:Federale'). Returns up to `max_results` matches as LIGHTWEIGHT entries (id, thread_id, from, subject, snippet, ha",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_forward_email",
      "type": "tool",
      "label": "gmail_forward_email",
      "description": "Forward a Gmail message — ATOMIC, attachments included automatically. Use this whenever the user says 'przekaż', 'forward', 'wyślij dalej', 'przeslij ten mail' etc. Fetches the source message, downloads ALL attachments from it, builds a pro",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_get_current_user",
      "type": "tool",
      "label": "gmail_get_current_user",
      "description": "Return the email address and a few profile fields of the Gmail account the current Slack user has connected. Useful when the user says 'from me' or 'my email'.",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_list_labels",
      "type": "tool",
      "label": "gmail_list_labels",
      "description": "List the user's Gmail labels (system labels like INBOX, SPAM, TRASH plus their custom ones). Returns id, name, type. Useful before gmail_add_label_to_email to pick the right id.",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_list_thread_messages",
      "type": "tool",
      "label": "gmail_list_thread_messages",
      "description": "Fetch all messages in a Gmail thread as LLM-friendly slim records: from/to/subject/date + plain-text body (HTML stripped if no plain alternative, capped at 50k chars per message) + ATTACHMENT METADATA ONLY (filename, mime, size, attachment_",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:gmail_send_email",
      "type": "tool",
      "label": "gmail_send_email",
      "description": "Send an email from the calling user's Gmail mailbox. The mail goes out AS them (their address). DESTRUCTIVE/EXTERNAL: ALWAYS confirm in Slack first (via send_approval_request) before calling. `body_type` is 'plaintext' or 'html'. `cc`/`bcc`",
      "group": "integration",
      "meta": {
        "module": "pd_gmail"
      }
    },
    {
      "id": "tool:grep",
      "type": "tool",
      "label": "grep",
      "description": "Search workspace files for a regex. Returns matching lines.",
      "group": "tool",
      "meta": {
        "module": "core"
      }
    },
    {
      "id": "tool:list_my_approved_tools",
      "type": "tool",
      "label": "list_my_approved_tools",
      "description": "Show which destructive tools the current Slack user has PRE-APPROVED (via the 'Always approve this tool' button). Call this BEFORE `send_approval_request` — if the tool you're about to run is in the returned list, you can call it directly w",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "tool:list_my_connections",
      "type": "tool",
      "label": "list_my_connections",
      "description": "List the third-party accounts (Gmail, Notion, Google Drive, …) that the current Slack user has already connected through Pipedream. Use this to check whether the user has linked the app they need BEFORE calling a per-user tool — if they hav",
      "group": "integration",
      "meta": {
        "module": "connect_tools"
      }
    },
    {
      "id": "tool:list_slack_channels",
      "type": "tool",
      "label": "list_slack_channels",
      "description": "List Slack channels the bot can see (id, name, is_member).",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "group:pd_notion",
      "type": "integration",
      "label": "Notion",
      "description": "Per-user OAuth via Pipedream Connect; tokens injected at run time.",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "tool:notion_append_to_page",
      "type": "tool",
      "label": "notion_append_to_page",
      "description": "Append Markdown content to an existing Notion page. Use this to add notes, sections, follow-ups.",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "tool:notion_create_page",
      "type": "tool",
      "label": "notion_create_page",
      "description": "Create a new Notion page. `parent_id` is the ID of the parent page or database. `title` is the page title. `markdown_content` is the page body (basic Markdown supported).",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "tool:notion_fetch_page",
      "type": "tool",
      "label": "notion_fetch_page",
      "description": "Fetch a Notion page's content as Markdown. `page_id` accepts both raw IDs and full URLs.",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "tool:notion_query_database",
      "type": "tool",
      "label": "notion_query_database",
      "description": "Query a Notion database. `filter_json` and `sorts_json` are JSON strings matching the Notion API filter/sorts schema. Pass empty strings for none. Returns rows as list of dicts.",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "tool:notion_search",
      "type": "tool",
      "label": "notion_search",
      "description": "Search Notion for pages and databases by title. Returns ID, title, type, and url. Use this first when looking for a doc. `query` is fuzzy.",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "tool:notion_update_page",
      "type": "tool",
      "label": "notion_update_page",
      "description": "Update properties of an existing Notion page (e.g. set Status=Won, change a date, tick a checkbox, edit a select). `properties_json` is a JSON object: property name → new value. Values use simple Python types: string for text/select/status/",
      "group": "integration",
      "meta": {
        "module": "pd_notion"
      }
    },
    {
      "id": "group:ai_tools",
      "type": "tool_group",
      "label": "AI delegation",
      "group": "tool",
      "meta": {
        "module": "ai_tools"
      }
    },
    {
      "id": "tool:openai_chat",
      "type": "tool",
      "label": "openai_chat",
      "description": "Delegate text analysis / generation to GPT. Bartosz' rule: Claude orchestrates, GPT writes and analyses. Call this whenever you need to: translate, summarise, classify, draft an email, write a Slack-style message, polish prose, brainstorm c",
      "group": "tool",
      "meta": {
        "module": "ai_tools"
      }
    },
    {
      "id": "tool:pptx_create_blank",
      "type": "tool",
      "label": "pptx_create_blank",
      "description": "Generate a clean .pptx deck from a list of slides. Each slide is `{title, bullets}` (bullets is a list of strings). Asecurio style: white background, blue title, black bullets. Use when no .pptx template was found. Returns the saved workspa",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "tool:pptx_from_template",
      "type": "tool",
      "label": "pptx_from_template",
      "description": "Open a .pptx template, apply {placeholder: value} replacements across all slides/shapes/tables, save as a new .pptx in workspace/generated/. Use after `gdrive_download` for native .pptx or `gdrive_export_as(file_id, 'pptx')` for Google Slid",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "group:repo_tools",
      "type": "tool_group",
      "label": "Self-edit (repo_*)",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_apply_all_pending",
      "type": "tool",
      "label": "repo_apply_all_pending",
      "description": "[ADMIN ONLY — Bartosz] Apply ALL of Bartosz's currently-pending edits in one git commit and a single deploy. Use when Bartosz says 'apply all', 'zaaplikuj wszystkie', or after you've proposed >1 edits in this conversation and he confirms th",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_list_files",
      "type": "tool",
      "label": "repo_list_files",
      "description": "[ADMIN ONLY — Bartosz] List files in a directory of the repo working copy. `directory` is relative to repo root ('' or '.' for root). `pattern` is a glob filter ('*.py' for Python only, '*' for everything). Skips dot-files except .gitignore",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_list_pending_edits",
      "type": "tool",
      "label": "repo_list_pending_edits",
      "description": "[ADMIN ONLY — Bartosz] List pending edits awaiting Bartosz's approval. Each entry shows id, action, file_path, and short summary. Newest first. Use this to see what's queued up or to find an edit_id to re-surface for approval.",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_propose_create_file",
      "type": "tool",
      "label": "repo_propose_create_file",
      "description": "[ADMIN ONLY — Bartosz] Propose CREATING a new file. Stores a pending edit; the actual write only happens after Bartosz clicks ✅ Apply. `file_path` is repo-root-relative. Refuses if the file already exists (use repo_propose_edit instead). `s",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_propose_delete_file",
      "type": "tool",
      "label": "repo_propose_delete_file",
      "description": "[ADMIN ONLY — Bartosz] Propose DELETING an existing file. Stores a pending edit; the actual delete only happens after Bartosz clicks ✅ Apply. `file_path` is repo-root-relative. `summary` should explain why (shown to Bartosz).",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_propose_edit",
      "type": "tool",
      "label": "repo_propose_edit",
      "description": "[ADMIN ONLY — Bartosz] Propose an EDIT to an existing file. Generates a unified diff and stores a pending edit; the actual write only happens when Bartosz clicks ✅ Apply in the Slack approval message. `file_path` is repo-root-relative. `old",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_rollback_last",
      "type": "tool",
      "label": "repo_rollback_last",
      "description": "[ADMIN ONLY — Bartosz] Propose a ROLLBACK of the most recent Janusz commit on main. Walks `git log` for the latest 'Janusz: ...' commit, creates a pending revert that surfaces in Slack with the diff and an Apply button. On Apply: runs `git ",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_search_files",
      "type": "tool",
      "label": "repo_search_files",
      "description": "[ADMIN ONLY — Bartosz] Search the repo for a regex pattern. Uses ripgrep when available, falls back to grep. `pattern` is the regex. `path` is the dir/file to search ('' = whole repo). `file_glob` filters by filename ('*.py' = Python only).",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_show_audit_log",
      "type": "tool",
      "label": "repo_show_audit_log",
      "description": "[ADMIN ONLY — Bartosz] Show recent audit-log entries — every read/write/refusal/apply that went through the `repo_*` tool family. Returns newest first. Useful when Bartosz asks 'co Janusz dziś robił z kodem' or to verify a particular operat",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:repo_show_file",
      "type": "tool",
      "label": "repo_show_file",
      "description": "[ADMIN ONLY — Bartosz] Read a file from the repo working copy. `file_path` is relative to repo root (e.g. 'workspace/apps/cv_pipeline/schema.py'). For long files pass `offset` (line) and `limit` (lines). Truncates at 32 KB to keep the promp",
      "group": "tool",
      "meta": {
        "module": "repo_tools"
      }
    },
    {
      "id": "tool:send_approval_request",
      "type": "tool",
      "label": "send_approval_request",
      "description": "Send an INTERACTIVE approval request with three buttons: ✅ Approve / ✅ Always approve this tool / ❌ Reject. Use this INSTEAD of asking for confirmation in plain text BEFORE every destructive or sensitive action (gmail_send_email, gdrive_del",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "tool:send_slack_message",
      "type": "tool",
      "label": "send_slack_message",
      "description": "Send a Slack message. `blocks` is a JSON string of Block Kit blocks (recommended) OR plain text via a single section block. Set thread_ts to reply in a thread, replace_message_ts to edit a previous message. THIS IS YOUR ONLY VOICE — humans ",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "group:pd_sheets",
      "type": "integration",
      "label": "Google Sheets",
      "description": "Per-user OAuth via Pipedream Connect; tokens injected at run time.",
      "group": "integration",
      "meta": {
        "module": "pd_sheets"
      }
    },
    {
      "id": "tool:sheets_append_row",
      "type": "tool",
      "label": "sheets_append_row",
      "description": "Append rows to the bottom of a Google Sheets range. `values_json` is a JSON array of rows. Sheets finds the last row in the given range and inserts after it. Same value semantics as sheets_write (USER_ENTERED — formulas and dates parse).",
      "group": "integration",
      "meta": {
        "module": "pd_sheets"
      }
    },
    {
      "id": "tool:sheets_find_rows",
      "type": "tool",
      "label": "sheets_find_rows",
      "description": "Find rows in a Google Sheets range where a specific column equals a value. `column_index` is 0-based (0 = first column in the range). Returns up to `max_results` matching rows with their row index inside the range. Useful for lookups like '",
      "group": "integration",
      "meta": {
        "module": "pd_sheets"
      }
    },
    {
      "id": "tool:sheets_read",
      "type": "tool",
      "label": "sheets_read",
      "description": "Read values from a Google Sheets range. `spreadsheet_id` is the ID from the URL (docs.google.com/spreadsheets/d/<ID>/...). `range_a1` is in A1 notation, e.g. 'Sheet1!A1:D100' or 'A1:D'. Returns a list of rows (each row a list of cell values",
      "group": "integration",
      "meta": {
        "module": "pd_sheets"
      }
    },
    {
      "id": "tool:sheets_write",
      "type": "tool",
      "label": "sheets_write",
      "description": "Write values to a Google Sheets range, replacing whatever was there. `values_json` is a JSON array of rows (e.g. '[[\"a\",\"b\"],[\"c\",\"d\"]]'). Values are interpreted by Sheets as if the user had typed them — formulas work, dates parse. DESTRUCT",
      "group": "integration",
      "meta": {
        "module": "pd_sheets"
      }
    },
    {
      "id": "tool:slack_react",
      "type": "tool",
      "label": "slack_react",
      "description": "Add an emoji reaction to a Slack message (e.g. emoji='eyes', 'white_check_mark'). Useful as quick acknowledgement.",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "tool:upload_to_slack",
      "type": "tool",
      "label": "upload_to_slack",
      "description": "Upload a workspace file to Slack and return its permalink. ALWAYS uploads into the current thread you are talking in — never to a channel root. Leave `channel_id` and `thread_ts` blank to inherit them from the current Slack context (the thr",
      "group": "tool",
      "meta": {
        "module": "slack_tools"
      }
    },
    {
      "id": "tool:view_image",
      "type": "tool",
      "label": "view_image",
      "description": "Look at an image file in the workspace using vision and return a description / analysis. Use for screenshots, photos, diagrams, scanned docs the user uploaded (they land in workspace/uploads/). `file_path` is the workspace path. `prompt` sa",
      "group": "tool",
      "meta": {
        "module": "ai_tools"
      }
    },
    {
      "id": "tool:xlsx_create_blank",
      "type": "tool",
      "label": "xlsx_create_blank",
      "description": "Generate a clean .xlsx from a list of sheets. Each sheet is `{name, headers, rows}`. Asecurio style: blue header row, frozen pane, Calibri 11. Use when no .xlsx template found. Returns the saved workspace path.",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "tool:xlsx_from_template",
      "type": "tool",
      "label": "xlsx_from_template",
      "description": "Open an .xlsx template, apply {placeholder: value} replacements across all sheets/cells, save as a new .xlsx in workspace/generated/. Preserves formulas. Use after `gdrive_download` for native .xlsx or `gdrive_export_as(file_id, 'xlsx')` fo",
      "group": "tool",
      "meta": {
        "module": "doc_gen"
      }
    },
    {
      "id": "cron:career_inbox",
      "type": "cron",
      "label": "career@asecurio.com inbox monitor",
      "description": "Scans the career@asecurio.com inbox for new CVs and feeds the RODO-safe cv_pipeline. Zero LLM calls inside the loop.",
      "group": "cron",
      "meta": {
        "schedule": "*/5 8-18 * * 1-5"
      }
    },
    {
      "id": "cron:career_inbox_watchdog",
      "type": "cron",
      "label": "career_inbox stall watchdog",
      "description": "Checks the career_inbox heartbeat every 5 min during work hours and DMs the admin if the tick goes silent.",
      "group": "cron",
      "meta": {
        "schedule": "*/5 9-18 * * 1-5"
      }
    },
    {
      "id": "cron:pricing_check",
      "type": "cron",
      "label": "weekly model pricing watchdog",
      "description": "Weekly: fetches Anthropic + OpenAI pricing, diffs the rates in cost_estimator, and proposes a self-edit if anything drifted.",
      "group": "cron",
      "meta": {
        "schedule": "0 9 * * 1"
      }
    },
    {
      "id": "skill:shared/skills/_archived-linkedin-recruiting",
      "type": "skill",
      "label": "_archived-linkedin-recruiting",
      "description": "ARCHIVED 2026-06-10. Do NOT auto-load. LinkedIn automated lookup was rolled back because Browserbase IP + Chromium fingerprint are detected by LinkedIn regardless of valid session cookies — every browser_goto to a LinkedIn profile hit /404/. Manual workflow now: Bartosz opens LinkedIn in his real browser, copy-pastes the profile content into Slack, Janusz extracts structured fields via openai_chat. To re-enable automated flow we'd need Browserbase Pro (stealth mode + residential proxy pool, ~$200/mc) — see tasks/etap11_browser_spec.md.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/_archived-linkedin-recruiting"
      }
    },
    {
      "id": "skill:shared/skills/asecurio_brand",
      "type": "skill",
      "label": "asecurio_brand",
      "description": "Asecurio brand identity — colors, fonts, layout rules. Apply whenever Janusz generates a deliverable for Asecurio: presentation, PDF, Word document, Excel workbook, email signature, social-media image, internal report. Trigger phrases include \"wygeneruj prezentację\", \"stwórz raport\", \"zrób dokument\", \"make a slide\", \"create CV\", \"draft a pitch deck\", anything that produces a Microsoft-Office / Google-Workspace / PDF artefact whose audience is Asecurio or its clients. Always pair with the format skill (pdf, docx, xlsx, pptx) — this one supplies the visual identity, the format skill supplies the API mechanics.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/asecurio_brand"
      }
    },
    {
      "id": "skill:shared/skills/company",
      "type": "skill",
      "label": "company",
      "description": "Auto-loaded on every run. Asecurio company context — who we are, who's on the team, how we talk.",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/company"
      }
    },
    {
      "id": "skill:shared/skills/contract_templates",
      "type": "skill",
      "label": "contract_templates",
      "description": "Create or format Polish legal contract templates (Umowa Zlecenie, Umowa o Dzieło, NDA, RODO clauses) for Asecurio in Google Docs. Use when asked to \"stwórz umowę\", \"sformatuj wzorzec\", \"napisz NDA\", \"edytuj kontrakt\", or anything that produces a Polish-law contract on Asecurio letterhead. Pair with `asecurio_brand` for colour/font. Pair with `docx_editing` when the contract needs to stay in `.docx`.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/contract_templates"
      }
    },
    {
      "id": "skill:shared/skills/docx_editing",
      "type": "skill",
      "label": "docx_editing",
      "description": "Edit `.docx` Word documents — fill placeholders in a contract template, swap dates in a recurring report, update a CV before sending. Use when a `.docx` already exists on disk or on Google Drive. For creating a Word doc from scratch with bespoke design, prefer `pdf_creation` (HTML/CSS → PDF) unless the deliverable specifically needs to stay editable in Word. Pair with `asecurio_brand` for colour/font when adding new content.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/docx_editing"
      }
    },
    {
      "id": "skill:shared/skills/excel_editing",
      "type": "skill",
      "label": "excel_editing",
      "description": "Edit and modify Excel spreadsheets. Use when working with .xlsx files.",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/excel_editing"
      }
    },
    {
      "id": "skill:shared/skills/notion_asecurio",
      "type": "skill",
      "label": "notion_asecurio",
      "description": "How Notion is structured at Asecurio. Read before searching, creating, or editing any Asecurio Notion page.",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/notion_asecurio"
      }
    },
    {
      "id": "skill:shared/skills/onboarding",
      "type": "skill",
      "label": "onboarding",
      "description": "What Janusz should do on first runs — discover the environment, ask the right questions, fill knowledge gaps.",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/onboarding"
      }
    },
    {
      "id": "skill:shared/skills/pdf_creation",
      "type": "skill",
      "label": "pdf_creation",
      "description": "Generate Asecurio-branded PDF decks (slides) and reports from scratch. Use whenever the user asks for \"wygeneruj prezentację w PDF\", \"stwórz raport\", \"make a one-pager\", \"create a sales deck\", \"draft a CV\", or any artefact whose final form is a PDF. Pair with `asecurio_brand` (mandatory) for colours/fonts/layout rules.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/pdf_creation"
      }
    },
    {
      "id": "skill:shared/skills/pdf_form_filling",
      "type": "skill",
      "label": "pdf_form_filling",
      "description": "Fill in PDF forms programmatically — application forms, intake sheets, tax / KRS / RODO declarations the user uploaded. Works with both AcroForm PDFs (have proper interactive fields) and flat PDFs (no fields, write text at coordinates). Use whenever someone hands Janusz a PDF and says \"wypełnij to\", \"fill this in\", \"uzupełnij formularz\". This skill is brand-neutral — it preserves whatever styling the source PDF already had.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/pdf_form_filling"
      }
    },
    {
      "id": "skill:shared/skills/pdf_signing",
      "type": "skill",
      "label": "pdf_signing",
      "description": "Add a handwritten-looking signature to a PDF — at the bottom of a contract, on a signature line, or anywhere the user designates. Uses `Kalam` Google Font (script-style) to imitate handwriting. Use when the user says \"podpisz to\", \"add my signature\", \"wstaw podpis Bartosza\", \"sign this contract\". This is a VISUAL signature (a glyph that looks like a signature), NOT a cryptographic one. For cryptographic PDF signing (PAdES / qualified e-signature) the user must go through the EU Trusted List provider — out of scope for this skill.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/pdf_signing"
      }
    },
    {
      "id": "skill:shared/skills/pptx_editing",
      "type": "skill",
      "label": "pptx_editing",
      "description": "Edit existing `.pptx` PowerPoint files — replace text in a client deck, swap a date, change a logo, add a slide to a recurring report. Use when working with a `.pptx` already on disk or downloaded from Google Drive. For creating a presentation from scratch with full design control, use `pdf_creation` instead (HTML/CSS + WeasyPrint gives a better Asecurio-branded result than python-pptx). Pair with `asecurio_brand` for colour/font choices when adding new content.\n",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/pptx_editing"
      }
    },
    {
      "id": "skill:shared/skills/users/BARTOSZ_PLACEHOLDER",
      "type": "skill",
      "label": "user_bartosz",
      "description": "Preferences and context for Bartosz Wasilonek (CEO / COO Asecurio). Auto-loaded when DMing or @mentioned by him.",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/users/BARTOSZ_PLACEHOLDER"
      }
    },
    {
      "id": "skill:shared/skills/writing_docs",
      "type": "skill",
      "label": "writing_docs",
      "description": "Asecurio documentation standard — based on ISO 24495-1:2023 (Plain Language). Read before drafting any Asecurio document.",
      "group": "skill",
      "meta": {
        "kind": "shared",
        "path": "shared/skills/writing_docs"
      }
    },
    {
      "id": "control:plane",
      "type": "control_plane",
      "label": "Control Plane",
      "description": "Every turn passes through cost metering + governance."
    },
    {
      "id": "control:cost_estimator",
      "type": "control",
      "label": "Cost estimator",
      "description": "USD cost per Anthropic / OpenAI call from a rates table.",
      "group": "control",
      "meta": {
        "module": "cost_estimator"
      }
    },
    {
      "id": "control:monthly_spend",
      "type": "control",
      "label": "Monthly spend",
      "description": "Per-provider monthly buckets + limits; refuses the turn when over budget.",
      "group": "control",
      "meta": {
        "module": "monthly_spend"
      }
    },
    {
      "id": "control:cost_persistence",
      "type": "control",
      "label": "Cost persistence",
      "description": "Persists per-thread / per-turn cost to workspace/state.",
      "group": "control",
      "meta": {
        "module": "cost_persistence"
      }
    },
    {
      "id": "control:approvals",
      "type": "control",
      "label": "Approvals",
      "description": "Admin approval gate for sensitive actions.",
      "group": "control",
      "meta": {
        "module": "approvals"
      }
    },
    {
      "id": "control:pending_edits",
      "type": "control",
      "label": "Pending edits",
      "description": "Queue of proposed self-edits awaiting Slack Approve/Reject.",
      "group": "control",
      "meta": {
        "module": "pending_edits"
      }
    },
    {
      "id": "control:thread_claims",
      "type": "control",
      "label": "Thread claims",
      "description": "Per-thread auto-respond ownership.",
      "group": "control",
      "meta": {
        "module": "thread_claims"
      }
    },
    {
      "id": "control:pricing_check",
      "type": "control",
      "label": "Pricing watchdog",
      "description": "Weekly drift check of model pricing -> proposes a self-edit.",
      "group": "control",
      "meta": {
        "module": "pricing_check"
      }
    },
    {
      "id": "control:repo_edit",
      "type": "control",
      "label": "Repo self-edit",
      "description": "Applies / rolls back admin-approved edits to Janusz's own code.",
      "group": "control",
      "meta": {
        "module": "repo_edit"
      }
    },
    {
      "id": "state:monthly_spend.json",
      "type": "state",
      "label": "monthly_spend.json",
      "group": "state"
    },
    {
      "id": "state:thread_costs.json",
      "type": "state",
      "label": "thread_costs.json",
      "group": "state"
    }
  ],
  "edges": [
    {
      "source": "entry:slack_gateway",
      "target": "loop:agent",
      "kind": "event"
    },
    {
      "source": "loop:agent",
      "target": "model:orchestrator",
      "kind": "orchestrates"
    },
    {
      "source": "loop:agent",
      "target": "model:content",
      "kind": "delegates"
    },
    {
      "source": "loop:agent",
      "target": "group:core",
      "kind": "calls"
    },
    {
      "source": "group:core",
      "target": "tool:bash",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:browser",
      "kind": "calls"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_click",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_close",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_close_all",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_get_text",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_goto",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_press_key",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_scroll",
      "kind": "contains"
    },
    {
      "source": "group:browser",
      "target": "tool:browser_type",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:pd_calendar",
      "kind": "calls"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_create_event",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_delete_event",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_get_event",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_list_calendars",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_list_events",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_quickadd",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_search_events",
      "kind": "contains"
    },
    {
      "source": "group:pd_calendar",
      "target": "tool:calendar_update_event",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:connect_tools",
      "kind": "calls"
    },
    {
      "source": "group:connect_tools",
      "target": "tool:connect_integration",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:doc_gen",
      "kind": "calls"
    },
    {
      "source": "group:doc_gen",
      "target": "tool:docx_create_blank",
      "kind": "contains"
    },
    {
      "source": "group:doc_gen",
      "target": "tool:docx_from_template",
      "kind": "contains"
    },
    {
      "source": "group:core",
      "target": "tool:file_edit",
      "kind": "contains"
    },
    {
      "source": "group:core",
      "target": "tool:file_read",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:file_convert",
      "kind": "calls"
    },
    {
      "source": "group:file_convert",
      "target": "tool:file_to_markdown",
      "kind": "contains"
    },
    {
      "source": "group:core",
      "target": "tool:file_write",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:pd_drive",
      "kind": "calls"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_create_folder",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_delete",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_download",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_export_as",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_list_folder",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_move",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_read_file",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_rename",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_search",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_share",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_update_file",
      "kind": "contains"
    },
    {
      "source": "group:pd_drive",
      "target": "tool:gdrive_upload_file",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:slack_tools",
      "kind": "calls"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:get_slack_reactions",
      "kind": "contains"
    },
    {
      "source": "group:core",
      "target": "tool:glob_search",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:pd_gmail",
      "kind": "calls"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_add_label_to_email",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_create_draft",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_download_attachment",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_find_email",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_forward_email",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_get_current_user",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_list_labels",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_list_thread_messages",
      "kind": "contains"
    },
    {
      "source": "group:pd_gmail",
      "target": "tool:gmail_send_email",
      "kind": "contains"
    },
    {
      "source": "group:core",
      "target": "tool:grep",
      "kind": "contains"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:list_my_approved_tools",
      "kind": "contains"
    },
    {
      "source": "group:connect_tools",
      "target": "tool:list_my_connections",
      "kind": "contains"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:list_slack_channels",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:pd_notion",
      "kind": "calls"
    },
    {
      "source": "group:pd_notion",
      "target": "tool:notion_append_to_page",
      "kind": "contains"
    },
    {
      "source": "group:pd_notion",
      "target": "tool:notion_create_page",
      "kind": "contains"
    },
    {
      "source": "group:pd_notion",
      "target": "tool:notion_fetch_page",
      "kind": "contains"
    },
    {
      "source": "group:pd_notion",
      "target": "tool:notion_query_database",
      "kind": "contains"
    },
    {
      "source": "group:pd_notion",
      "target": "tool:notion_search",
      "kind": "contains"
    },
    {
      "source": "group:pd_notion",
      "target": "tool:notion_update_page",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:ai_tools",
      "kind": "calls"
    },
    {
      "source": "group:ai_tools",
      "target": "tool:openai_chat",
      "kind": "contains"
    },
    {
      "source": "group:doc_gen",
      "target": "tool:pptx_create_blank",
      "kind": "contains"
    },
    {
      "source": "group:doc_gen",
      "target": "tool:pptx_from_template",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:repo_tools",
      "kind": "calls"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_apply_all_pending",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_list_files",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_list_pending_edits",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_propose_create_file",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_propose_delete_file",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_propose_edit",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_rollback_last",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_search_files",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_show_audit_log",
      "kind": "contains"
    },
    {
      "source": "group:repo_tools",
      "target": "tool:repo_show_file",
      "kind": "contains"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:send_approval_request",
      "kind": "contains"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:send_slack_message",
      "kind": "contains"
    },
    {
      "source": "loop:agent",
      "target": "group:pd_sheets",
      "kind": "calls"
    },
    {
      "source": "group:pd_sheets",
      "target": "tool:sheets_append_row",
      "kind": "contains"
    },
    {
      "source": "group:pd_sheets",
      "target": "tool:sheets_find_rows",
      "kind": "contains"
    },
    {
      "source": "group:pd_sheets",
      "target": "tool:sheets_read",
      "kind": "contains"
    },
    {
      "source": "group:pd_sheets",
      "target": "tool:sheets_write",
      "kind": "contains"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:slack_react",
      "kind": "contains"
    },
    {
      "source": "group:slack_tools",
      "target": "tool:upload_to_slack",
      "kind": "contains"
    },
    {
      "source": "group:ai_tools",
      "target": "tool:view_image",
      "kind": "contains"
    },
    {
      "source": "group:doc_gen",
      "target": "tool:xlsx_create_blank",
      "kind": "contains"
    },
    {
      "source": "group:doc_gen",
      "target": "tool:xlsx_from_template",
      "kind": "contains"
    },
    {
      "source": "skill:shared/skills/_archived-linkedin-recruiting",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/asecurio_brand",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/company",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/contract_templates",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/docx_editing",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/excel_editing",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/notion_asecurio",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/onboarding",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/pdf_creation",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/pdf_form_filling",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/pdf_signing",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/pptx_editing",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/users/BARTOSZ_PLACEHOLDER",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "skill:shared/skills/writing_docs",
      "target": "loop:agent",
      "kind": "memory"
    },
    {
      "source": "cron:career_inbox",
      "target": "entry:slack_gateway",
      "kind": "triggers"
    },
    {
      "source": "cron:career_inbox_watchdog",
      "target": "cron:career_inbox",
      "kind": "monitors"
    },
    {
      "source": "control:plane",
      "target": "loop:agent",
      "kind": "governs"
    },
    {
      "source": "control:plane",
      "target": "control:cost_estimator",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:monthly_spend",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:cost_persistence",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:approvals",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:pending_edits",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:thread_claims",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:pricing_check",
      "kind": "contains"
    },
    {
      "source": "control:plane",
      "target": "control:repo_edit",
      "kind": "contains"
    },
    {
      "source": "cron:pricing_check",
      "target": "control:cost_estimator",
      "kind": "proposes"
    },
    {
      "source": "control:plane",
      "target": "state:monthly_spend.json",
      "kind": "persists"
    },
    {
      "source": "control:plane",
      "target": "state:thread_costs.json",
      "kind": "persists"
    }
  ],
  "stats": {
    "node_count": 127,
    "edge_count": 125,
    "by_type": {
      "agent": 1,
      "entry": 1,
      "agent_loop": 1,
      "model": 2,
      "tool_group": 7,
      "tool": 81,
      "integration": 6,
      "cron": 3,
      "skill": 14,
      "control_plane": 1,
      "control": 8,
      "state": 2
    }
  }
}