Back to list
1plco

fulcrum-sdk

by 1plco

1🍴 0📅 Jan 25, 2026

SKILL.md


name: fulcrum-sdk description: "Dispatch milestones to user-facing timeline. Use when completing significant steps: starting major phases, calling external APIs, creating external references, completing database operations, or validating Pydantic models. Never dispatch every tool call or internal logs. Environment variables FULCRUM_DISPATCH_URL, FULCRUM_DISPATCH_TOKEN, FULCRUM_TICKET_UUID, and FULCRUM_RUN_UUID must be set (injected by system)." license: "© 2025 Daisyloop Technologies Inc. See LICENSE.txt"

Fulcrum SDK - Dispatch System

Overview

The dispatch system provides a user-facing timeline of agent progress. Dispatches appear as milestones in the ticket UI, helping users understand what the agent is doing and track progress on their request.

Key principle: Dispatch milestones, not tool traces. Users care about meaningful progress, not internal implementation details.

Quick Start

from fulcrum_sdk._internal.dispatch import get_dispatch_client

dispatch = get_dispatch_client()  # Configured from FULCRUM_* env vars

# Simple text milestone
dispatch.dispatch_text("Starting invoice processing")

# API call with service context
dispatch.dispatch_api_call(
    "Called Phonic to confirm appointment",
    service="phonic",
    operation="outbound_call",
    phone="+1234567890"
)

Or use the validation script:

uv run skills/fulcrum-sdk/scripts/dispatch.py

When to Dispatch

DO Dispatch

SituationExample
Starting a major phasedispatch_text("Starting data extraction")
Calling external APIsdispatch_api_call("Geocoding address", service="mapbox", ...)
Creating external referencesdispatch_external_ref("Browser task created", provider="browser-use", ...)
Database operations (with counts)dispatch_db("Inserted invoices", operation="insert", table="invoices", rows=15)
Pydantic model displaydispatch_model("Validated invoice data", model=invoice)

DO NOT Dispatch

SituationWhy
Every tool callUsers don't need internal trace
Internal logsUse stdout/stderr instead
Large payloadsKeep summaries concise
Secrets/credentialsNever dispatch sensitive data
Debug outputBelongs in stdout events

Kind Selection Guide

Choose the dispatch kind based on what's most valuable to the user:

KindUse WhenExample
textGeneral milestones, phase transitions"Processing complete"
api_callExternal API interaction is the key event"Called Claude for extraction"
external_refCreating a reference for later displayBrowser task, generated file
dbDatabase operation (include counts, not rows)"Inserted 15 records"
modelPydantic model display (shows field values)"Validated Invoice model"

API Reference

Getting the Client

from fulcrum_sdk._internal.dispatch import get_dispatch_client

dispatch = get_dispatch_client()

# Check if dispatch is enabled
if dispatch.enabled:
    dispatch.dispatch_text("Ready to process")

dispatch_text(summary, text=None)

General text milestone. Simplest dispatch kind.

dispatch.dispatch_text("Starting invoice processing")
dispatch.dispatch_text("Extracted 12 line items", text="Items included office supplies, software licenses")

dispatch_api_call(summary, service, operation, **details)

External API call. Include service name and operation.

dispatch.dispatch_api_call(
    "Geocoding delivery address",
    service="mapbox",
    operation="geocode",
    address="123 Main St"
)

dispatch.dispatch_api_call(
    "Generated summary with Claude",
    service="anthropic",
    operation="messages.create",
    model="claude-sonnet-4-5-20250929"
)

dispatch_external_ref(summary, provider, ref_type, ref_id, url=None)

External reference for later retrieval/display.

# Browser Use task
dispatch.dispatch_external_ref(
    "Browser task created",
    provider="browser-use",
    ref_type="task",
    ref_id=task.id
)

# Phonic conversation
dispatch.dispatch_external_ref(
    "Outbound call started",
    provider="phonic",
    ref_type="conversation",
    ref_id=result.conversation_id
)

# Note: dispatch_external_ref is ONLY for external services with hosted IDs
# (e.g., browser-use tasks, phonic conversations). Do NOT use it for local
# files in /output/ - those are automatically collected and uploaded after
# execution. Users access them through the ticket files panel.

dispatch_db(summary, operation, table, rows=None, query=None)

Database operation. Include counts, not raw data.

dispatch.dispatch_db(
    "Inserted invoice records",
    operation="insert",
    table="invoices",
    rows=15
)

dispatch.dispatch_db(
    "Updated order status",
    operation="update",
    table="orders",
    rows=1,
    query="UPDATE orders SET status = 'complete' WHERE id = ?"
)

dispatch_model(summary, model, input_summary=None)

Display Pydantic model data as an intermediate step. The model's field values are automatically serialized and shown in the timeline.

from models.invoice import Invoice

invoice = Invoice(**data)
dispatch.dispatch_model(
    "Validated invoice data",
    model=invoice,
    input_summary="12 line items, total $1,234.56"
)
# Timeline will show: model: Invoice, temperature: 10, unit: celsius, etc.

Best Practices

Write Good Summaries

Summaries should be:

  • Present tense: "Processing invoices" not "Processed invoices"
  • User-focused: What matters to them, not implementation details
  • Concise: Single line, max 512 characters
  • Specific: "Extracted 12 line items" not "Extracted data"

Include Relevant Context

# Good - includes context
dispatch.dispatch_api_call(
    "Confirming appointment for John Smith",
    service="phonic",
    operation="outbound_call",
    phone="+1234567890"
)

# Bad - missing context
dispatch.dispatch_api_call(
    "Made phone call",
    service="phonic",
    operation="outbound_call"
)

Handle Optional Dispatch

The client returns False on any error and never raises exceptions:

# Dispatch failures don't affect execution
result = dispatch.dispatch_text("Starting processing")
# result is False if dispatch failed, but code continues

# Or check explicitly
if not dispatch.dispatch_text("Starting"):
    print("Dispatch failed (non-critical)")

Anti-Patterns

Don't Dispatch Tool Traces

# BAD - Too granular
dispatch.dispatch_text("Reading file config.json")
dispatch.dispatch_text("Parsing JSON content")
dispatch.dispatch_text("Validating schema")
dispatch.dispatch_text("Writing to output.json")

# GOOD - Meaningful milestone
dispatch.dispatch_text("Configuration processed successfully")

Don't Dispatch Large Payloads

# BAD - Raw data in dispatch
dispatch.dispatch_json("Extracted data", {"rows": [...100 rows...]})

# GOOD - Summary only
dispatch.dispatch_db("Extracted invoice data", operation="select", table="invoices", rows=100)

Don't Dispatch Sensitive Data

# BAD - Contains API key
dispatch.dispatch_api_call("Calling API", service="mapbox", api_key="sk-...")

# GOOD - Key is automatically redacted, but don't include it
dispatch.dispatch_api_call("Geocoding address", service="mapbox", operation="geocode")

Scripts

scripts/dispatch.py

Validation script that emits test dispatches:

# Run with mock environment
export FULCRUM_DISPATCH_URL="http://localhost:8000/dispatch"
export FULCRUM_DISPATCH_TOKEN="test-token"
export FULCRUM_TICKET_UUID="test-ticket-uuid"
export FULCRUM_RUN_UUID="test-run-uuid"

uv run skills/fulcrum-sdk/scripts/dispatch.py

References

For the complete dispatch contract specification, see references/dispatch-contract.md.

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon