Skip to content

Commit 0fcb7dc

Browse files
Merge pull request #3 from IsraelAraujo70/feat/workflows-triggers
feat(friend): workflow triggers as their own nodes, with webhook test mode and a bot trigger
2 parents 141f3fa + 0b7b19a commit 0fcb7dc

106 files changed

Lines changed: 4018 additions & 560 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎FORK.md‎

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,9 @@ On the host, under the T3 home (`~/.t3`):
336336
- `workflows/definitions/` is a git repository with one `<id>.workflow.yaml` per workflow. Every save
337337
is a commit; the server ingests it at startup and picks up hand edits. Library templates ship in
338338
[`apps/server/src/bots/workflows/library/`](apps/server/src/bots/workflows/library/).
339-
- `userdata/bots.sqlite` holds each run's journal (`workflow_facts`) and the run list (`workflow_runs`).
339+
- `userdata/bots.sqlite` holds each run's journal (`workflow_facts`), the run list (`workflow_runs`),
340+
each trigger's last event (`workflow_trigger_samples`) and the deliveries captured as test events
341+
(`workflow_test_deliveries`).
340342
- `workflows/runs/<runId>/` holds the receipts of each command unit (`started.json`, `exit.json`),
341343
the step directories, build artifacts, and the run's worktrees until `retain` lets them go.
342344

@@ -370,8 +372,19 @@ of the Android Bots tab. It lists the bots host's workflows with their last run.
370372

371373
- **Create one.** "From template" copies a ready routine under a new name; "New workflow" starts
372374
blank. A new workflow starts turned off.
373-
- **Run it.** Turn it on, then press "Run now" and fill in the parameters, or keep the defaults.
374-
While it is on, its triggers (a schedule, a new upstream tag) start runs by themselves.
375+
- **Run it.** Press "Run now" and fill in the parameters, or keep the defaults. Run now needs a
376+
manual trigger in the workflow and works even while the workflow is off, so you can try it before
377+
arming anything; a suspended workflow stays blocked until you resume it.
378+
- **Triggers.** Each trigger is a node on the left of the canvas: manual (Run now), bot, called by
379+
another workflow, webhook, schedule, new upstream tag, PR merged, thread finished, workflow
380+
failed. Every trigger starts the whole graph. The workflow switch arms them all; each trigger also
381+
has its own switch in its inspector and on Android. A trigger node shows when it last fired.
382+
- **Webhooks.** A webhook trigger's inspector shows its URL with a copy button and the last event
383+
it received. "Listen for test event" waits two minutes for the next request: that request is
384+
captured, starts no run, and fills the event shown there and the list of `{{ trigger.* }}` paths
385+
you can copy into prompts and params. "Run with this event" then runs the workflow with it. A test
386+
delivery uses up its replay key, so the sender's redelivery of that same request is ignored.
387+
Values from a trigger reach agent prompts marked as untrusted data.
375388
- **Follow a run.** The run page shows each step with its status, cost and the thread its agent
376389
worked in. A failed step can be retried from there.
377390
- **Approve a gate.** A run that needs you stops at a gate and shows the question with its options
@@ -380,9 +393,11 @@ of the Android Bots tab. It lists the bots host's workflows with their last run.
380393
- **Read the result.** A run ends ready for review (with a draft pull request), with no gain,
381394
blocked, or failed. Merging into `friend/main` stays your step.
382395

383-
Any bot can run, cancel or turn on a workflow when you ask in your own message in its conversation,
384-
or when you allow it to manage bots in Settings → Bots. Messages from another bot, scheduled tasks
385-
and workflow steps do not count as your request.
396+
A bot runs a workflow only through the workflow's bot trigger, whose description tells bots when to
397+
use it and whose optional list says which bots may. On top of that, a bot runs, cancels or turns on
398+
a workflow only when you ask in your own message in its conversation, or when you allow it to manage
399+
bots in Settings → Bots. Messages from another bot, scheduled tasks and workflow steps do not count
400+
as your request.
386401

387402
Each workflow is a file in `~/.t3/workflows/definitions` on the bots host. Saving in the editor
388403
commits it there, and a hand edit of the file shows up in the app. Two templates are not ready to run

‎apps/mobile/src/bots/workflows/WorkflowDetailScreen.tsx‎

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import * as Clipboard from "expo-clipboard";
2+
import { relativeTime } from "../../lib/time";
13
import { useAtomValue } from "@effect/atom-react";
24
import * as Schema from "effect/Schema";
35
import { useState } from "react";
@@ -98,12 +100,14 @@ function DetailContent({
98100
{document.definition && (
99101
<>
100102
<View className="flex-row flex-wrap gap-2">
101-
<ControlPill
102-
label="Run now"
103-
variant="primary"
104-
disabled={!canRun || !document.valid}
105-
onPress={() => setShowRun(true)}
106-
/>
103+
{document.triggers.some((trigger) => trigger.kind === "manual") && (
104+
<ControlPill
105+
label="Run now"
106+
variant="primary"
107+
disabled={!canRun || !document.valid}
108+
onPress={() => setShowRun(true)}
109+
/>
110+
)}
107111
{document.valid && (
108112
<ControlPill
109113
label="Edit"
@@ -115,10 +119,44 @@ function DetailContent({
115119
</View>
116120
<WorkflowGraph model={toCanvasModel(document.definition)} />
117121
<ListSection title="Triggers">
118-
{(document.definition.triggers ?? []).map((trigger) => (
119-
<Text key={trigger.id}>
120-
{trigger.id} · {trigger.kind}
121-
</Text>
122+
{document.triggers.map((trigger) => (
123+
<View key={trigger.id} className="gap-2 p-3">
124+
<Text>{trigger.title ?? trigger.id}</Text>
125+
<Text>{trigger.label}</Text>
126+
{trigger.kind !== "manual" && (
127+
<Switch
128+
accessibilityLabel={`Enable ${trigger.title ?? trigger.id}`}
129+
value={trigger.enabled !== false}
130+
disabled={pending || !canEnable}
131+
onValueChange={async (enabled) => {
132+
setPending(true);
133+
await enable({
134+
environmentId,
135+
input: {
136+
workflowId,
137+
triggerId: trigger.id,
138+
enabled,
139+
clientRequestId: Crypto.randomUUID(),
140+
},
141+
});
142+
setPending(false);
143+
}}
144+
/>
145+
)}
146+
{trigger.lastFiredAt && (
147+
<Text>Last event {relativeTime(trigger.lastFiredAt)}</Text>
148+
)}
149+
{trigger.kind === "webhook" && trigger.webhookUrl && (
150+
<>
151+
<Text selectable>{trigger.webhookUrl}</Text>
152+
<ControlPill
153+
label="Copy"
154+
variant="pill"
155+
onPress={() => void Clipboard.setStringAsync(trigger.webhookUrl!)}
156+
/>
157+
</>
158+
)}
159+
</View>
122160
))}
123161
</ListSection>
124162
{showRun && (

‎apps/mobile/src/bots/workflows/WorkflowGraph.logic.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ describe("mobile graph geometry", () => {
55
it("resolves body coordinates before fitting the graph", () => {
66
const result = graphGeometry(
77
{
8+
triggers: [],
9+
entries: [],
810
nodes: [
911
{
1012
id: "parent",

0 commit comments

Comments
 (0)