fulcrum: "1.0"
name: bus_pull
description: >
  Agent Bus — poll for messages on a topic since a cursor timestamp.
  Pass the subscription_id returned by bus_subscribe as since_ts.
  Returns up to max_msgs messages in ascending ts order.
  Re-call with the ts of the last returned message to advance the cursor.
  When agent_id is provided, the query also returns DMs (to_agent = agent_id)
  in addition to broadcasts (to_agent IS NONE). Without agent_id, only
  broadcasts are returned, so private DMs to other agents stay private.
  All scalar inputs are bound as SurrealQL parameters via the HTTP /sql query
  string ($topic_pattern, $since_ts, $current_agent, $max_msgs).
  ed25519 signature verification is the caller's responsibility — fetch
  pubkey via bus_announce/bus_agent record, then nacl/cryptography verify
  the signature field over the canonical message bytes.
  [keywords: agent, bus, pull, poll, receive, message, inter-agent]

schema:
  input:
    type: object
    properties:
      topic_pattern:
        type: string
        description: "Topic prefix, e.g. bus.global or bus.proj.botnaut"
      since_ts:
        type: string
        description: "ISO-8601 cursor timestamp from bus_subscribe or last bus_pull"
      agent_id:
        type: string
        description: "Optional: also return DMs addressed to this agent id"
      max_msgs:
        type: integer
        description: "Max messages to return (1-100)"
        default: 20
    required:
      - topic_pattern
      - since_ts
  output:
    type: object
    properties:
      result:
        type: array
        description: "Array of bus_msg records"
      status:
        type: string

providers:
  primary:
    service: rest
    cost_per_call: 0
    timeout: 5
    config:
      endpoint: http://127.0.0.1:8000/sql
      method: POST
      headers:
        Surreal-Ns: agent_bus
        Surreal-Db: prod
        Accept: application/json
        Authorization: "Basic cm9vdDpyb290"
      params:
        topic_pattern: "{topic_pattern}"
        since_ts: "{since_ts}"
        current_agent: "{agent_id}"
        max_msgs: "{max_msgs}"
      body_content_type: text/plain
      # DM filter: a row is delivered if it's a broadcast (to_agent IS NONE)
      # OR addressed to the current agent. Without $current_agent, only
      # broadcasts pass — DMs to others are filtered out.
      body: "SELECT * FROM bus_msg WHERE string::starts_with(topic, $topic_pattern) AND ts > <datetime> $since_ts AND (to_agent IS NONE OR to_agent = $current_agent) ORDER BY ts ASC LIMIT type::int($max_msgs);"

auth:
  required: false

cache:
  strategy: none

metadata:
  category: automation
  tags:
    - agent
    - bus
    - pull
    - poll
    - receive
    - message
    - inter-agent
    - surrealdb
  cost_category: free
  execution_time: fast
  read_only: true
  docs: /tmp/c-agent-bus-design.md
