Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.svantic.com/llms.txt

Use this file to discover all available pages before exploring further.

attach

import { attach } from '@svantic/sdk';

What it is

attach() is the one-call glue between an Express app and an agent. A single call does the work of four otherwise-separate steps:
  1. Mount the agent’s A2A endpoints on your Express app (expose).
  2. Activate triggers registered on the agent via agent.add_triggers() — webhooks become Express routes, schedules become cron timers, emit triggers become event-bus listeners.
  3. Connect to Svantic (MeshConnector.connect), with automatic retries.
  4. Install SIGTERM / SIGINT handlers that deregister cleanly.
It returns an AgentHandle — a small surface for firing emit triggers and tearing the whole setup down.

When to use it

Use attach() when you’re embedding an agent into a service you already run:
  • You have an existing Express app with middleware, routes, and lifecycle.
  • You want triggers (webhooks, cron, emit) without writing the plumbing.
  • You want a single object (AgentHandle) to own teardown.
For standalone agents that don’t need to share an Express process, prefer new Agent({...}).start() — it’s simpler.

Functional usage

import express from 'express';
import { Agent, attach, type RuntimeTrigger } from '@svantic/sdk';

const app = express();
app.use(express.json());

// 1. Build the agent and its capabilities.
const agent = new Agent({
  name: 'orders',
  description: 'Order automation.',
  public_url: 'https://api.acme.com',
});
agent.define_capability({ /* fetch_order */ });
agent.define_capability({ /* cancel_order */ });

// 2. Register triggers on the agent. Forge generates these for you,
//    but you can hand-write them too.
agent.add_triggers([
  {
    type: 'webhook',
    path: '/hooks/stripe',
    prompt: 'Payment {{id}} of {{amount}} just settled; update the order.',
    payload_map: {
      id: 'data.object.id',
      amount: 'data.object.amount_total',
    },
  },
  {
    type: 'schedule',
    cron: '0 9 * * MON-FRI',
    prompt: 'Email ops the overnight order summary.',
  },
  {
    type: 'emit',
    event: 'order_cancelled',
    prompt: 'Order {{order_id}} was cancelled; issue the refund.',
  },
]);

// 3. Wire everything — A2A endpoints, triggers, Svantic connection.
const handle = await attach(app, {
  agent,
  mesh: {
    client_id: process.env.SVANTIC_CLIENT_ID!,
    client_secret: process.env.SVANTIC_CLIENT_SECRET!,
  },
});

app.listen(4000);

// 4. Fire emit triggers from anywhere in the service.
app.post('/orders/:id/cancel', async (req, res) => {
  await db.orders.cancel(req.params.id);
  handle.emit('order_cancelled', { order_id: req.params.id });
  res.sendStatus(204);
});

// 5. Teardown (attach also handles SIGTERM / SIGINT automatically).
await handle.detach();
See the Attach to Express and Triggers guides for the end-to-end story.

Signature

function attach(app: Express, config: AttachConfig): Promise<AgentHandle>

AttachConfig

FieldTypePurpose
agentAgentThe agent to mount. Capabilities and triggers must be registered before calling attach.
mesh?MeshConnectorConfig (retries/retry-delay optional)Svantic connection config. Omit to skip mesh registration entirely — useful for local-only dev.

RuntimeTrigger

One trigger definition. Every trigger carries a prompt template (with &#123;&#123;…&#125;&#125; placeholders) that is fired when the trigger matches.
FieldTypePurpose
type'webhook' | 'schedule' | 'emit'Dispatcher kind.
event?stringFor emit triggers — the event name.
path?stringFor webhook triggers — the HTTP path mounted on the Express app (POST).
cron?stringFor schedule triggers — a standard 5-field cron expression. See cron matching for supported syntax.
queue?stringReserved for future queue-backed triggers.
verify?stringOptional verification strategy (e.g. webhook HMAC). Name only — verification is plumbed by the Forge-generated code.
secret_env?stringName of the env var holding the verification secret.
payload_map?Record<string, string>Dot-notation extraction map applied before interpolation. E.g. { customer: 'event.data.customer.id' }.
promptstringPrompt template with &#123;&#123;key&#125;&#125; placeholders.

Trigger kinds

  • webhookattach() mounts app.post(trigger.path). The request body is the payload.
  • schedule — fires every minute; runs when the cron expression matches the current minute.
  • emit — listens on an internal event bus. Trigger with handle.emit(event, payload) or handle.ask(event, payload).

AgentHandle

Returned by attach().
MemberTypePurpose
emit(event, payload?)voidFire an emit trigger without waiting for a result.
ask(event, payload?)Promise<unknown>Fire an emit trigger and await the handler’s return value. Rejects after 30 s.
detach()Promise<void>Stop crons, remove event listeners, disconnect from Svantic, close the agent. Removes the internal SIGTERM/SIGINT handlers.
meshMeshConnector | nullThe underlying connector, or null when config.mesh was omitted.
agentAgentThe underlying agent.
attach() installs SIGTERM and SIGINT handlers that call detach() for you; you don’t need to wire signal handling yourself.

Exported helpers

interpolate_prompt(template, payload, payload_map?)

Replace &#123;&#123;key&#125;&#125; placeholders in template with values from payload. When payload_map is provided, values are first extracted via dot-notation (e.g. event.data.customer.id) and flattened. Unknown placeholders are left as-is (&#123;&#123;key&#125;&#125;) so they’re easy to spot in logs.

cron_matches_now(expr, now?)

Returns true when the cron expression matches the given instant (default: now). Supports:
  • * wildcard,
  • single values (5),
  • comma lists (0,15,30,45),
  • ranges (9-17),
  • steps (*/15, 9-17/2),
  • weekday names (MONSUN) in the day-of-week field.
Only 5-field cron (minute hour day-of-month month day-of-week) is supported.

Example

import express from 'express';
import { Agent, attach } from '@svantic/sdk';

const app = express();
app.use(express.json());

const agent = new Agent({ name: 'orders', description: 'Order automation.' });
agent.define_capability({ /* … */ });

agent.add_triggers([
  {
    type: 'webhook',
    path: '/hooks/stripe',
    prompt: 'A payment for {{amount}} just settled; update the order.',
    payload_map: { amount: 'data.object.amount_total' },
  },
  {
    type: 'schedule',
    cron: '0 9 * * MON-FRI',
    prompt: 'Email the ops team the overnight order summary.',
  },
]);

const handle = await attach(app, {
  agent,
  mesh: {
    client_id: process.env.SVANTIC_CLIENT_ID!,
    client_secret: process.env.SVANTIC_CLIENT_SECRET!,
  },
});

app.listen(4000);

// later, from anywhere in the service:
handle.emit('order_cancelled', { order_id: 'ord_42' });

See also