loopengine 0.3.11 → 0.3.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +97 -8
- package/README.md +16 -0
- package/THIRD_PARTY_NOTICES.md +35 -0
- package/dist/core/system-agents/workflow-example-messages.d.ts +13 -0
- package/dist/core/system-agents/workflow-example-messages.js +116 -0
- package/dist/core/system-agents/workflow-examples.d.ts +6 -0
- package/dist/core/system-agents/workflow-examples.js +9 -8
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/web/workflow-page.js +124 -27
- package/package.json +4 -3
package/LICENSE
CHANGED
|
@@ -1,13 +1,102 @@
|
|
|
1
|
-
MIT License
|
|
1
|
+
# Functional Source License, Version 1.1, MIT Future License
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## Abbreviation
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
FSL-1.1-MIT
|
|
6
|
+
|
|
7
|
+
## Notice
|
|
8
|
+
|
|
9
|
+
Copyright 2026 Evan Yan
|
|
10
|
+
|
|
11
|
+
## Terms and Conditions
|
|
12
|
+
|
|
13
|
+
### Licensor ("We")
|
|
14
|
+
|
|
15
|
+
The party offering the Software under these Terms and Conditions.
|
|
16
|
+
|
|
17
|
+
### The Software
|
|
18
|
+
|
|
19
|
+
The "Software" is each version of the software that we make available under
|
|
20
|
+
these Terms and Conditions, as indicated by our inclusion of these Terms and
|
|
21
|
+
Conditions with the Software.
|
|
22
|
+
|
|
23
|
+
### License Grant
|
|
24
|
+
|
|
25
|
+
Subject to your compliance with this License Grant and the Patents,
|
|
26
|
+
Redistribution and Trademark clauses below, we hereby grant you the right to
|
|
27
|
+
use, copy, modify, create derivative works, publicly perform, publicly display
|
|
28
|
+
and redistribute the Software for any Permitted Purpose identified below.
|
|
29
|
+
|
|
30
|
+
### Permitted Purpose
|
|
31
|
+
|
|
32
|
+
A Permitted Purpose is any purpose other than a Competing Use. A Competing Use
|
|
33
|
+
means making the Software available to others in a commercial product or
|
|
34
|
+
service that:
|
|
35
|
+
|
|
36
|
+
1. substitutes for the Software;
|
|
37
|
+
|
|
38
|
+
2. substitutes for any other product or service we offer using the Software
|
|
39
|
+
that exists as of the date we make the Software available; or
|
|
40
|
+
|
|
41
|
+
3. offers the same or substantially similar functionality as the Software.
|
|
42
|
+
|
|
43
|
+
Permitted Purposes specifically include using the Software:
|
|
44
|
+
|
|
45
|
+
1. for your internal use and access;
|
|
46
|
+
|
|
47
|
+
2. for non-commercial education;
|
|
48
|
+
|
|
49
|
+
3. for non-commercial research; and
|
|
50
|
+
|
|
51
|
+
4. in connection with professional services that you provide to a licensee
|
|
52
|
+
using the Software in accordance with these Terms and Conditions.
|
|
53
|
+
|
|
54
|
+
### Patents
|
|
55
|
+
|
|
56
|
+
To the extent your use for a Permitted Purpose would necessarily infringe our
|
|
57
|
+
patents, the license grant above includes a license under our patents. If you
|
|
58
|
+
make a claim against any party that the Software infringes or contributes to
|
|
59
|
+
the infringement of any patent, then your patent license to the Software ends
|
|
60
|
+
immediately.
|
|
61
|
+
|
|
62
|
+
### Redistribution
|
|
63
|
+
|
|
64
|
+
The Terms and Conditions apply to all copies, modifications and derivatives of
|
|
65
|
+
the Software.
|
|
66
|
+
|
|
67
|
+
If you redistribute any copies, modifications or derivatives of the Software,
|
|
68
|
+
you must include a copy of or a link to these Terms and Conditions and not
|
|
69
|
+
remove any copyright notices provided in or with the Software.
|
|
70
|
+
|
|
71
|
+
### Disclaimer
|
|
72
|
+
|
|
73
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTIES OF ANY KIND, EXPRESS OR
|
|
74
|
+
IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF FITNESS FOR A PARTICULAR
|
|
75
|
+
PURPOSE, MERCHANTABILITY, TITLE OR NON-INFRINGEMENT.
|
|
76
|
+
|
|
77
|
+
IN NO EVENT WILL WE HAVE ANY LIABILITY TO YOU ARISING OUT OF OR RELATED TO THE
|
|
78
|
+
SOFTWARE, INCLUDING INDIRECT, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES,
|
|
79
|
+
EVEN IF WE HAVE BEEN INFORMED OF THEIR POSSIBILITY IN ADVANCE.
|
|
80
|
+
|
|
81
|
+
### Trademarks
|
|
82
|
+
|
|
83
|
+
Except for displaying the License Details and identifying us as the origin of
|
|
84
|
+
the Software, you have no right under these Terms and Conditions to use our
|
|
85
|
+
trademarks, trade names, service marks or product names.
|
|
86
|
+
|
|
87
|
+
## Grant of Future License
|
|
88
|
+
|
|
89
|
+
We hereby irrevocably grant you an additional license to use the Software under
|
|
90
|
+
the MIT license that is effective on the second anniversary of the date we make
|
|
91
|
+
the Software available. On or after that date, you may use the Software under
|
|
92
|
+
the MIT license, in which case the following will apply:
|
|
93
|
+
|
|
94
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
95
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
96
|
+
the Software without restriction, including without limitation the rights to
|
|
97
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
98
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
99
|
+
so, subject to the following conditions:
|
|
11
100
|
|
|
12
101
|
The above copyright notice and this permission notice shall be included in all
|
|
13
102
|
copies or substantial portions of the Software.
|
package/README.md
CHANGED
|
@@ -481,3 +481,19 @@ and doesn't count as activity.
|
|
|
481
481
|
|
|
482
482
|
`deploy/workspace/Dockerfile` is a runtime image for this: it serves a
|
|
483
483
|
scaffolded project mounted at `/workspace` instead of baking agents in.
|
|
484
|
+
|
|
485
|
+
## License
|
|
486
|
+
|
|
487
|
+
LoopEngine is under the [Functional Source License, Version 1.1, MIT
|
|
488
|
+
Future License](LICENSE) (FSL-1.1-MIT). In short:
|
|
489
|
+
|
|
490
|
+
- You can use, change and self-host it for anything, including running
|
|
491
|
+
your own business on it, except offering it to others as a competing
|
|
492
|
+
commercial product or service, such as a hosted LoopEngine.
|
|
493
|
+
- Each version becomes MIT-licensed two years after its release.
|
|
494
|
+
- Versions up to and including 0.3.12 were released under MIT and stay
|
|
495
|
+
under MIT.
|
|
496
|
+
|
|
497
|
+
The license text is what applies; this summary isn't legal advice.
|
|
498
|
+
Third-party software included in LoopEngine keeps its own license: see
|
|
499
|
+
[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
LoopEngine includes the following third-party software, under its own
|
|
4
|
+
license. This notice is kept with every copy of LoopEngine, as that
|
|
5
|
+
license requires.
|
|
6
|
+
|
|
7
|
+
## LobeHub Icons
|
|
8
|
+
|
|
9
|
+
The model provider logos in `web/provider-icons.ts` are from LobeHub's
|
|
10
|
+
icon set, `@lobehub/icons-static-svg` 1.101.0
|
|
11
|
+
(https://github.com/lobehub/lobe-icons), under the MIT License:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
MIT License
|
|
15
|
+
|
|
16
|
+
Copyright (c) 2023 LobeHub
|
|
17
|
+
|
|
18
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
19
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
20
|
+
in the Software without restriction, including without limitation the rights
|
|
21
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
22
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
23
|
+
furnished to do so, subject to the following conditions:
|
|
24
|
+
|
|
25
|
+
The above copyright notice and this permission notice shall be included in all
|
|
26
|
+
copies or substantial portions of the Software.
|
|
27
|
+
|
|
28
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
29
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
33
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
34
|
+
SOFTWARE.
|
|
35
|
+
```
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface WorkflowExampleMessage {
|
|
2
|
+
/** Stable id: lowercase-hyphenated. */
|
|
3
|
+
id: string;
|
|
4
|
+
category: string;
|
|
5
|
+
title: string;
|
|
6
|
+
/** What a person would type to describe it. */
|
|
7
|
+
message: string;
|
|
8
|
+
/** The apps it would work with, by name. */
|
|
9
|
+
apps: string[];
|
|
10
|
+
/** What it would ask before doing, in plain words. */
|
|
11
|
+
asksFirst: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare const WORKFLOW_EXAMPLE_MESSAGES: readonly WorkflowExampleMessage[];
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// Workflow examples as messages: jobs across many areas, each as what a
|
|
2
|
+
// person would type on the New workflow page. Using one fills in the
|
|
3
|
+
// description, for the planner to plan; unlike the ready-made examples
|
|
4
|
+
// (./workflow-examples.ts) nothing is planned in advance. The same list
|
|
5
|
+
// is on loopengine.co/workflows.
|
|
6
|
+
export const WORKFLOW_EXAMPLE_MESSAGES = [
|
|
7
|
+
{ id: "weekly-blog-post", category: "Marketing", title: "Weekly blog post", message: "Every Monday, find timely blog ideas about our product area, ask me which one to write, draft it, and let me review it before it's published.", apps: ["Firecrawl"], asksFirst: ["picking the idea", "publishing the post"] },
|
|
8
|
+
{ id: "competitor-pricing-watch", category: "Marketing", title: "Competitor pricing watch", message: "Every weekday, read our three competitors' pricing pages and tell me what changed since yesterday.", apps: ["Firecrawl"], asksFirst: [] },
|
|
9
|
+
{ id: "release-posts", category: "Marketing", title: "Posts about each release", message: "When we publish a release on GitHub, write a short post for X about what's new, and let me approve it before it goes out.", apps: ["GitHub", "X"], asksFirst: ["posting on X"] },
|
|
10
|
+
{ id: "sales-call-brief", category: "Sales", title: "Sales call brief", message: "When I send you a company's name before a sales call, research the company and its recent news, check what we know about them in HubSpot, and send me a one-page brief.", apps: ["Firecrawl", "HubSpot"], asksFirst: [] },
|
|
11
|
+
{ id: "renewal-check-ins", category: "Customer success", title: "Renewal check-ins", message: "Every Monday, find customers whose subscription renews in the next 30 days in Stripe, and draft a check-in email for each. Ask me before any email is sent.", apps: ["Stripe", "Gmail"], asksFirst: ["sending an email"] },
|
|
12
|
+
{ id: "monday-metrics", category: "Finance", title: "Monday metrics", message: "Every Monday at 9, pull last week's revenue, new customers and cancellations from Stripe, compare them with the week before, and send me a short summary.", apps: ["Stripe"], asksFirst: [] },
|
|
13
|
+
{ id: "overdue-invoices", category: "Finance", title: "Overdue invoice reminders", message: "Every morning, find invoices in Stripe that are more than 7 days overdue, and write a polite reminder to each customer. Ask me before any reminder is sent.", apps: ["Stripe", "Gmail"], asksFirst: ["sending a reminder"] },
|
|
14
|
+
{ id: "release-notes", category: "Engineering", title: "Release notes", message: "When a release is tagged on GitHub, read the pull requests merged since the last one and draft release notes for me to edit.", apps: ["GitHub"], asksFirst: [] },
|
|
15
|
+
{ id: "stale-pull-requests", category: "Engineering", title: "Stale pull requests", message: "Every weekday at 10, find pull requests that have waited more than two days for a review, and remind me which ones and who they're waiting on.", apps: ["GitHub"], asksFirst: [] },
|
|
16
|
+
{ id: "new-hire-onboarding", category: "People", title: "New hire onboarding", message: "When I tell you someone new is joining, write their onboarding page in Notion and create their first-week tasks in Linear. Show me the plan before anything is created.", apps: ["Notion", "Linear"], asksFirst: ["creating the pages and tasks"] },
|
|
17
|
+
{ id: "candidate-summaries", category: "People", title: "Candidate summaries", message: "Every morning, read new applications in our hiring inbox, compare each CV with the job description, and send me a short summary of each with its strengths and gaps. Never reply to a candidate yourself.", apps: ["Gmail"], asksFirst: [] },
|
|
18
|
+
{ id: "low-stock-reorders", category: "E-commerce", title: "Low stock reorders", message: "Every morning, check Shopify for products running low, estimate when each will sell out at the current pace, and send me a reorder list.", apps: ["Shopify"], asksFirst: [] },
|
|
19
|
+
{ id: "software-renewals", category: "Operations", title: "Software renewals", message: "On the first of each month, list the software subscriptions in our Airtable that renew in the next 30 days, with their cost and who uses them, and send it to me.", apps: ["Airtable"], asksFirst: [] },
|
|
20
|
+
{ id: "help-center-gaps", category: "Customer service", title: "Help center gaps", message: "Every Friday, read this week's support emails, find the questions we answer most that aren't in our help center in Notion, and draft an article for each. Let me review them first.", apps: ["Gmail", "Notion"], asksFirst: ["publishing an article"] },
|
|
21
|
+
{ id: "escalate-upset-customers", category: "Customer service", title: "Escalate upset customers", message: "Every 15 minutes, check the support inbox for angry customers or anyone mentioning a chargeback or a lawyer, and message me in Slack with a summary and a suggested reply.", apps: ["Gmail", "Slack"], asksFirst: [] },
|
|
22
|
+
{ id: "where-is-my-order", category: "Customer service", title: "Where is my order?", message: "When a customer asks where their order is, look it up in Shopify, find the tracking link, and reply with the latest status. Let me approve replies for the first week.", apps: ["Gmail", "Shopify"], asksFirst: ["sending a reply"] },
|
|
23
|
+
{ id: "weekly-support-report", category: "Customer service", title: "Weekly support report", message: "Every Monday, count last week's support emails by topic, note which took longest to answer, and send me a short report.", apps: ["Gmail"], asksFirst: [] },
|
|
24
|
+
{ id: "bug-reports-to-linear", category: "Customer service", title: "Bug reports to Linear", message: "When a customer reports a bug by email, check Linear for an existing issue, add their report to it or open a new one, and tell them we're on it. Ask me before replying.", apps: ["Gmail", "Linear"], asksFirst: ["replying to the customer"] },
|
|
25
|
+
{ id: "new-customer-check-in", category: "Customer success", title: "New customer check-in", message: "Three days after a customer signs up in Stripe, send them a short check-in email with the two help articles new customers need most. Let me approve the first ten.", apps: ["Stripe", "Gmail"], asksFirst: ["sending an email"] },
|
|
26
|
+
{ id: "usage-drop-alerts", category: "Customer success", title: "Usage drop alerts", message: "Every Monday, find accounts in PostHog whose usage dropped by half over the last two weeks, and send me a list with what changed for each.", apps: ["PostHog"], asksFirst: [] },
|
|
27
|
+
{ id: "low-score-follow-up", category: "Customer success", title: "Low survey score follow-up", message: "When a Typeform survey response comes in with a score of 6 or less, read it, find the customer in HubSpot, and draft a personal reply for me to send.", apps: ["Typeform", "HubSpot"], asksFirst: [] },
|
|
28
|
+
{ id: "quarterly-review-prep", category: "Customer success", title: "Quarterly review prep", message: "Before each quarterly review, pull the customer's usage from PostHog, their invoices from Stripe and their open issues from Linear, and write me a one-page summary.", apps: ["PostHog", "Stripe", "Linear"], asksFirst: [] },
|
|
29
|
+
{ id: "demo-request-routing", category: "Sales", title: "Demo request routing", message: "When a demo request comes in through our Typeform, research the company, score it against our ideal customer, add the contact to HubSpot, and tell me in Slack who it is.", apps: ["Typeform", "HubSpot", "Firecrawl", "Slack"], asksFirst: [] },
|
|
30
|
+
{ id: "stalled-deal-follow-ups", category: "Sales", title: "Stalled deal follow-ups", message: "Every morning, find HubSpot deals with no activity for 10 days, and draft a follow-up email for each. Ask me before any is sent.", apps: ["HubSpot", "Gmail"], asksFirst: ["sending a follow-up"] },
|
|
31
|
+
{ id: "call-notes-to-crm", category: "Sales", title: "Call notes to the CRM", message: "When I paste my notes after a call, summarise them, log them on the contact in HubSpot, and list the next steps with dates.", apps: ["HubSpot"], asksFirst: [] },
|
|
32
|
+
{ id: "proposal-drafts", category: "Sales", title: "Proposal drafts", message: "When a deal reaches the proposal stage in HubSpot, draft the proposal in Notion from our template with the client's details and pricing, for me to edit.", apps: ["HubSpot", "Notion"], asksFirst: [] },
|
|
33
|
+
{ id: "win-loss-reasons", category: "Sales", title: "Win and loss reasons", message: "Every month, read the notes on the deals we won and lost in HubSpot, and tell me the three most common reasons for each.", apps: ["HubSpot"], asksFirst: [] },
|
|
34
|
+
{ id: "prospect-list", category: "Sales", title: "Weekly prospect list", message: "Every Monday, find 20 companies that match our ideal customer and are hiring for roles we help with, and add them to an Airtable list with a line on why each fits.", apps: ["Firecrawl", "Airtable"], asksFirst: [] },
|
|
35
|
+
{ id: "newsletter-draft", category: "Marketing", title: "Newsletter draft", message: "Every other Thursday, collect what we shipped and wrote in the last two weeks, and draft our newsletter for me to edit.", apps: ["Linear", "Notion"], asksFirst: [] },
|
|
36
|
+
{ id: "blog-content-audit", category: "Marketing", title: "Blog content audit", message: "Every month, read our blog posts on Webflow, find the ones with outdated facts or broken links, and send me a list of what to fix.", apps: ["Webflow"], asksFirst: [] },
|
|
37
|
+
{ id: "campaign-results", category: "Marketing", title: "Campaign results", message: "Every Monday, pull last week's signups by source from PostHog, compare them with the week before, and tell me which campaigns are working.", apps: ["PostHog"], asksFirst: [] },
|
|
38
|
+
{ id: "landing-page-copy", category: "Marketing", title: "Landing page copy", message: "When I describe a new feature, write three versions of the landing page headline and copy, in our voice, for me to choose from.", apps: [], asksFirst: [] },
|
|
39
|
+
{ id: "webinar-follow-up", category: "Marketing", title: "Webinar follow-up", message: "After each webinar, email everyone who registered: the recording for those who came, and a short summary for those who didn't. Let me approve the emails first.", apps: ["Gmail"], asksFirst: ["sending the emails"] },
|
|
40
|
+
{ id: "case-study-drafts", category: "Marketing", title: "Case study drafts", message: "When I paste a customer interview, turn it into a case study draft in Notion: the problem, what they did, and the results with numbers.", apps: ["Notion"], asksFirst: [] },
|
|
41
|
+
{ id: "daily-x-post", category: "Social media", title: "Daily post on X", message: "Every weekday at 10, write one post for X from our latest blog post or release, and let me approve it before it's posted.", apps: ["X"], asksFirst: ["posting on X"] },
|
|
42
|
+
{ id: "mention-replies", category: "Social media", title: "Replies to mentions", message: "Every hour, search X for mentions of our product, like the positive ones, and draft replies to questions for me to approve.", apps: ["X"], asksFirst: ["replying on X"] },
|
|
43
|
+
{ id: "social-calendar", category: "Social media", title: "Social calendar", message: "Every Friday, plan next week's posts for X from our blog and releases, and put them in a Notion calendar for me to review.", apps: ["X", "Notion"], asksFirst: [] },
|
|
44
|
+
{ id: "conversations-to-join", category: "Social media", title: "Conversations to join", message: "Every morning, find what people on X are saying about our industry, and send me the five posts most worth replying to.", apps: ["X"], asksFirst: [] },
|
|
45
|
+
{ id: "customer-shout-outs", category: "Social media", title: "Customer shout-outs", message: "When a customer posts something good about us on X, repost it and thank them. Ask me before each repost.", apps: ["X"], asksFirst: ["reposting"] },
|
|
46
|
+
{ id: "competitor-social-recap", category: "Social media", title: "Competitor social recap", message: "Every Monday, summarise what our three competitors posted on X last week and which posts got the most attention.", apps: ["X"], asksFirst: [] },
|
|
47
|
+
{ id: "refund-risk-check", category: "E-commerce", title: "Refund risk check", message: "Before any refund over $200, check the customer's order history in Shopify for earlier refunds, and tell me if anything looks off.", apps: ["Shopify"], asksFirst: [] },
|
|
48
|
+
{ id: "daily-sales-summary", category: "E-commerce", title: "Daily sales summary", message: "Every evening, total today's Shopify orders, revenue and refunds, compare them with the same day last week, and send me the numbers.", apps: ["Shopify"], asksFirst: [] },
|
|
49
|
+
{ id: "best-customer-thank-yous", category: "E-commerce", title: "Best customer thank-yous", message: "Every month, find our 20 best customers by spend in Shopify and draft a personal thank-you email for each. Ask me before sending.", apps: ["Shopify", "Gmail"], asksFirst: ["sending an email"] },
|
|
50
|
+
{ id: "delivery-delay-updates", category: "E-commerce", title: "Delivery delay updates", message: "Every morning, find Shopify orders shipped more than 7 days ago that still haven't arrived, and email each customer an update. Ask me before sending.", apps: ["Shopify", "Gmail"], asksFirst: ["sending an update"] },
|
|
51
|
+
{ id: "returns-report", category: "E-commerce", title: "Returns report", message: "Every month, list the products returned most often in Shopify, with the reasons customers gave, and send me the list.", apps: ["Shopify"], asksFirst: [] },
|
|
52
|
+
{ id: "wholesale-orders", category: "E-commerce", title: "Wholesale orders", message: "When a wholesale order arrives by email, check the items and quantities, and draft the order summary and the Stripe invoice for me to approve.", apps: ["Gmail", "Stripe"], asksFirst: ["sending the invoice"] },
|
|
53
|
+
{ id: "month-end-checklist", category: "Finance", title: "Month-end checklist", message: "On the last working day of the month, check Stripe for unpaid invoices, failed payments and refunds, and send me a month-end checklist.", apps: ["Stripe"], asksFirst: [] },
|
|
54
|
+
{ id: "failed-payment-notes", category: "Finance", title: "Failed payment notes", message: "When a payment fails in Stripe, email the customer a friendly note with a link to update their card. Ask me before sending the first few.", apps: ["Stripe", "Gmail"], asksFirst: ["sending a note"] },
|
|
55
|
+
{ id: "monthly-spend-summary", category: "Finance", title: "Monthly spend summary", message: "Every month, total our spending by category from the receipts in our finance inbox, and send me a summary with anything unusual.", apps: ["Gmail"], asksFirst: [] },
|
|
56
|
+
{ id: "supplier-invoice-check", category: "Finance", title: "Supplier invoice check", message: "When a supplier invoice arrives by email, check it against our Airtable list of approved suppliers and amounts, and flag anything that doesn't match.", apps: ["Gmail", "Airtable"], asksFirst: [] },
|
|
57
|
+
{ id: "cash-forecast", category: "Finance", title: "Cash forecast", message: "Every Monday, list the invoices due to us in the next 30 days from Stripe and the bills we owe from our Airtable, and tell me the week-by-week cash position.", apps: ["Stripe", "Airtable"], asksFirst: [] },
|
|
58
|
+
{ id: "paypal-disputes", category: "Finance", title: "PayPal disputes", message: "When a PayPal dispute opens, find the order and its delivery, and draft our response with the evidence. Ask me before it's submitted.", apps: ["PayPal"], asksFirst: ["submitting the response"] },
|
|
59
|
+
{ id: "weekly-company-update", category: "Operations", title: "Weekly company update", message: "Every Friday, read what each team closed in Linear this week, and write a short company update for me to post.", apps: ["Linear"], asksFirst: [] },
|
|
60
|
+
{ id: "meeting-action-items", category: "Operations", title: "Meeting action items", message: "When I paste a meeting transcript, list the decisions and action items, and create a Todoist task for each with its owner and due date.", apps: ["Todoist"], asksFirst: [] },
|
|
61
|
+
{ id: "standup-digest", category: "Operations", title: "Standup digest", message: "Every morning, collect yesterday's closed and in-progress Jira issues, and send the team a short standup summary in Slack.", apps: ["Jira", "Slack"], asksFirst: [] },
|
|
62
|
+
{ id: "process-docs", category: "Operations", title: "Process docs", message: "When I describe how we do something, turn it into a step-by-step process page in Notion, and point out anything that seems unclear.", apps: ["Notion"], asksFirst: [] },
|
|
63
|
+
{ id: "stale-board-items", category: "Operations", title: "Stale board items", message: "Every Friday, find items on our monday.com boards with no update for two weeks, and ask their owners whether they're still active.", apps: ["monday.com"], asksFirst: [] },
|
|
64
|
+
{ id: "new-error-triage", category: "Engineering", title: "New error triage", message: "When a new error shows up in Sentry, find the commit that likely caused it on GitHub, and open an issue with the details. Ask me before opening it.", apps: ["Sentry", "GitHub"], asksFirst: ["opening an issue"] },
|
|
65
|
+
{ id: "failed-build-summary", category: "Engineering", title: "Failed build summary", message: "When a CircleCI build fails on main, read the logs, find the failing test and the change that broke it, and send me a summary.", apps: ["CircleCI", "GitHub"], asksFirst: [] },
|
|
66
|
+
{ id: "dependency-updates", category: "Engineering", title: "Dependency updates", message: "Every Monday, list the open dependency update pull requests on GitHub, and say which look safe to merge and which need a closer look.", apps: ["GitHub"], asksFirst: [] },
|
|
67
|
+
{ id: "pull-request-summaries", category: "Engineering", title: "Pull request summaries", message: "When a pull request is opened, summarise what it changes and what to check carefully, as a comment for the reviewer. Ask me before posting.", apps: ["GitHub"], asksFirst: ["posting a comment"] },
|
|
68
|
+
{ id: "deploy-notes", category: "Engineering", title: "Deploy notes", message: "After each Vercel production deploy, list the changes it shipped and send them to the team.", apps: ["Vercel"], asksFirst: [] },
|
|
69
|
+
{ id: "issue-labeling", category: "Engineering", title: "Issue labeling", message: "Every hour, label new GitHub issues by area and priority, and ask the reporter for steps to reproduce when they're missing. Ask me before commenting.", apps: ["GitHub"], asksFirst: ["commenting on an issue"] },
|
|
70
|
+
{ id: "feature-request-log", category: "Product", title: "Feature request log", message: "When a customer asks for a feature by email, add it to our Airtable of requests, or add their name to an existing one, so we can see what's asked for most.", apps: ["Gmail", "Airtable"], asksFirst: [] },
|
|
71
|
+
{ id: "weekly-product-metrics", category: "Product", title: "Weekly product metrics", message: "Every Monday, pull last week's active users, signups and the three most used features from PostHog, and send me a short summary.", apps: ["PostHog"], asksFirst: [] },
|
|
72
|
+
{ id: "funnel-drop-off", category: "Product", title: "Funnel drop-off", message: "Every week, check our signup funnel in Amplitude, and tell me where the biggest drop-off was and how it compares with last week.", apps: ["Amplitude"], asksFirst: [] },
|
|
73
|
+
{ id: "interview-themes", category: "Product", title: "User interview themes", message: "When I paste notes from user interviews, group what people said into themes, with quotes, on a Notion page.", apps: ["Notion"], asksFirst: [] },
|
|
74
|
+
{ id: "one-page-spec", category: "Product", title: "One-page spec", message: "When I describe a feature in a few sentences, write a one-page spec in Notion: the problem, who it's for, what's in and out, and open questions.", apps: ["Notion"], asksFirst: [] },
|
|
75
|
+
{ id: "customer-changelog", category: "Product", title: "Customer changelog", message: "Every two weeks, write our public changelog from the Linear issues we completed, in plain words for customers, for me to edit.", apps: ["Linear"], asksFirst: [] },
|
|
76
|
+
{ id: "figma-comment-roundup", category: "Design", title: "Figma comment roundup", message: "Every Friday, collect the open comments on our Figma files, and send each designer a list of what's waiting on them.", apps: ["Figma"], asksFirst: [] },
|
|
77
|
+
{ id: "design-guideline-check", category: "Design", title: "Design guideline check", message: "When I share a Figma link, review the screens against our design guidelines in Notion, and list what doesn't match.", apps: ["Figma", "Notion"], asksFirst: [] },
|
|
78
|
+
{ id: "blog-social-images", category: "Design", title: "Social images for posts", message: "When a blog post is published, make three social images for it in Canva from our template, for me to choose from.", apps: ["Canva"], asksFirst: [] },
|
|
79
|
+
{ id: "time-off-requests", category: "People", title: "Time-off requests", message: "When someone emails a time-off request, check it against our team calendar and leave policy in Notion, and draft my reply.", apps: ["Gmail", "Notion"], asksFirst: [] },
|
|
80
|
+
{ id: "handbook-answers", category: "People", title: "Handbook answers", message: "When someone asks an HR question in Slack, answer from our handbook in Notion, and tell them who to ask when the handbook doesn't say.", apps: ["Slack", "Notion"], asksFirst: [] },
|
|
81
|
+
{ id: "offboarding-checklist", category: "People", title: "Offboarding checklist", message: "When I tell you someone is leaving, make their offboarding checklist in Notion: accounts to close, equipment to return, and handover tasks.", apps: ["Notion"], asksFirst: [] },
|
|
82
|
+
{ id: "work-anniversaries", category: "People", title: "Work anniversaries", message: "On each work anniversary in our Airtable, draft a short note from me thanking them for something specific they did this year.", apps: ["Airtable"], asksFirst: [] },
|
|
83
|
+
{ id: "job-descriptions", category: "People", title: "Job descriptions", message: "When I describe a role in a few lines, write the job description in our usual format, with the responsibilities and what we're looking for.", apps: [], asksFirst: [] },
|
|
84
|
+
{ id: "interview-feedback", category: "People", title: "Interview feedback summary", message: "After each candidate's interviews, collect the interviewers' notes from Notion, and summarise the strengths, concerns and recommendation.", apps: ["Notion"], asksFirst: [] },
|
|
85
|
+
{ id: "contract-review", category: "Legal", title: "Contract review", message: "When I send you a contract, list the clauses that differ from our standard terms in Notion, and flag anything unusual about liability, payment or termination.", apps: ["Notion"], asksFirst: [] },
|
|
86
|
+
{ id: "nda-check", category: "Legal", title: "NDA check", message: "When an NDA arrives by email, compare it with our standard NDA, list the differences, and draft a reply asking for our usual changes. Ask me before sending.", apps: ["Gmail", "Notion"], asksFirst: ["sending a reply"] },
|
|
87
|
+
{ id: "contract-renewal-dates", category: "Legal", title: "Contract renewal dates", message: "Every month, list the contracts in our Airtable that renew or can be cancelled in the next 60 days, with the notice period for each.", apps: ["Airtable"], asksFirst: [] },
|
|
88
|
+
{ id: "data-deletion-requests", category: "Legal", title: "Data deletion requests", message: "When someone emails asking for their data to be deleted, find them in HubSpot and Stripe, list what we hold, and draft the reply. Ask me before anything is deleted or sent.", apps: ["Gmail", "HubSpot", "Stripe"], asksFirst: ["deleting anything", "sending a reply"] },
|
|
89
|
+
{ id: "access-review", category: "IT", title: "Access review", message: "Every quarter, list who has admin access to GitHub and our other tools from our Airtable, and ask each team lead to confirm it's still needed.", apps: ["Airtable", "GitHub"], asksFirst: [] },
|
|
90
|
+
{ id: "equipment-requests", category: "IT", title: "Equipment requests", message: "When someone asks for equipment in Slack, log it in our Airtable with the date and owner, and tell them when to expect it.", apps: ["Slack", "Airtable"], asksFirst: [] },
|
|
91
|
+
{ id: "outage-alerts", category: "IT", title: "Outage alerts", message: "Every 15 minutes, check the status pages of the tools we rely on, and message me in Slack as soon as something goes down.", apps: ["Firecrawl", "Slack"], asksFirst: [] },
|
|
92
|
+
{ id: "software-request-check", category: "IT", title: "Software request check", message: "When someone asks for a new tool, check whether we already pay for something similar in our Airtable, and send me the request with that answer.", apps: ["Airtable"], asksFirst: [] },
|
|
93
|
+
{ id: "morning-market-news", category: "Research", title: "Morning market news", message: "Every morning, search for news about our market and our top five customers, and send me anything worth knowing in five bullet points.", apps: ["Firecrawl"], asksFirst: [] },
|
|
94
|
+
{ id: "regulation-watch", category: "Research", title: "Regulation watch", message: "Every Monday, check for new regulations or guidance that affect our industry in the countries we sell to, and summarise what changed.", apps: ["Firecrawl"], asksFirst: [] },
|
|
95
|
+
{ id: "new-papers", category: "Research", title: "New papers", message: "Every Friday, find new papers about AI agents, and summarise the three most relevant, with links.", apps: ["Firecrawl"], asksFirst: [] },
|
|
96
|
+
{ id: "grant-finder", category: "Research", title: "Grant finder", message: "Every month, find grants and funding programmes we could apply for, with their deadlines and requirements, in an Airtable list.", apps: ["Firecrawl", "Airtable"], asksFirst: [] },
|
|
97
|
+
{ id: "company-brief", category: "Research", title: "Company brief", message: "When I send you a company name, research what it does, its size, funding, leadership and recent news, and write a one-page brief.", apps: ["Firecrawl"], asksFirst: [] },
|
|
98
|
+
{ id: "weekly-kpis", category: "Data & reporting", title: "Weekly KPIs", message: "Every Monday at 8, pull revenue from Stripe, signups from PostHog and the number of open support emails, and send the leadership team one KPI summary.", apps: ["Stripe", "PostHog", "Gmail"], asksFirst: [] },
|
|
99
|
+
{ id: "anomaly-alerts", category: "Data & reporting", title: "Anomaly alerts", message: "Every hour, check signups and payments, and message me in Slack if either is far below the usual for that time of day.", apps: ["Stripe", "PostHog", "Slack"], asksFirst: [] },
|
|
100
|
+
{ id: "morning-database-report", category: "Data & reporting", title: "Morning database report", message: "Every morning, run our usual report queries on Supabase, and send me the results with anything that changed a lot.", apps: ["Supabase"], asksFirst: [] },
|
|
101
|
+
{ id: "survey-analysis", category: "Data & reporting", title: "Survey analysis", message: "When a Typeform survey closes, read every response, group the answers into themes with counts and quotes, and write up the findings.", apps: ["Typeform"], asksFirst: [] },
|
|
102
|
+
{ id: "evening-inbox-sort", category: "Personal", title: "Evening inbox sort", message: "Every evening, sort my inbox: label newsletters, archive receipts, and list the emails that need my reply tomorrow.", apps: ["Gmail"], asksFirst: [] },
|
|
103
|
+
{ id: "morning-brief", category: "Personal", title: "Morning brief", message: "Every morning at 7, send me a short brief: today's top tasks from Todoist, the emails waiting on me, and the news I follow.", apps: ["Todoist", "Gmail", "Firecrawl"], asksFirst: [] },
|
|
104
|
+
{ id: "reading-list", category: "Personal", title: "Reading list", message: "When I send you a link, read the article, save a three-line summary to my Notion reading list, and tag it by topic.", apps: ["Firecrawl", "Notion"], asksFirst: [] },
|
|
105
|
+
{ id: "trip-planning", category: "Personal", title: "Trip planning", message: "When I tell you where I'm travelling and when, find flights and hotels near my meetings, and send me three options for each.", apps: ["Firecrawl"], asksFirst: [] },
|
|
106
|
+
{ id: "weekly-review", category: "Personal", title: "Weekly review", message: "Every Friday afternoon, list what I finished in Todoist this week and what slipped, and suggest my three priorities for next week.", apps: ["Todoist"], asksFirst: [] },
|
|
107
|
+
{ id: "unanswered-emails", category: "Personal", title: "Unanswered emails", message: "Every morning, find emails I sent over three days ago that never got a reply, and draft a polite follow-up for each. Ask me before sending.", apps: ["Gmail"], asksFirst: ["sending a follow-up"] },
|
|
108
|
+
{ id: "listing-descriptions", category: "Real estate", title: "Listing descriptions", message: "When I send you a property's details, write the listing description and a short post for X, in our style.", apps: [], asksFirst: [] },
|
|
109
|
+
{ id: "listing-enquiries", category: "Real estate", title: "Listing enquiries", message: "When someone asks about a listing by email, reply with its details and the next viewing times, and ask what they're looking for. Let me approve the first few replies.", apps: ["Gmail"], asksFirst: ["sending a reply"] },
|
|
110
|
+
{ id: "student-questions", category: "Education", title: "Student questions", message: "When a student emails a question about the course, answer from our course handbook in Notion, and pass anything about grades or refunds to me.", apps: ["Gmail", "Notion"], asksFirst: [] },
|
|
111
|
+
{ id: "lesson-plans", category: "Education", title: "Lesson plans", message: "When I give you a topic and a level, write a lesson plan with goals, activities and a short quiz.", apps: [], asksFirst: [] },
|
|
112
|
+
{ id: "donor-thank-yous", category: "Nonprofits", title: "Donor thank-yous", message: "When a donation comes in through Stripe, draft a personal thank-you to the donor that mentions what their gift pays for. Ask me before sending.", apps: ["Stripe", "Gmail"], asksFirst: ["sending a thank-you"] },
|
|
113
|
+
{ id: "grant-reports", category: "Nonprofits", title: "Grant reports", message: "Every quarter, collect our programme numbers from Airtable, and draft the report for each funder in their format.", apps: ["Airtable"], asksFirst: [] },
|
|
114
|
+
{ id: "client-weekly-update", category: "Agencies", title: "Client weekly update", message: "Every Friday, write each client a short update on what we did this week from our Linear projects, for me to review before it's sent.", apps: ["Linear", "Gmail"], asksFirst: ["sending an update"] },
|
|
115
|
+
{ id: "monthly-client-invoices", category: "Agencies", title: "Monthly client invoices", message: "At the end of each month, total each client's hours from our Airtable time log and draft their invoice in Stripe. Ask me before any invoice is sent.", apps: ["Airtable", "Stripe"], asksFirst: ["sending an invoice"] },
|
|
116
|
+
];
|
|
@@ -4,6 +4,12 @@ export interface WorkflowExample {
|
|
|
4
4
|
id: string;
|
|
5
5
|
/** Groups examples on the page: "Customer service", "Operations"… */
|
|
6
6
|
category: string;
|
|
7
|
+
/** Its card: what a person would type to describe it, the apps it
|
|
8
|
+
* works with, and what it asks before doing (the same as
|
|
9
|
+
* ./workflow-example-messages.ts and loopengine.co/workflows). */
|
|
10
|
+
message: string;
|
|
11
|
+
apps: string[];
|
|
12
|
+
asksFirst: string[];
|
|
7
13
|
plan: WorkflowPlan;
|
|
8
14
|
}
|
|
9
15
|
export declare const WORKFLOW_EXAMPLES: readonly WorkflowExample[];
|
|
@@ -69,10 +69,11 @@ Edit this: replace each [bracket] with your own policy. The agent checks claims
|
|
|
69
69
|
`,
|
|
70
70
|
};
|
|
71
71
|
/** The parts every example's plan shares. */
|
|
72
|
-
function example(id, category, plan) {
|
|
72
|
+
function example(id, category, card, plan) {
|
|
73
73
|
return {
|
|
74
74
|
id,
|
|
75
75
|
category,
|
|
76
|
+
...card,
|
|
76
77
|
plan: {
|
|
77
78
|
parameters: {},
|
|
78
79
|
notify: { on: false, reason: '' },
|
|
@@ -83,7 +84,7 @@ function example(id, category, plan) {
|
|
|
83
84
|
};
|
|
84
85
|
}
|
|
85
86
|
export const WORKFLOW_EXAMPLES = [
|
|
86
|
-
example('customer-email-replies', 'Customer service', {
|
|
87
|
+
example('customer-email-replies', 'Customer service', { message: "Every 10 minutes, check our support inbox. For each customer email, look up their order in Shopify and our policies, and reply in the thread. Let me approve every reply, and leave refund requests for the team.", apps: ["Gmail", "Shopify"], asksFirst: ["sending a reply"] }, {
|
|
87
88
|
name: 'Answer customer emails',
|
|
88
89
|
id: 'answer-customer-emails',
|
|
89
90
|
summary: 'Checks the support inbox every 10 minutes, looks up each customer\'s order and your policies, and replies. Every reply waits for your approval until you trust it; refund requests are left for a person.',
|
|
@@ -134,7 +135,7 @@ Rules:
|
|
|
134
135
|
skills: [SUPPORT_POLICIES],
|
|
135
136
|
notes: [policiesNote('your policies', 'support-policies')],
|
|
136
137
|
}),
|
|
137
|
-
example('refund-approvals', 'Customer service', {
|
|
138
|
+
example('refund-approvals', 'Customer service', { message: "Every 10 minutes, find refund and return requests in our inbox. Check each against the Shopify order and our return policy, work out the amount, and refund it. Ask me before any refund or reply goes out.", apps: ["Gmail", "Shopify"], asksFirst: ["issuing a refund", "sending the reply"] }, {
|
|
138
139
|
name: 'Refund approvals',
|
|
139
140
|
id: 'refund-approvals',
|
|
140
141
|
summary: 'Checks the inbox for refund and return requests, checks each against the order and your return policy, and proposes the refund and the reply. Both wait for your approval.',
|
|
@@ -186,7 +187,7 @@ Rules:
|
|
|
186
187
|
skills: [RETURN_POLICY],
|
|
187
188
|
notes: [policiesNote('your return policy', 'return-policy')],
|
|
188
189
|
}),
|
|
189
|
-
example('incident-triage', 'Operations', {
|
|
190
|
+
example('incident-triage', 'Operations', { message: "When our alerting tool sends an alert, rate how bad it is, check GitHub for the deploys and merged changes that likely caused it, and send me a short summary in Slack. Ask me before opening or commenting on an issue.", apps: ["GitHub", "Slack"], asksFirst: ["opening or commenting on issues"] }, {
|
|
190
191
|
name: 'Triage an incident',
|
|
191
192
|
id: 'triage-incident',
|
|
192
193
|
summary: 'Takes an alert or a bug report, works out what broke and how bad it is, finds the deploys and changes that likely caused it, and sends you a summary. Opening or commenting on issues waits for approval.',
|
|
@@ -232,7 +233,7 @@ Rules:
|
|
|
232
233
|
},
|
|
233
234
|
notes: ['Send it alerts: add a webhook to this workflow (Workflows tab) with "Message me the result when it finishes" on, and paste its URL into your alerting tool; or mention it in Slack, or paste an alert into Chat'],
|
|
234
235
|
}),
|
|
235
|
-
example('expense-approvals', 'Finance', {
|
|
236
|
+
example('expense-approvals', 'Finance', { message: "Every 15 minutes, read new claims in the claims inbox with their receipts, check each against our expense policy, and email the approver a one-line verdict. Never approve or pay anything yourself.", apps: ["Gmail"], asksFirst: ["replying to the claimant"] }, {
|
|
236
237
|
name: 'Check expense claims',
|
|
237
238
|
id: 'check-expense-claims',
|
|
238
239
|
summary: 'Checks the claims inbox, reads each claim and its receipt, checks it against your expense policy, and sends the approver a one-line verdict. Replies to claimants wait for your approval.',
|
|
@@ -277,7 +278,7 @@ Rules:
|
|
|
277
278
|
skills: [EXPENSE_POLICY],
|
|
278
279
|
notes: [policiesNote('your expense policy', 'expense-policy'), 'Set EXPENSE_APPROVER_EMAIL (Environment tab) to the approver\'s address'],
|
|
279
280
|
}),
|
|
280
|
-
example('lead-follow-up', 'Sales', {
|
|
281
|
+
example('lead-follow-up', 'Sales', { message: "Every hour, pick up new leads in HubSpot, research each company on the web, and send a short, personal first email. Ask me before sending, and before updating the lead.", apps: ["HubSpot", "Firecrawl", "Gmail"], asksFirst: ["sending the first email", "updating the lead"] }, {
|
|
281
282
|
name: 'Lead follow-up',
|
|
282
283
|
id: 'lead-follow-up',
|
|
283
284
|
summary: 'Every hour, picks up new leads in HubSpot, researches each on the web, and sends a short, personal first email. Sending and updating the CRM wait for your approval.',
|
|
@@ -329,7 +330,7 @@ Rules:
|
|
|
329
330
|
},
|
|
330
331
|
notes: ['Edit "What we sell" at the top of its instructions (Overview tab)'],
|
|
331
332
|
}),
|
|
332
|
-
example('inbox-to-tasks', 'Operations', {
|
|
333
|
+
example('inbox-to-tasks', 'Operations', { message: "Every 10 minutes, turn each request in our shared inbox into a GitHub issue with an owner and a due date, then tell the sender who is handling it. Let me approve the reply.", apps: ["Gmail", "GitHub"], asksFirst: ["replying to the sender"] }, {
|
|
333
334
|
name: 'Inbox to tasks',
|
|
334
335
|
id: 'inbox-to-tasks',
|
|
335
336
|
summary: 'Checks a shared inbox every 10 minutes and turns each request into a task (a GitHub issue) with an owner and a due date, then tells the sender who is handling it.',
|
|
@@ -382,7 +383,7 @@ Rules:
|
|
|
382
383
|
},
|
|
383
384
|
notes: ['Edit "Who handles what" and the repository at the top of its instructions (Overview tab)'],
|
|
384
385
|
}),
|
|
385
|
-
example('weekly-research-digest', 'Research', {
|
|
386
|
+
example('weekly-research-digest', 'Research', { message: "Every Monday at 9, search the web for last week's news on our topics and competitors, and send me a short digest with links.", apps: ["Firecrawl", "Slack"], asksFirst: [] }, {
|
|
386
387
|
name: 'Weekly research digest',
|
|
387
388
|
id: 'weekly-research-digest',
|
|
388
389
|
summary: 'Every Monday morning, searches the web for news on the topics you choose and sends you a short digest with links.',
|
package/dist/index.d.ts
CHANGED
|
@@ -88,6 +88,7 @@ export { workflowPageHtml } from './web/workflow-page.js';
|
|
|
88
88
|
export { WorkflowError, checkWorkflowForAgent, createWorkflow, forgetWorkflow, migrateAgentWorkflows, getPlanJob, listAgentWorkflows, planWorkflow, resolveConnections, prepareWorkflowRun, updateWorkflow, startPlanJob, type AgentWorkflowView, type CreatedWorkflow, type PlanJob, type PlannedWorkflow, type WorkflowAgentInfo, type WorkflowCheck, type WorkflowConflict, type WorkflowCreateDeps, type WorkflowMcpDeps, type WorkflowMcpCreateDeps, resolveMcpServers, prepareMcpAbilities, } from './web/workflow-admin.js';
|
|
89
89
|
export { WORKFLOW_PLANNER_PROMPT, planParameters, planWorkflowWithModel, validateWorkflowPlan, workflowTemplate, type WorkflowPlan, type WorkflowPlannerTools, type WorkflowStep } from './core/system-agents/workflow-planner.js';
|
|
90
90
|
export { WORKFLOW_EXAMPLES, workflowExample, type WorkflowExample } from './core/system-agents/workflow-examples.js';
|
|
91
|
+
export { WORKFLOW_EXAMPLE_MESSAGES, type WorkflowExampleMessage } from './core/system-agents/workflow-example-messages.js';
|
|
91
92
|
export { createTrigger, updateTrigger, rotateTrigger, listTriggers, findTriggerByToken, publicTrigger, checkTriggerSettings, verifySenderSignature, parsePayload, valueAt, deliveryId, passesFilter, paramsFromPayload, recordDelivery as recordTriggerDelivery, FileTriggerStore, getTriggerStore, setTriggerStore, TriggerError, type WorkflowTrigger, type PublicTrigger, type TriggerSettings, type TriggerVerify, type TriggerFilter, type TriggerSender, type SignatureAlgorithm, type TriggerDelivery, type TriggerOutcome, type TriggerStore } from './core/workflow-triggers.js';
|
|
92
93
|
export { checkCallbackUrl, callbackSecret, signCallback, deliverCallback, runCallbackPayload, isPrivateAddress, CallbackUrlError, type CallbackDelivery, type RunCallbackEvent } from './core/run-callbacks.js';
|
|
93
94
|
export { WorkflowRunner, FileRunStore, getRunStore, setRunStore, listRuns, getRun, isUnderWay, RunError, type WorkflowRun, type WorkflowRunStatus, type RunStore, type ExecuteRun, type RunOutcome, type StartRunInput } from './core/workflow-runs.js';
|
package/dist/index.js
CHANGED
|
@@ -81,6 +81,7 @@ export { workflowPageHtml } from './web/workflow-page.js';
|
|
|
81
81
|
export { WorkflowError, checkWorkflowForAgent, createWorkflow, forgetWorkflow, migrateAgentWorkflows, getPlanJob, listAgentWorkflows, planWorkflow, resolveConnections, prepareWorkflowRun, updateWorkflow, startPlanJob, resolveMcpServers, prepareMcpAbilities, } from './web/workflow-admin.js';
|
|
82
82
|
export { WORKFLOW_PLANNER_PROMPT, planParameters, planWorkflowWithModel, validateWorkflowPlan, workflowTemplate } from './core/system-agents/workflow-planner.js';
|
|
83
83
|
export { WORKFLOW_EXAMPLES, workflowExample } from './core/system-agents/workflow-examples.js';
|
|
84
|
+
export { WORKFLOW_EXAMPLE_MESSAGES } from './core/system-agents/workflow-example-messages.js';
|
|
84
85
|
export { createTrigger, updateTrigger, rotateTrigger, listTriggers, findTriggerByToken, publicTrigger, checkTriggerSettings, verifySenderSignature, parsePayload, valueAt, deliveryId, passesFilter, paramsFromPayload, recordDelivery as recordTriggerDelivery, FileTriggerStore, getTriggerStore, setTriggerStore, TriggerError } from './core/workflow-triggers.js';
|
|
85
86
|
export { checkCallbackUrl, callbackSecret, signCallback, deliverCallback, runCallbackPayload, isPrivateAddress, CallbackUrlError } from './core/run-callbacks.js';
|
|
86
87
|
export { WorkflowRunner, FileRunStore, getRunStore, setRunStore, listRuns, getRun, isUnderWay, RunError } from './core/workflow-runs.js';
|
|
@@ -18,13 +18,26 @@ export const workflowPageHtml = `<!doctype html>
|
|
|
18
18
|
<style>${devUiCss}
|
|
19
19
|
.wf { max-width: 860px; display: grid; gap: 18px; }
|
|
20
20
|
.wf textarea { width: 100%; min-height: 130px; box-sizing: border-box; font: inherit; padding: 10px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); color: var(--ink); }
|
|
21
|
-
.wf-ex-
|
|
22
|
-
.wf-ex-
|
|
23
|
-
.wf-ex-
|
|
24
|
-
.wf-ex {
|
|
21
|
+
.wf-ex-search { width: 100%; box-sizing: border-box; font: inherit; padding: 8px 12px; border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); color: var(--ink); }
|
|
22
|
+
.wf-ex-cats { display: flex; flex-wrap: wrap; gap: 6px; }
|
|
23
|
+
.wf-ex-cats button { font: inherit; font-size: 12.5px; font-weight: 600; padding: 5px 12px; border-radius: 999px; border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer; }
|
|
24
|
+
.wf-ex-cats button[aria-pressed="true"] { background: var(--ink); border-color: var(--ink); color: var(--bg); }
|
|
25
|
+
.wf-ex-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 10px; }
|
|
26
|
+
.wf-ex { border: 1px solid var(--line); border-radius: var(--radius); padding: 12px 14px; display: flex; flex-direction: column; gap: 6px; background: var(--bg); }
|
|
27
|
+
.wf-ex:hover { border-color: var(--accent); }
|
|
28
|
+
.wf-ex-head { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
|
29
|
+
.wf-ex-cat { font-size: 10.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; padding: 1px 8px; border-radius: 999px; background: var(--accent-soft); color: var(--accent-ink); }
|
|
25
30
|
.wf-ex strong { font-size: 14px; }
|
|
26
31
|
.wf-ex p { margin: 0; font-size: 13px; color: var(--ink-muted); }
|
|
27
|
-
.wf-ex
|
|
32
|
+
.wf-ex-apps { display: flex; flex-wrap: wrap; gap: 4px; margin-top: auto; padding-top: 4px; }
|
|
33
|
+
.wf-ex-apps span { font-size: 11.5px; padding: 1px 6px; border: 1px solid var(--line); border-radius: 5px; background: var(--surface); color: var(--ink); }
|
|
34
|
+
.wf-ex-asks strong { font-size: 12px; color: var(--warn); }
|
|
35
|
+
.wf-ex .wf-btn { align-self: flex-start; margin-top: 4px; }
|
|
36
|
+
.wf-ex-pager { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; align-items: center; }
|
|
37
|
+
.wf-ex-pager button { font: inherit; font-size: 13px; font-weight: 600; min-width: 34px; padding: 5px 10px; border-radius: 6px; border: 1px solid var(--line); background: var(--bg); color: var(--ink); cursor: pointer; }
|
|
38
|
+
.wf-ex-pager button:not(:disabled):not([aria-current]):hover { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-ink); }
|
|
39
|
+
.wf-ex-pager button[aria-current] { background: var(--ink); border-color: var(--ink); color: var(--bg); }
|
|
40
|
+
.wf-ex-pager button:disabled { opacity: 0.4; cursor: default; }
|
|
28
41
|
.wf-instructions textarea { min-height: 180px; font-family: var(--font-mono); font-size: 12px; }
|
|
29
42
|
.wf-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; display: grid; gap: 10px; background: var(--bg); }
|
|
30
43
|
.wf-card h2 { margin: 0; font-size: 18px; }
|
|
@@ -83,7 +96,8 @@ ${consoleShellScript}
|
|
|
83
96
|
var DASH = String.fromCharCode(8212);
|
|
84
97
|
var LABELS = { allow: 'Allow', ask: 'Ask me', deny: 'Never' };
|
|
85
98
|
var SENDERS = { stripe: 'Stripe', github: 'GitHub', shopify: 'Shopify', standard: 'Standard Webhooks', custom: 'your app' };
|
|
86
|
-
var state = { description: '', planned: null, agents: [], examples: [], ready: false, canInstall: false };
|
|
99
|
+
var state = { description: '', planned: null, agents: [], examples: [], messages: [], ready: false, canInstall: false, exQuery: '', exCat: 'All', exPage: 1 };
|
|
100
|
+
var EX_PAGE = 6;
|
|
87
101
|
|
|
88
102
|
function esc(s) {
|
|
89
103
|
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
|
@@ -111,10 +125,7 @@ ${consoleShellScript}
|
|
|
111
125
|
'<p class="hint">Rather start from something ready-made? <a href="#examples">Choose a workflow example</a>, or <a href="/interview">find your first agent</a>.</p>' +
|
|
112
126
|
examplesHtml();
|
|
113
127
|
var text = document.getElementById('wfText');
|
|
114
|
-
|
|
115
|
-
for (var i = 0; i < examples.length; i++) {
|
|
116
|
-
examples[i].addEventListener('click', function (ev) { openExample(ev.currentTarget.dataset.example); });
|
|
117
|
-
}
|
|
128
|
+
wireExamples();
|
|
118
129
|
if (location.hash === '#examples') {
|
|
119
130
|
var box = document.getElementById('examples');
|
|
120
131
|
if (box) box.scrollIntoView();
|
|
@@ -132,28 +143,113 @@ ${consoleShellScript}
|
|
|
132
143
|
});
|
|
133
144
|
}
|
|
134
145
|
|
|
135
|
-
//
|
|
136
|
-
//
|
|
146
|
+
// Examples: the ready-made ones (a plan, no model call) and the rest as
|
|
147
|
+
// messages (they fill in the description, for the planner), searched,
|
|
148
|
+
// filtered by area and paged.
|
|
149
|
+
function allExamples() {
|
|
150
|
+
return state.examples.map(function (e) {
|
|
151
|
+
return { id: e.id, category: e.category, title: e.plan.name, message: e.message, apps: e.apps || [], asksFirst: e.asksFirst || [], ready: true };
|
|
152
|
+
}).concat(state.messages.map(function (m) {
|
|
153
|
+
return { id: m.id, category: m.category, title: m.title, message: m.message, apps: m.apps || [], asksFirst: m.asksFirst || [], ready: false };
|
|
154
|
+
}));
|
|
155
|
+
}
|
|
156
|
+
|
|
137
157
|
function examplesHtml() {
|
|
138
|
-
if (!state.examples.length) return '';
|
|
139
|
-
var groups = [];
|
|
140
|
-
state.examples.forEach(function (e) {
|
|
141
|
-
var g = groups.filter(function (x) { return x.name === e.category; })[0];
|
|
142
|
-
if (!g) { g = { name: e.category, items: [] }; groups.push(g); }
|
|
143
|
-
g.items.push(e);
|
|
144
|
-
});
|
|
158
|
+
if (!state.examples.length && !state.messages.length) return '';
|
|
145
159
|
return '<div class="wf-card" id="examples"><h2>Start from an example</h2>' +
|
|
146
|
-
'<p class="hint" style="margin:0">Ready-made
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
'<button type="button" class="wf-btn plain" data-example="' + esc(e.id) + '">Use this</button></div>';
|
|
152
|
-
}).join('') + '</div></div>';
|
|
153
|
-
}).join('') +
|
|
160
|
+
'<p class="hint" style="margin:0">Ready-made ones open the plan straight away. The rest fill in the description above, for you to change and plan.</p>' +
|
|
161
|
+
'<input type="search" class="wf-ex-search" id="wfExSearch" placeholder="Search examples, e.g. invoices, GitHub, Shopify" aria-label="Search examples" value="' + esc(state.exQuery) + '">' +
|
|
162
|
+
'<div class="wf-ex-cats" id="wfExCats" role="group" aria-label="Filter examples"></div>' +
|
|
163
|
+
'<div class="wf-ex-grid" id="wfExList"></div>' +
|
|
164
|
+
'<nav class="wf-ex-pager" id="wfExPager" aria-label="Example pages"></nav>' +
|
|
154
165
|
'</div>';
|
|
155
166
|
}
|
|
156
167
|
|
|
168
|
+
function exampleMatches(e) {
|
|
169
|
+
if (state.exCat !== 'All' && e.category !== state.exCat) return false;
|
|
170
|
+
var q = state.exQuery.trim().toLowerCase();
|
|
171
|
+
if (!q) return true;
|
|
172
|
+
var text = (e.title + ' ' + e.message + ' ' + e.category + ' ' + e.apps.join(' ')).toLowerCase();
|
|
173
|
+
return q.split(' ').every(function (word) { return !word || text.indexOf(word) !== -1; });
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// The page numbers to show: first, last and around the current one.
|
|
177
|
+
function pageNumbers(current, count) {
|
|
178
|
+
var out = [];
|
|
179
|
+
for (var n = 1; n <= count; n++) {
|
|
180
|
+
if (n === 1 || n === count || Math.abs(n - current) <= 1) out.push(n);
|
|
181
|
+
else if (out[out.length - 1] !== null) out.push(null);
|
|
182
|
+
}
|
|
183
|
+
return out;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function renderExampleList() {
|
|
187
|
+
var list = document.getElementById('wfExList');
|
|
188
|
+
if (!list) return;
|
|
189
|
+
var all = allExamples();
|
|
190
|
+
var cats = ['All'];
|
|
191
|
+
all.forEach(function (e) { if (cats.indexOf(e.category) === -1) cats.push(e.category); });
|
|
192
|
+
document.getElementById('wfExCats').innerHTML = cats.map(function (c) {
|
|
193
|
+
return '<button type="button" data-ex-cat="' + esc(c) + '" aria-pressed="' + (c === state.exCat) + '">' + esc(c) + '</button>';
|
|
194
|
+
}).join('');
|
|
195
|
+
var matching = all.filter(exampleMatches);
|
|
196
|
+
var pages = Math.max(1, Math.ceil(matching.length / EX_PAGE));
|
|
197
|
+
if (state.exPage > pages) state.exPage = pages;
|
|
198
|
+
var shown = matching.slice((state.exPage - 1) * EX_PAGE, state.exPage * EX_PAGE);
|
|
199
|
+
list.innerHTML = shown.length ? shown.map(function (e) {
|
|
200
|
+
return '<div class="wf-ex">' +
|
|
201
|
+
'<div class="wf-ex-head"><span class="wf-ex-cat">' + esc(e.category) + '</span>' + (e.ready ? '<span class="le-pill good">Ready-made</span>' : '') + '</div>' +
|
|
202
|
+
'<strong>' + esc(e.title) + '</strong>' +
|
|
203
|
+
'<p>' + String.fromCharCode(8220) + esc(e.message) + String.fromCharCode(8221) + '</p>' +
|
|
204
|
+
(e.apps.length ? '<div class="wf-ex-apps">' + e.apps.map(function (a) { return '<span>' + esc(a) + '</span>'; }).join('') + '</div>' : '') +
|
|
205
|
+
(e.asksFirst.length ? '<p class="wf-ex-asks"><strong>Asks you before:</strong> ' + esc(e.asksFirst.join('; ')) + '</p>' : '') +
|
|
206
|
+
'<button type="button" class="wf-btn plain" data-example="' + esc(e.id) + '">' + (e.ready ? 'Use this plan' : 'Use this') + '</button>' +
|
|
207
|
+
'</div>';
|
|
208
|
+
}).join('') : '<p class="hint">No examples match ' + String.fromCharCode(8220) + esc(state.exQuery.trim()) + String.fromCharCode(8221) + (state.exCat === 'All' ? '' : ' in ' + esc(state.exCat)) + '. Describe it in your own words above instead.</p>';
|
|
209
|
+
var pager = document.getElementById('wfExPager');
|
|
210
|
+
pager.innerHTML = pages > 1
|
|
211
|
+
? '<button type="button" data-ex-page="' + (state.exPage - 1) + '"' + (state.exPage === 1 ? ' disabled' : '') + '>' + String.fromCharCode(8249) + ' Previous</button>' +
|
|
212
|
+
pageNumbers(state.exPage, pages).map(function (n) {
|
|
213
|
+
return n === null ? '<span class="hint" aria-hidden="true">' + ELLIPSIS + '</span>'
|
|
214
|
+
: '<button type="button" data-ex-page="' + n + '" aria-label="Page ' + n + '"' + (n === state.exPage ? ' aria-current="page"' : '') + '>' + n + '</button>';
|
|
215
|
+
}).join('') +
|
|
216
|
+
'<button type="button" data-ex-page="' + (state.exPage + 1) + '"' + (state.exPage === pages ? ' disabled' : '') + '>Next ' + String.fromCharCode(8250) + '</button>'
|
|
217
|
+
: '';
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function wireExamples() {
|
|
221
|
+
var box = document.getElementById('examples');
|
|
222
|
+
if (!box) return;
|
|
223
|
+
renderExampleList();
|
|
224
|
+
document.getElementById('wfExSearch').addEventListener('input', function (ev) {
|
|
225
|
+
state.exQuery = ev.target.value;
|
|
226
|
+
state.exPage = 1;
|
|
227
|
+
renderExampleList();
|
|
228
|
+
});
|
|
229
|
+
box.addEventListener('click', function (ev) {
|
|
230
|
+
var t = ev.target.closest('button');
|
|
231
|
+
if (!t) return;
|
|
232
|
+
if (t.dataset.exCat) { state.exCat = t.dataset.exCat; state.exPage = 1; renderExampleList(); return; }
|
|
233
|
+
if (t.dataset.exPage) { state.exPage = Number(t.dataset.exPage); renderExampleList(); box.scrollIntoView({ block: 'start' }); return; }
|
|
234
|
+
if (t.dataset.example) useExample(t.dataset.example);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// A ready-made example opens its plan; any other fills in the
|
|
239
|
+
// description, to change and plan.
|
|
240
|
+
function useExample(id) {
|
|
241
|
+
if (state.examples.some(function (e) { return e.id === id; })) { openExample(id); return; }
|
|
242
|
+
var m = state.messages.filter(function (x) { return x.id === id; })[0];
|
|
243
|
+
if (!m) return;
|
|
244
|
+
state.description = m.message;
|
|
245
|
+
renderDescribe();
|
|
246
|
+
window.scrollTo(0, 0);
|
|
247
|
+
var text = document.getElementById('wfText');
|
|
248
|
+
text.focus();
|
|
249
|
+
var status = document.getElementById('wfStatus');
|
|
250
|
+
if (status) status.textContent = 'Change it to fit what you need, then plan it.';
|
|
251
|
+
}
|
|
252
|
+
|
|
157
253
|
function openExample(id) {
|
|
158
254
|
var e = state.examples.filter(function (x) { return x.id === id; })[0];
|
|
159
255
|
if (!e) return;
|
|
@@ -466,6 +562,7 @@ ${consoleShellScript}
|
|
|
466
562
|
state.canInstall = Boolean(d.canInstallAbilities);
|
|
467
563
|
state.agents = d.agents || [];
|
|
468
564
|
state.examples = d.examples || [];
|
|
565
|
+
state.messages = d.messages || [];
|
|
469
566
|
renderDescribe();
|
|
470
567
|
})
|
|
471
568
|
.catch(function (err) { page.innerHTML = '<p class="error">Could not load: ' + esc(err.message) + '</p>'; });
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loopengine",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Runtime for tool-using AI agents with a transparent ReAct loop.",
|
|
6
|
-
"license": "MIT",
|
|
6
|
+
"license": "FSL-1.1-MIT",
|
|
7
7
|
"homepage": "https://loopengine.co",
|
|
8
8
|
"repository": {
|
|
9
9
|
"type": "git",
|
|
@@ -137,7 +137,8 @@
|
|
|
137
137
|
"dist/web",
|
|
138
138
|
"protocol/loop-event.schema.json",
|
|
139
139
|
"PROTOCOL.md",
|
|
140
|
-
"LICENSE"
|
|
140
|
+
"LICENSE",
|
|
141
|
+
"THIRD_PARTY_NOTICES.md"
|
|
141
142
|
],
|
|
142
143
|
"scripts": {
|
|
143
144
|
"build": "tsc; s=$?; chmod +x dist/bin/cli.js; rm -rf dist/core/system-skills; cp -r core/system-skills dist/core/system-skills; tsx scripts/write-dist-package-json.ts; exit $s",
|