Repository navigation
feat(friend): workflow triggers as their own nodes, with webhook test mode and a bot trigger - #3
Merged
IsraelAraujo70 merged 43 commits intoNov 2, 2026
Conversation
Triggers become a map keyed by id with three declared kinds (manual, bot, call), a switch per trigger (TriggerToggled), the trigger id on run summaries, the webhook sample and test-mode RPCs, and the run trigger payload read on demand. The policy, samples and shared helpers land with their first consumers; the server and clients compile again next. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
The compiler, trigger sources, catalog, definitions summaries and the four library workflows read `triggers` as a map. Run summaries and run views carry the trigger id; the trigger-sample and test-mode RPCs answer "unavailable" until their services land. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…liveries Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Moves the bot and parent caller lookup out of the decider into `callerOf`, reads trigger ids with `Object.hasOwn`, and keeps imports below the module docblock. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`botEntries` takes the definition, switches and trigger states from each workflow's document instead of recompiling HEAD and reading the status twice; trigger ids are looked up with `Object.hasOwn`. The test now covers the allow-list and the workflow switch. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
The style registry now has `trigger:<kind>` rows; the palette lists node kinds only, so it skips them instead of offering buttons that would add nodes of a kind that does not exist. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
A listener that registers while a test event is being stored stays in the set and waits for the next delivery instead of being dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…eplay an event Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
…es over MCP Covers the "no bot trigger" and "not allowed" refusals for a manager bot, the `bot` entry `workflow_list` returns, and `triggerId` reaching `setEnabled`. A param error now reads `count: expected number` once, not with the name twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Trigger nodes get a source handle, an "Off" pill when switched off, and on a run the static ok halo for the trigger that started it (the others dimmed). Entry edges draw dashed, carry no label and cannot be edited. Clicking a trigger selects it. Client atoms read a run's trigger payload, a trigger's last event and the webhook listen stream. Co-Authored-By: GPT-6.1 Sol <noreply@openai.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
A node or trigger named like an Object property (`constructor`) no longer opens the trigger inspector or reads as taken. "Run with this event" shows the server's reason when the run cannot start. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nual trigger Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
The guide explains Run now without arming the workflow, trigger nodes and their own switches, the webhook test mode (and that a test delivery uses up its replay key), and the bot trigger. The design doc's §5 now describes the trigger map, the policy, the bot trigger, samples and the test mode, and its YAML excerpts use the map form. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: GPT-6.1 Sol <noreply@openai.com>
The listen subscription kept the shared five-minute idle TTL, so after "Stop" the server kept a listener and could capture the next real delivery without a run; it also had no terminal predicate. It now releases at once and completes on Captured or Expired. The panel disables Listen and "Run with this event" without the matching permission and shows a listen error. A child run's "call" placeholder no longer resolves to a trigger of another kind that happens to be named `call`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The contracts test lists the read-scoped workflow RPCs; it now includes `run.trigger` and `trigger.sample`. The Android trigger switch drops a try/finally the React Compiler cannot lower, as the workflow switch does (the command resolves to a result, it does not throw). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
IsraelAraujo70
merged commit Nov 2, 2026
0fcb7dc
into
feat/workflows-integration
7 of 18 checks passed
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.
Stacked on #2. Review that one first; this diff is only the triggers work on top of it.
Why
The owner asked for triggers that read as their own things, the way n8n does it: webhook, manual and bot triggers you can see, switch and test. In #2, triggers live only in the YAML. Manual (Run now) and bot (
workflow_run) runs are implicit, and the canvas shows no trigger at all.What changed
triggersbecomes a map keyed by id, sharing the node id namespace. Three kinds are now declared:manual,bot(adescriptionand an optionalbotsallow-list) andcall(an optionalfromlist). A workflow has at most one of each. The six event kinds stay, each with an optionaltitle, and the four library templates migrate. Migration 014 is edited in place, since it never ran in production.TriggerToggledfact, on by default, works next to the workflow's switch. One pure function,triggerBlockintriggers/policy.ts, decides whether a fired trigger becomes a run. The workflow decider is the authority; the RPC and MCP handlers call the same function to refuse early.manualtrigger. Runs the owner starts from their own session ignore both switches, like n8n's "Execute workflow"; suspension still blocks them.trigger.body,trigger.headers(redacted) andtrigger.query, capped at 256 KiB, through the existingScheduledTaskServiceseam.workflow_trigger_samples.paramsmapping and the{{ trigger.body.* }}expressions run for real.bottrigger is the only way a bot can run a workflow.workflow_listshows bots the trigger's description and parameter schema, and whether a run would start now. The rule from feat(friend): workflows on the bots host, with hidden step threads #2 still applies on top: a bot withoutcanManageBotsmay run or enable a workflow only during a turn that came from the owner's own message. Gates stay owner-only.trigger.*values inside an agentpromptare wrapped as untrusted data.FORK.md, the user guide and the design doc are updated. Threemcp-tool-usecases are added.Scope
ScheduledTaskService.tsseam, which now passes the request through; itsscripts/friend/seams.tsentry is updated.nullfor a command withoutparsebefore this PR. It is still unfixed.Blast Radius
Workflows and their MCP tools only. Changes in behavior:
manualtrigger shows no Run now button.bottrigger listing it, or open to any bot.replayKey, so a redelivery with the same key is dropped later; the user guide says so.The deploy is the same as #2: migration 014 at boot,
vp i, a web rebuild, a server restart, and new builds of the Mac app and the APK.Verification
vp run check:changed friend/mainpasses: format, lint, knip, typecheck of 39 packages, 89 test files and the React Compiler check.node scripts/friend/check-seams.ts: OK. 207 test files with 2,417 tests pass across the touched suites.~/.cache/friend-tmp/triggers/proof/out/proof.logon the bots host).issue 42from{{ trigger.body.issue.number }}into the step.disabledwhen the workflow or that trigger is off.run.triggerreturns the payload and truncates it abovemaxBytes.~/.cache/friend-tmp/triggers/proof/shots/INDEX.mdon the bots host.callcaptured child runs.Not verified here:
cd evals && pnpm mcp-tool-use --case workflow-run-needs-bot-trigger,workflow-run-uses-bot-params,workflow-enable-one-trigger --out runs/workflow-triggers.Work by Claude Opus 5.5 (lead) with GPT-6.1 Sol workers, in T3 Code.
🤖 Generated with Claude Code