@nightowlsdev/engine-trigger-chat
EngineAdapter, reduced governanceExperimentalAn adapter for ONE specific case: you already run a Trigger.dev `chat.agent` Session on your own account and want to front it as a Night Owls agent. It's an adapter (reduced governance, tier-2 events), and experimental (Trigger's `chat.agent` surface only just GA'd — verify it's reachable in your account first). For the usual durable + governed run, use `@nightowlsdev/runner-background` with a native engine instead — that's the first-class durable path.
What it does
**Use this when:** someone has ALREADY deployed a Trigger.dev `chat.agent` Session (their own agent loop, on their Trigger account) and you want to expose it as a single agent inside a Night Owls swarm — chat with it, see its tool calls, delegate to it. That is the one job this adapter does. **For everything else — a durable, governed run you own — use `@nightowlsdev/runner-background` with a native engine (Mastra or AI SDK) instead:** that runs the loop in YOUR process on Trigger's durable primitives (durable tasks + wait tokens), so the full governance plane (fail-closed tool gate, cost caps, secrets, HITL approval) applies AND you get durable park/resume. That is what "durable" means everywhere else in these docs; this adapter is the narrow exception. **How.** `triggerChatEngine({ taskId, secretKey?, baseURL?, modelId? })` starts/resumes a Session over `@trigger.dev/sdk` and maps its `UIMessageChunk` wire onto Night Owls `SwarmEvent`s, then `defineSwarm({ engine })` (or `owl install engine-trigger-chat`) wires it. Reads `TRIGGER_SECRET_KEY` from the env; requires Node ≥ 22. **What it can and can't do (the adapter tier, stated honestly).** Because the loop runs inside the remote Session — a process Night Owls does NOT execute inside — the in-loop governance can't reach it: `kind: 'adapter'`, no fail-closed tool gate, no cost kill-switch, no pre-generation veto. What DOES cross the boundary: tool calls/results are VISIBLE (`events.tier: 2`, unlike the opaque `engine-a2a`), the remote's own per-tool `needsApproval` is RELAYED to a Night Owls approval, durable park/resume is native to the Trigger runtime (`hitl.durableResume: true`), and per-generation `swarm.usage` is metered best-effort when the wire attaches usage metadata (the per-turn `swarm.turn_usage` boundary always fires). **Experimental** only in the sense that Trigger's `chat.agent` surface GA'd very recently and this adapter is built against its types — verify `chat.agent` is reachable in your Trigger account before relying on it. Ships a `nightOwlsPlugin` for CLI scaffolding.
Install
pnpm add @nightowlsdev/engine-trigger-chatKey exports
- triggerChatEngine
- TriggerChatEngine
- TRIGGER_CHAT_ENGINE_CAPABILITIES
- createTriggerSessionTransport
- createChunkMapper / extractUsageFromMessageMetadata / mapControlEvent
- nightOwlsPlugin
Usage
import { defineSwarm } from "@nightowlsdev/core";
import { triggerChatEngine, TRIGGER_CHAT_ENGINE_CAPABILITIES } from "@nightowlsdev/engine-trigger-chat";
// ⚠ EXPERIMENTAL: targets Trigger's chat.agent / Sessions surface, which is NOT generally available
// (unexercised against a live server). For a GA durable runtime use @nightowlsdev/runner-background
// (Trigger v4 durable tasks + wait tokens) instead. This engine is an ADAPTER over a deployed
// chat.agent Session: the loop runs on YOUR Trigger task, rendered here as one Night Owls agent.
const swarm = defineSwarm({ agents, engine: triggerChatEngine({ taskId: "my-chat-agent" }) });
// Durable by the REMOTE (Trigger's own runtime), not by our storage, always true, no `durable` flag.
console.log(TRIGGER_CHAT_ENGINE_CAPABILITIES.hitl.durableResume); // true (static)What it provides
engine-trigger-chat is an EXPERIMENTAL protocol adapter over a deployed Trigger.dev chat.agent Session: the chat.agent loop runs inside the customer's OWN Trigger.dev task, so it renders that remote Session as a single Night Owls agent rather than executing in-process. Because chat.agent's wire IS the AI-SDK UIMessageChunk stream, tool_call / tool_result ARE visible (tier 2 — richer than opaque A2A). ⚠ It targets Trigger's chat.agent / Sessions surface, which GA'd only just before this package was written and is UNEXERCISED against a live server: every method is grounded in the installed types but never verified end-to-end. For a GA durable runtime use @nightowlsdev/runner-background (Trigger v4 durable TASKS + wait tokens) instead — that is what 'durable' means everywhere else in these docs.
When to use it
- You are exploring / spiking a deployed Trigger.dev chat.agent Session and want to surface it as one Night Owls agent — after verifying chat.agent is actually reachable in your Trigger account.
- You want a remote loop whose tool_call/tool_result are visible (tier 2, via the UIMessageChunk stream), not just messages and status.
- You want the remote's per-tool needsApproval relayed into Night Owls' ask/approval HITL, with the Session parked durably on Trigger's own runtime across process death / redeploys.
When not to
- You are building anything production — this engine is a spike over a brand-new surface unexercised against a live server; use @nightowlsdev/runner-background for the real, generally-available durable layer.
- You need Night Owls' governance to enforce — the loop runs inside someone else's deployed task, so the pre-generation reserve, fail-closed tool gate, and cost kill-switch physically cannot reach it (governance all false/'none').
- You need guaranteed per-step usage — chat.agent's finish-step carries no usage field; swarm.usage fires zero-or-more times per segment (only when the host attaches messageMetadata), so it's best-effort, not a per-step guarantee.
Alternatives
- @nightowlsdev/runner-backgroundYou want a GENERALLY-AVAILABLE durable runtime on Trigger — Trigger v4 durable tasks + wait.createToken/forToken/completeToken. This is the framework's real durable layer; prefer it over this experimental engine for anything real.
- engine-eveYou want a tier-2 remote adapter over a mature, exercised surface — a deployed Vercel Eve app parked on Vercel Workflows — rather than a brand-new, unexercised Trigger surface.
- A native engine (engine-mastra / engine-ai-sdk / engine-openai-agents)You control the loop and want the full governance plane in-process instead of adapting a remote you don't execute inside of.
Strengths
- Tier-2 events: because chat.agent's wire is the AI-SDK UIMessageChunk stream, tool_call/tool_result are visible — richer than opaque A2A (tier 1).
- durableResume is STATICALLY true — a Trigger Session/run is durable and resumable by externalId/runId on Trigger's runtime, parked across process death and redeploys, independent of your storage.
- The remote's per-tool needsApproval is relayed into Night Owls' ask/approval loop; runs.cancel is a real remote compute-stop.
- Simple wiring (taskId + ambient TRIGGER_SECRET_KEY), an honest modelId fallback for pricing, and engine-wall clean over @trigger.dev/sdk.
Limits & trade-offs
- ⚠ EXPERIMENTAL / UNEXERCISED: it targets chat.agent Sessions, which GA'd one day before the package was written and were never exercised against a live server — do not build a production loop on it without verifying the surface is reachable.
- Reduced-governance adapter tier: governance is all false — no pre-generation reserve, no fail-closed tool gate, no cost kill-switch reach into the remote task; approval is the remote's own, merely relayed.
- Usage is best-effort: chat.agent's finish-step carries no usage, so swarm.usage fires zero-or-more per segment (only when the host attaches messageMetadata); swarm.turn_usage still fires exactly once, aggregating whatever was seen.
- No delegation, workflows, rules, scratchpad, or client tools; no OTel telemetry (metering only); cancellation is between-steps (no guaranteed mid-stream ordering); requires Node >= 22.
How it works
triggerChatEngine({ taskId, secretKey?, baseURL?, triggerConfig?, modelId? }) builds a Trigger Session transport over @trigger.dev/sdk (or takes a test transport override) and returns (opts) => new TriggerChatEngine(opts, cfg). It starts/resumes a Session (sessions.start of type chat.agent) against the deployed task and maps the UIMessageChunk stream onto SwarmEvents via createChunkMapper, pulling token counts with extractUsageFromMessageMetadata when present and control events via mapControlEvent. hitl.durableResume is static true because Trigger's runtime parks the Session durably; there is no per-instance durable flag. TRIGGER_SECRET_KEY is read from the environment by default. Reads/history come from OUR event log.
Examples
Adapt a deployed chat.agent Session (experimental)
Verify chat.agent is reachable in your Trigger account first. TRIGGER_SECRET_KEY is read from the environment by default.
import { defineSwarm } from "@nightowlsdev/core";
import { triggerChatEngine } from "@nightowlsdev/engine-trigger-chat";
export default defineSwarm({
agents,
engine: triggerChatEngine({ taskId: "my-chat-agent" }),
});Confirm the tier-2, remote-durable, reduced-governance posture
Durable is a fact of Trigger's runtime (static true) — there is no durable flag. Governance is off because the loop runs inside someone else's task.
import { TRIGGER_CHAT_ENGINE_CAPABILITIES } from "@nightowlsdev/engine-trigger-chat";
console.log(TRIGGER_CHAT_ENGINE_CAPABILITIES.events.tier); // 2 — tool events visible
console.log(TRIGGER_CHAT_ENGINE_CAPABILITIES.hitl.durableResume); // true (static — Trigger's runtime)
console.log(TRIGGER_CHAT_ENGINE_CAPABILITIES.governance.preToolCall); // "none"Doing the parts it doesn't support
- A production durable runtime on TriggerDon't use this engine for it — use @nightowlsdev/runner-background, the GA durable layer built on Trigger v4 durable tasks + wait tokens. This engine's target surface (chat.agent Sessions) only just GA'd and is unexercised against a live server.
- Enforcing governance on the remote taskYou can't — the chat.agent loop runs inside the customer's own deployed task, so the pre-generation reserve, fail-closed tool gate, and cost caps have nothing to bind to. Its per-tool approval is the remote's own needsApproval, relayed. Use a native engine in-process for real enforcement.
- Guaranteed per-step usage / pricingchat.agent's finish-step carries no usage. Attach usage-shaped messageMetadata on the host's finish chunk so swarm.usage can fire, and set the modelId option so swarm.turn_usage can be priced (else it falls back to the agent row's modelId, then 'unknown' — metered, not enforced).
Related
- runner-background — The GA durable layer this engine is NOT — Trigger v4 durable tasks + wait tokens; prefer it for anything real.
- engine-eve — A sibling tier-2 remote adapter over a mature, exercised surface (a Vercel Eve app).
- engine-a2a — The other remote adapter — opaque tier-1, for A2A endpoints.
- core — The required base — the adapter maps the UIMessageChunk wire into core's SwarmEvent / ask loop.