Repository navigation
feat(studio): R09 trigger host respects meta.enabled - #1114
Merged
Merged
Conversation
Channel triggers (telegram, slack, github, teams, discord, http) now
mount per agent through the SDK's own channel factories, so signature
verification, sessions, replies and approval buttons behave exactly as
under `lousho dev`.
- server/triggers/channelTriggers.ts: CHANNEL_SECRET_KEYS aligned with
the spec schema's required sets, channelFromTrigger secret resolution
(github botName falls back to the spec name), one mountChannels call
per agent, duplicate-kind and missing-secret errors surfaced via
status(), remount on every TriggerHost reload via a new onReload hook
- The channel middleware buffers the raw body itself and replays the
exact bytes into the SDK handler, so platform signatures verify;
registered before the /hooks express.raw mount, ahead of the JSON
parser, with workspaceOf resolved earlier for the same reason
- Abort listeners run before SDK handling on { channel, headers, body }
(abortListeners.ts - the R03b core the webhook route will reuse);
a non-true filter answers 200 { ok, skipped: 'filter' } without
touching the SDK
- Channel turns build the working-copy agent through buildRunnableAgent
with trace-file + observe exporters; they land in Runs/Traces, never
the test-pane queue
- GET /agents/:id/triggers/status + runtimeClient.channelTriggerStatus;
the Overview Triggers card shows each channel's webhook URL, Copy,
mount errors, and the Runs/Traces note
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The webhook route now runs applyAbortListeners after signature verification and before the filter/dispatch (the channel path already did): a `when: true` listener cancels matching queued/running runs of the watched trigger with reason abort_listener:<id>, and the 202 response lists the cancelled runKeys. Stopped runs end 'stopped', so the L12 failure streak never counts them. Unit coverage for scopes, strict-true when, expression-error skip and trigger matching, plus two route tests (cancel on matching PR, no cancel on a different one). Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
TriggerHost caches meta.enabled per agent (refreshed on every reload,
which agent.changed 'meta' already triggers), rejects dispatch with
'disabled' for turned-off agents, and drops + logs queued runs on the
enabled->disabled transition while a running run finishes. Webhook
answers 409 LOUSHO_AGENT_DISABLED right after auth (no event processing
beyond it); channels answer 200 { ok, skipped: 'disabled' } before the
SDK handler; cron's existing disabled branch logs the contract skip
line and the cursor still advances; chain dispatches inherit the same
reason. Manual runs are untouched. UI: the Overview Triggers card shows
the off note (auto_failure_streak gets the streak copy) and non-chat
trigger nodes dim with node-trigger-off + the contract tooltip.
Generated with [Devin](https://devin.ai)
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
LinuxDevil
force-pushed
the
fb-r03b-abort-listeners
branch
from
November 12, 2026 23:38
317b2fc to
ef4d3ca
Compare
LinuxDevil
force-pushed
the
fb-r09-enabled-gate
branch
from
November 12, 2026 23:39
ed7f418 to
f4b0e49
Compare
LinuxDevil
force-pushed
the
fb-r03b-abort-listeners
branch
from
November 14, 2026 10:44
ef4d3ca to
f7610a2
Compare
# Conflicts: # apps/agent-forge/server/__tests__/channelTriggers.test.ts # apps/agent-forge/server/__tests__/hooksRoute.test.ts # apps/agent-forge/server/__tests__/workspaceSeam.test.ts # apps/agent-forge/server/app.ts # apps/agent-forge/server/triggers/channelTriggers.ts # apps/agent-forge/server/workspace/__tests__/middleware.test.ts # apps/agent-forge/server/workspace/context.ts # apps/agent-forge/shared/wireTypes.ts # apps/agent-forge/src/canvas/NodeCard.tsx # apps/agent-forge/src/components/CanvasArea.tsx # apps/agent-forge/src/screens/agentDetail/AgentOverviewTab.tsx # apps/agent-forge/src/screens/agentDetail/__tests__/AgentOverviewTab.test.tsx # apps/agent-forge/src/screens/agentDetail/agentOverview.css
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
TriggerHostcachesmeta.enabledper agent, refreshed insidereload()(everyagent.changed, incl.reason: 'meta');dispatch()rejects withreason: 'disabled'first, and an enabled→disabled transition drops + logs queued runs (Dropped <n> queued runs: the agent was turned off) while the in-flight run finishes{ error: 'Agent is turned off', code: 'LOUSHO_AGENT_DISABLED' }immediately after signature auth — no abort listeners, filter or dispatch for a turned-off agent; the dispatch-leveldisabledreason maps to the same 409{ ok: true, skipped: 'disabled' }before the SDK handler so platforms don't retry; cron's existingdisabledbranch logsCron trigger <id> skipped: the agent is turned offwith the cursor already advanced; chain dispatches inherit the same reason through the host/run,/message, test pane) are not gated.triggers-off-note(warning-soft/--warning-border, shares.agent-detail-noticebox styles) with theauto_failure_streakcopy variant; non-chat trigger nodes getnode-trigger-off(opacity .6) +title="Off — triggers won't fire"; chat triggers never dimTest plan
triggerHost.test.ts+3: disabled dispatch/isEnabled, drop-on-disable with running run finishing, no re-drop/re-loghooksRoute.test.ts+2: 409 path, auth-before-gate order (unsigned remote still 403)cronTriggers.test.ts+1: skip log + cursor advance;channelTriggers.test.ts+1: 200 skipped disabledAgentOverviewTab.test.tsx+3: off note both copies, hidden when enabled/chat-onlyStacked on #1113 (needs the R08b channel path + R03b webhook abort-listener ordering).
Generated with Devin