Logic Tools

Store, query, and reason over structured facts without a database or LLM call.

Logic cells

Every operation below runs inside a logic cell β€” a per-user symbolic fact space. Each cell has a persistent layer for durability and a symbolic layer for sub-millisecond query execution. When you call any logic.* tool, the system ensures your cell is ready: facts are loaded into the symbolic engine automatically so queries execute instantly with no LLM calls and no external round-trips.

Facts inside a logic cell are typed (entity, attribute, relation, metric, tag, context, constraint) and can be stored from natural language or passed directly as structured maps. Once stored, they persist across sessions β€” an agent that stores a fact today can query it tomorrow without re-learning it.

All Logic tools are available at data-grout@1/logic.*@1.

Namespaces

Logic cells support namespaces β€” fully isolated fact spaces per user. Each namespace has its own facts and constraints; facts in "varenholm" never appear in "default" and vice versa.

  • namespace parameter β€” All logic tools (logic.remember, logic.query, logic.assert, logic.forget, logic.constrain, logic.reflect, logic.hydrate, logic.export, logic.import, logic.tabulate) accept an optional namespace string parameter.
  • Default is "default" β€” Omitting namespace uses "default"; existing behavior is unchanged.
  • Isolation β€” Prolog uses a composite scope key (user_id::namespace).
  • Worker pool β€” One Prolog worker per user; facts are scoped by namespace within it.
  • logic.worlds β€” Lists all available namespaces for the user with fact/constraint counts per namespace.

logic.remember@1

Store one or more facts. Facts are natural language statements or structured key-value pairs that you want to recall later. Each fact gets a unique handle you can use to retract it.

Parameters

Parameter Type Required Default Description
statement string conditionally – A single fact as a natural language statement. Provide either statement or facts, not both
facts array conditionally – An array of structured facts to store in batch
tag string no – A label to group related facts (e.g. "project-alpha", "user-preferences")
namespace string no "default" Isolated fact space. Facts in one namespace never appear in another

Example: single fact

{
  "name": "data-grout@1/logic.remember@1",
  "arguments": {
    "statement": "The billing contact for Acme Corp is jane@acme.com",
    "tag": "contacts"
  }
}

Example: batch facts

{
  "name": "data-grout@1/logic.remember@1",
  "arguments": {
    "facts": [
      { "entity": "Acme Corp", "attribute": "billing_contact", "value": "jane@acme.com" },
      { "entity": "Acme Corp", "attribute": "plan", "value": "enterprise" },
      { "entity": "Globex", "attribute": "billing_contact", "value": "bob@globex.com" }
    ],
    "tag": "contacts"
  }
}

Response:

{
  "stored": 3,
  "handles": ["fact_a1", "fact_a2", "fact_a3"]
}

logic.assert@1

Store typed facts directly β€” zero LLM cost. Unlike logic.remember, Assert skips natural language translation entirely. Pass structured fact maps and they are stored exactly as specified. Facts are immediately queryable and participate in constraint inference.

Use Assert when you know the exact fact structure. Use Remember when you want natural language input.

Parameters

Parameter Type Required Default Description
facts array yes – Array of typed fact maps. Each must have a type field: entity, attribute, relation, metric, tag, or context. Any fact may include probability (0.0-1.0) for ProbLog-style weighted assertions
tag string no "default" Grouping tag for the asserted facts
namespace string no "default" Isolated fact space
skip_embedding boolean no false Skip embedding generation (cheaper for structural/navigation data)

Example

{
  "name": "data-grout@1/logic.assert@1",
  "arguments": {
    "facts": [
      { "type": "entity", "name": "Acme Corp" },
      { "type": "attribute", "entity": "Acme Corp", "attribute": "industry", "value": "saas" },
      { "type": "relation", "subject": "Acme Corp", "relation": "acquired", "object": "Globex", "probability": 0.85 },
      { "type": "metric", "entity": "Acme Corp", "metric": "arr", "value": 2500000 }
    ],
    "tag": "companies"
  }
}

Response:

{
  "handles": ["a1b2c3", "d4e5f6", "g7h8i9", "j0k1l2"],
  "count": 4,
  "llm_credits": 0,
  "message": "Asserted 4 facts directly."
}

logic.query@1

Retrieve facts. Three modes: ask a question in natural language, provide structured patterns, or write a raw Prolog goal for maximum precision. All modes execute against the symbolic layer at sub-millisecond latency.

Parameters

Parameter Type Required Default Description
question string conditionally – A natural language question. Provide one of question, patterns, or prolog
patterns array conditionally – Structured match patterns (e.g. [{ "entity": "Acme Corp" }])
prolog string conditionally – Raw Prolog goal string β€” zero LLM cost, full precision. Variables are returned as structured JSON bindings. Base predicates: entity/1, relation/3, attribute/3, metric/3, tag/2. Handle-aware variants for probability joins: entity_h/2, relation_h/4, attribute_h/4, metric_h/4, tag_h/3. Probability: probability/2. Graph: path/5, path_prob/6, reachable/4, reachable_prob/5, neighbors/4. Plus user-defined constraint predicates
limit integer no 50 Maximum number of results to return
namespace string no "default" Isolated fact space. Queries only return facts from this namespace

Example: natural language

{
  "name": "data-grout@1/logic.query@1",
  "arguments": {
    "question": "Who is the billing contact for Acme Corp?"
  }
}

Response:

{
  "results": [
    {
      "handle": "fact_a1",
      "statement": "The billing contact for Acme Corp is jane@acme.com",
      "tag": "contacts"
    }
  ]
}

Example: pattern match

{
  "name": "data-grout@1/logic.query@1",
  "arguments": {
    "patterns": [{ "attribute": "billing_contact" }],
    "limit": 10
  }
}

Returns all facts with a billing_contact attribute across all entities.

Example: raw Prolog query

{
  "name": "data-grout@1/logic.query@1",
  "arguments": {
    "prolog": "metric(E, \"arr\", V), V > 1000000"
  }
}

Response:

{
  "results": [
    { "E": "Acme Corp", "V": 2500000 }
  ],
  "total": 1,
  "description": "Direct Prolog query"
}

The prolog mode costs 1 credit (vs 3 for natural language) and gives you full control over the query. Available predicates include entity/1, relation/3, attribute/3, metric/3, tag/2, and the graph traversal predicates described below.

Graph traversal via patterns

Query patterns support three built-in graph predicates for navigating relation edges:

Predicate Description Pattern fields
path Find paths between two nodes from, to, relation, max_depth
reachable All nodes reachable from a start from, relation, max_depth
neighbors One-hop adjacency node, relation, direction
{
  "name": "data-grout@1/logic.query@1",
  "arguments": {
    "patterns": [{ "predicate": "reachable", "from": "Acme Corp", "relation": "acquired" }]
  }
}

Returns all entities reachable from β€œAcme Corp” via acquired relation edges. These predicates are also available in raw Prolog queries as short-form wrappers that auto-scope to the current namespace: path(From, To, RelType, MaxDepth, Path), reachable(From, To, RelType, MaxDepth), and neighbors(Node, RelType, Direction, Neighbors). User-defined constraint predicates (created via logic.constrain) are also callable in direct Prolog queries.


logic.constrain@1

Store a rule or policy that applies to future queries and workflows. Constraints are persistent and affect how the system reasons about your facts. If a constraint with the same name already exists, it is automatically overwritten (the old version is retracted and replaced). Constraints are eagerly compiled into callable Prolog predicates at storage time, so they are immediately available for queries in the same namespace.

The rule parameter accepts both natural language and raw Prolog syntax. If the input looks like a Prolog rule (e.g. connected(A, B) :- relation(A, "links_to", B).), it bypasses NL translation entirely for zero LLM cost. The constraint name is always derived from the rule head predicate β€” not from an LLM-generated descriptive name. Once stored, the constraint predicate is callable directly via logic.query(prolog: "connected(A, B)") β€” no special syntax needed.

Parameters

Parameter Type Required Default Description
rule string yes – The rule to store, as a natural language statement
tag string no – A label to group related constraints
namespace string no "default" Isolated fact space. Constraints apply only within this namespace

Example

{
  "name": "data-grout@1/logic.constrain@1",
  "arguments": {
    "rule": "Enterprise customers must have a billing contact before creating invoices",
    "tag": "billing-policy"
  }
}

Subsequent plans and queries will respect this constraint. For example, a workflow that creates an invoice for an enterprise customer without a billing contact on file would flag a policy violation.


logic.forget@1

Retract facts from your fact space. Provide specific fact handles or a pattern to match against.

Parameters

Parameter Type Required Default Description
handles array conditionally – Array of fact handles to retract (e.g. ["fact_a1", "fact_a2"]). Provide either handles or pattern, not both
pattern string conditionally – A match pattern to retract all matching facts (e.g. "entity:Acme Corp")
namespace string no "default" Isolated fact space. Retracts only from this namespace

Example: by handle

{
  "name": "data-grout@1/logic.forget@1",
  "arguments": {
    "handles": ["fact_a1"]
  }
}

Example: by pattern

{
  "name": "data-grout@1/logic.forget@1",
  "arguments": {
    "pattern": "entity:Globex"
  }
}

Retracts all facts about the entity β€œGlobex”.


logic.reflect@1

Get a summary of what the system knows about an entity or across your entire fact space. Useful for auditing stored knowledge or debugging unexpected behavior.

Parameters

Parameter Type Required Default Description
entity string no – Focus reflection on a specific entity. Omit to reflect on the full fact space
summary_only boolean no false Return only a high-level summary instead of listing individual facts
namespace string no "default" Isolated fact space. Reflects only facts in this namespace

Example

{
  "name": "data-grout@1/logic.reflect@1",
  "arguments": {
    "entity": "Acme Corp"
  }
}

Response:

{
  "entity": "Acme Corp",
  "fact_count": 2,
  "facts": [
    { "handle": "fact_a1", "attribute": "billing_contact", "value": "jane@acme.com" },
    { "handle": "fact_a2", "attribute": "plan", "value": "enterprise" }
  ],
  "constraints_applied": 1,
  "tags": ["contacts"]
}

logic.hydrate@1

Hydrate your context from symbolic memory. Given a goal in natural language, the tool decomposes it into subgoals, queries your logic cell for relevant facts, scores them by relevance, and returns a curated context payload within a token budget. Uses one LLM call for decomposition; all fact retrieval is symbolic and zero-cost.

Parameters

Parameter Type Required Default Description
goal string yes – The goal or current task in natural language
max_tokens integer no 4000 Token budget for returned context. Set to -1 for unlimited
include_constraints boolean no true Always include active constraints regardless of scoring
format string no "text" "text" for a prompt-ready string, "structured" for raw fact maps
namespace string no "default" Isolated fact space. Hydrates only from this namespace

Example

{
  "name": "data-grout@1/logic.hydrate@1",
  "arguments": {
    "goal": "Help the user migrate their payment system from Stripe to Adyen",
    "max_tokens": 2000
  }
}

Response:

{
  "context": "Entity: Stripe\nStripe: type = payment_provider\nConstraint: PCI compliance required\nAcme uses Stripe",
  "facts_included": 4,
  "facts_available": 12,
  "subgoals": ["payment_system", "stripe_integration", "migration_constraints"],
  "token_count": 85,
  "budget": 2000,
  "gaps": ["No facts found for 'adyen_knowledge' (Adyen integration details) β€” consider using discovery tools"]
}

The gaps field tells you what you don’t know β€” subgoals that returned no matching facts. Use this to decide whether to call discovery tools before proceeding.


logic.worlds@1

List all available namespaces for the user with fact and constraint counts per namespace. Use this to discover which worlds exist and how much data each contains.

Parameters

This tool has no required parameters.

Example

{
  "name": "data-grout@1/logic.worlds@1",
  "arguments": {}
}

Response:

{
  "worlds": [
    { "namespace": "default", "fact_count": 42, "constraint_count": 3 },
    { "namespace": "varenholm", "fact_count": 12, "constraint_count": 1 }
  ]
}

Typical usage

Logic tools are useful for maintaining state across conversations and workflows:

  1. Worlds β€” List namespaces with logic.worlds to see available fact spaces
  2. Hydrate context at the start of a task β€” get all relevant facts in one call (optionally scoped by namespace)
  3. Remember context an agent learns during a session (customer preferences, previous decisions)
  4. Assert structured facts directly when you know the exact shape (zero LLM cost)
  5. Constrain what future workflows can do based on business rules
  6. Query stored knowledge instantly when making decisions β€” use natural language for simplicity, patterns for structure, or raw Prolog for precision
  7. Forget outdated facts when information changes
  8. Reflect to audit what the system knows before taking action

Use the namespace parameter to keep separate worlds (e.g. "project-alpha", "varenholm") fully isolated from each other.

Power user: raw Prolog

For maximum control, use logic.query(prolog: "...") to write Prolog goals directly. This costs 1 credit (vs 3 for NL), has zero LLM latency, and supports the full fact-space predicate set including graph traversal. Use logic.assert to build up fact graphs, then query them with path/5, reachable/4, and neighbors/4 for native graph operations β€” these auto-scope to the current namespace so you never need to specify internal scope keys. User-defined constraint predicates from logic.constrain are also callable directly in Prolog queries, enabling a workflow of: define rules once, query them with full Prolog precision.