loopengine 0.3.10 → 0.3.11

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.
@@ -71,6 +71,10 @@ export interface StarterTemplate {
71
71
  candidate: AgentCandidate;
72
72
  setup: TemplateSetup;
73
73
  }
74
+ /** @deprecated Templates are now workflow examples
75
+ * (./workflow-examples.ts). Kept for a project server from an older
76
+ * template, whose Templates page still lists these: each example as the
77
+ * template it creates as a new agent. */
74
78
  export declare const STARTER_TEMPLATES: readonly StarterTemplate[];
75
79
  export declare function starterTemplate(id: string): StarterTemplate | undefined;
76
80
  /** The first run of a template's schedule: its time of day (and weekday)
@@ -1,452 +1,24 @@
1
1
  import { nextTimeOfDay, parseTimeOfDay, workspaceTimeZone } from '../time-zone.js';
2
- const GMAIL_KEYS = ['GMAIL_CLIENT_ID', 'GMAIL_CLIENT_SECRET', 'GMAIL_REFRESH_TOKEN'];
3
- const SHOPIFY_KEYS = ['SHOPIFY_STORE_DOMAIN', 'SHOPIFY_ADMIN_TOKEN'];
4
- const GITHUB_APP = 'io.github.github/github-mcp-server';
5
- const FIRECRAWL_APP = 'io.github.firecrawl/firecrawl-mcp-server';
6
- const policiesNote = (what, skill) => `Write ${what} into its ${skill} skill (Advanced → Skills): the agent answers from it and nothing else`;
7
- export const STARTER_TEMPLATES = [
8
- {
9
- id: 'customer-email-replies',
10
- category: 'Customer service',
11
- candidate: {
12
- title: 'Answer customer emails',
13
- summary: 'Checks the support inbox every 10 minutes, looks up each customer\'s order, and replies. Every reply waits for your approval until you trust it; refund requests are left for a person.',
14
- trigger: { kind: 'schedule', description: 'every 10 minutes, for new emails', every: '10m' },
15
- hoursPerWeek: 5,
16
- fit: 7,
17
- risk: 'medium',
18
- asksFirst: ['sending a reply'],
19
- needs: [
20
- { kind: 'ability', name: '@loopengine-co/gmail', description: 'your support inbox' },
21
- { kind: 'ability', name: '@loopengine-co/shopify', description: 'your store\'s orders' },
22
- { kind: 'other', name: 'Your policies', description: 'shipping, returns and the rest, in its support-policies skill' },
23
- ],
24
- agent: {
25
- name: 'customer-email-replies',
26
- systemPrompt: `You answer customer emails for our business, from our support inbox.
27
-
28
- When asked to handle new emails:
29
- 1. Find them with gmail_search, query "is:unread in:inbox". Skip newsletters, receipts, automatic notifications and "no-reply" senders: mark those read and move on.
30
- 2. For each customer email, read the whole conversation with gmail_get_thread first.
31
- 3. Work out what they want: order status, a product question, a return or refund, a complaint, or something else.
32
- 4. Look up what you need before answering: the order with shopify_find_orders (by the order number they give, or their email) and shopify_get_order, and our policies in the support-policies skill (open it with Skill). Never guess an order detail, a date or a policy.
33
- 5. Reply with gmail_send, passing reply_to_message_id so it stays in the same thread: short, friendly, in the customer's language, using their name, answering the question first and saying what happens next.
34
- 6. Mark the email read with gmail_modify_labels (remove "UNREAD").
35
-
36
- Rules:
37
- - Refunds, returns and replacements are handled by a person: reply that their request has been passed to the team, then star the email (add "STARRED") instead of acting on it.
38
- - If the customer is angry, mentions a lawyer, a chargeback or a safety problem, don't promise anything; save a calm reply with gmail_create_draft and star the email for a person.
39
- - If you can't find the order or the answer, say so and ask for the order number; don't invent one.
40
- - Never share one customer's information with anyone else.`,
41
- scheduleMessage: 'Handle new customer emails in the support inbox.',
42
- exampleMessage: 'Handle new customer emails in the support inbox.',
43
- },
44
- },
45
- setup: {
46
- abilities: ['@loopengine-co/gmail', '@loopengine-co/shopify'],
47
- keys: [...GMAIL_KEYS, ...SHOPIFY_KEYS],
48
- skills: [{
49
- id: 'support-policies',
50
- description: 'Our shipping, returns, refunds and other customer policies. Read it before telling a customer anything about a policy.',
51
- body: `# Our customer policies
52
-
53
- Edit this: replace each [bracket] with your own policy. The agent answers customers from this and nothing else.
54
-
55
- ## Shipping
56
- - Where we ship: [countries]
57
- - How long it takes: [e.g. 3–5 business days]
58
- - Tracking: [when and how customers get it]
59
-
60
- ## Returns and refunds
61
- - Return window: [e.g. 30 days from delivery]
62
- - What can't be returned: [e.g. sale items, opened cosmetics]
63
- - How refunds are paid: [e.g. to the original payment method, in 5–10 business days]
64
-
65
- ## Other questions
66
- - [Order changes and cancellations]
67
- - [Warranty]
68
- - [Anything customers often ask]
69
- `,
70
- }],
71
- rules: [
72
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
73
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
74
- { name: 'template-drafts-allowed', tool: 'gmail_create_draft', decision: 'allow' },
75
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
76
- { name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
77
- { name: 'template-no-refunds', tool: 'shopify_create_refund', decision: 'deny' },
78
- ],
79
- schedule: { every: '10m', name: 'Check the support inbox', message: 'Handle new customer emails in the support inbox.' },
80
- needsApprovals: true,
81
- notes: [policiesNote('your policies', 'support-policies')],
82
- },
83
- },
84
- {
85
- id: 'refund-approvals',
86
- category: 'Customer service',
87
- candidate: {
88
- title: 'Refund approvals',
89
- 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.',
90
- trigger: { kind: 'schedule', description: 'every 10 minutes, for new refund requests', every: '10m' },
91
- hoursPerWeek: 3,
92
- fit: 7,
93
- risk: 'high',
94
- asksFirst: ['issuing a refund', 'sending the reply'],
95
- needs: [
96
- { kind: 'ability', name: '@loopengine-co/gmail', description: 'your support inbox' },
97
- { kind: 'ability', name: '@loopengine-co/shopify', description: 'your store\'s orders and refunds' },
98
- { kind: 'other', name: 'Your return policy', description: 'in its return-policy skill' },
99
- ],
100
- agent: {
101
- name: 'refund-approvals',
102
- systemPrompt: `You handle refund and return requests for our store, from our support inbox.
103
-
104
- When asked to handle new refund requests:
105
- 1. Find them with gmail_search, query "is:unread in:inbox (refund OR return OR damaged OR broken OR "money back")". Read each conversation with gmail_get_thread. If an email isn't really a refund or return request, leave it unread for the support team.
106
- 2. Find the order with shopify_find_orders (the order number they give, or their email) and read it with shopify_get_order: what was bought, when, how much, what's already been refunded, whether it was delivered.
107
- 3. Check the request against our return policy in the return-policy skill (open it with Skill): the return window, whether the item is eligible, and whether the reason is covered.
108
- 4. Decide what to propose: a full refund, a partial refund, refunding shipping, or no refund (and which part of the policy says so).
109
- 5. Work out the exact amount with shopify_preview_refund. Never state an amount you haven't previewed.
110
- 6. Issue it with shopify_create_refund, with a note giving the policy rule and the facts from the order. A person approves it first.
111
- 7. Once the refund is done, reply with gmail_send (reply_to_message_id set): what was refunded, to which payment method, and that it takes 5–10 business days to arrive. Then mark the email read with gmail_modify_labels.
112
-
113
- Rules:
114
- - Never refund more than was paid, and never refund an order twice: check refunds on the order first.
115
- - If the refund is denied, don't try again; save a reply with gmail_create_draft explaining what you'd suggest instead, and star the email for a person.
116
- - If the order can't be found, ask the customer for the order number; don't guess.
117
- - If the customer mentions a chargeback, a lawyer or a safety problem, don't act; star the email for a person.`,
118
- scheduleMessage: 'Handle new refund and return requests in the support inbox.',
119
- exampleMessage: 'Handle new refund and return requests in the support inbox.',
120
- },
121
- },
122
- setup: {
123
- abilities: ['@loopengine-co/gmail', '@loopengine-co/shopify'],
124
- keys: [...GMAIL_KEYS, ...SHOPIFY_KEYS],
125
- skills: [{
126
- id: 'return-policy',
127
- description: 'Our return and refund policy: the window, what qualifies, and how much is refunded. Read it before proposing any refund.',
128
- body: `# Our return policy
129
-
130
- Edit this: replace each [bracket] with your own policy. The agent proposes refunds from this and nothing else.
131
-
132
- - Return window: [e.g. 30 days from delivery]
133
- - Eligible items: [e.g. unused, in the original packaging]
134
- - Not eligible: [e.g. sale items, gift cards, personalised items]
135
- - Damaged or wrong items: [e.g. full refund including shipping, no return needed]
136
- - Change of mind: [e.g. refund of the item price, customer pays return shipping]
137
- - Shipping costs: [when they're refunded]
138
- - Partial refunds: [when you offer one, and how much]
139
- `,
140
- }],
141
- rules: [
142
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
143
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
144
- { name: 'template-drafts-allowed', tool: 'gmail_create_draft', decision: 'allow' },
145
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
146
- { name: 'template-preview-allowed', tool: 'shopify_preview_refund', decision: 'allow' },
147
- { name: 'template-refunds-need-approval', tool: 'shopify_create_refund', decision: 'ask' },
148
- { name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
149
- ],
150
- schedule: { every: '10m', name: 'Check for refund requests', message: 'Handle new refund and return requests in the support inbox.' },
151
- needsApprovals: true,
152
- notes: [policiesNote('your return policy', 'return-policy')],
153
- },
154
- },
155
- {
156
- id: 'incident-triage',
157
- category: 'Operations',
158
- candidate: {
159
- title: 'Triage incidents',
160
- 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 a summary to Slack. Opening or commenting on issues waits for approval.',
161
- trigger: { kind: 'event', description: 'when your alerting tool calls its webhook, or someone passes it an alert in Chat or Slack' },
162
- hoursPerWeek: 3,
163
- fit: 6,
164
- risk: 'medium',
165
- asksFirst: ['opening or commenting on issues'],
166
- needs: [
167
- { kind: 'app', name: 'GitHub', description: 'your repository\'s commits, deploys and issues' },
168
- { kind: 'messaging', name: 'Slack', description: 'where each summary is sent (its Messaging tab)' },
169
- ],
170
- agent: {
171
- name: 'incident-triage',
172
- systemPrompt: `You help the on-call engineer triage incidents.
173
-
174
- Our repository on GitHub: [owner/name] (edit this). Pass its owner and repo to every GitHub tool.
175
-
176
- When you get an alert, error or bug report, follow your triage-incident workflow.
177
-
178
- Rules:
179
- - Only read on your own; never roll anything back yourself.
180
- - Say how sure you are. If the evidence is thin, say what would confirm it instead of guessing.
181
- - Keep the summary short enough to read on a phone.`,
182
- exampleMessage: 'Alert: checkout-api error rate 18% (normally under 1%) since 14:05 UTC. Errors: "timeout connecting to payments".',
183
- },
184
- },
185
- setup: {
186
- abilities: [],
187
- apps: [{ id: GITHUB_APP, title: 'GitHub', tools: ['list_commits', 'list_pull_requests', 'actions_list', 'search_issues', 'issue_read', 'issue_write', 'add_issue_comment'] }],
188
- keys: [],
189
- skills: [{
190
- id: 'triage-incident',
191
- description: 'Triage an alert, error or bug report: what broke, how bad it is, and the change that likely caused it.',
192
- workflow: { title: 'Triage an incident', parameters: { alert: { description: 'The alert, error or bug report', required: true } }, webhook: true },
193
- body: `Triage the alert you were given.
194
-
195
- 1. Summarise it in two sentences: what is failing and who is affected.
196
- 2. Rate the severity: SEV1 (customers can't use the product), SEV2 (a major feature is broken), SEV3 (degraded or a workaround exists), SEV4 (minor).
197
- 3. Find what changed shortly before it started: github_actions_list (method list_workflow_runs) for recent deploys and failed builds, github_list_pull_requests (state closed) for recently merged changes, and github_list_commits (narrowed with since and path) for detail. Match times carefully.
198
- 4. Check whether it's already known with github_search_issues; read a match with github_issue_read (method get).
199
- 5. If an issue already tracks it, add your findings with github_add_issue_comment; otherwise, for SEV1 or SEV2, open one with github_issue_write (method create). Both ask a person first.
200
- 6. End with the summary, which is sent to Slack: what's broken, severity, the most likely cause with links, and one next step (who should look, what to check, or whether to roll back).`,
201
- }],
202
- rules: [
203
- { name: 'template-new-issues-need-approval', tool: 'github_issue_write', decision: 'ask' },
204
- { name: 'template-comments-need-approval', tool: 'github_add_issue_comment', decision: 'ask' },
205
- ],
206
- needsApprovals: true,
207
- notes: ['Send it alerts: paste one into Chat, mention it in Slack, or have your alerting tool call its webhook'],
208
- },
209
- },
210
- {
211
- id: 'expense-approvals',
212
- category: 'Finance',
213
- candidate: {
214
- title: 'Check expense claims',
215
- 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.',
216
- trigger: { kind: 'schedule', description: 'every 15 minutes, for new claims', every: '15m' },
217
- hoursPerWeek: 3,
218
- fit: 7,
219
- risk: 'high',
220
- asksFirst: ['replying to the claimant'],
221
- needs: [
222
- { kind: 'ability', name: '@loopengine-co/gmail', description: 'the inbox where claims arrive' },
223
- { kind: 'other', name: 'Your expense policy', description: 'in its expense-policy skill' },
224
- ],
225
- agent: {
226
- name: 'expense-approvals',
227
- systemPrompt: `You check expense claims against our expense policy.
228
-
229
- When asked to check new claims:
230
- 1. Find them with gmail_search, query "is:unread in:inbox". Read each with gmail_get_thread.
231
- 2. Read the amount, currency, date, merchant, category and who submitted it, from the email and its receipt (the attachment names show whether a receipt is there).
232
- 3. Check it against our expense policy in the expense-policy skill (open it with Skill): the category is allowed, it's under the limit, the receipt matches the amount and date, and it isn't a duplicate of an earlier claim (search the inbox for the same merchant and amount).
233
- 4. Write a one-line verdict: "Within policy" or "Outside policy: <reason>", then the details you checked.
234
- 5. Email the verdict to the approver at the address in the EXPENSE_APPROVER_EMAIL setting with gmail_send, then mark the claim read with gmail_modify_labels.
235
-
236
- Rules:
237
- - Never approve or pay a claim yourself. A person always decides.
238
- - If the receipt is missing or unreadable, draft a reply to the claimant asking for it with gmail_create_draft; don't fill in values.
239
- - If the policy doesn't cover the case, say which part is unclear rather than deciding.`,
240
- scheduleMessage: 'Check the new expense claims in the inbox.',
241
- exampleMessage: 'Check the new expense claims in the inbox.',
242
- },
243
- },
244
- setup: {
245
- abilities: ['@loopengine-co/gmail'],
246
- keys: [...GMAIL_KEYS],
247
- skills: [{
248
- id: 'expense-policy',
249
- description: 'Our expense policy: what can be claimed, the limits, and what a claim needs. Read it before giving any verdict.',
250
- body: `# Our expense policy
251
-
252
- Edit this: replace each [bracket] with your own policy. The agent checks claims against this and nothing else.
253
-
254
- ## What can be claimed, and the limits
255
- - Travel: [e.g. economy flights and trains; taxis when there's no public transport]
256
- - Hotels: [e.g. up to 150 per night]
257
- - Meals: [e.g. up to 50 per day when travelling]
258
- - Equipment and software: [e.g. up to 200 without pre-approval]
259
-
260
- ## What can't be claimed
261
- - [e.g. alcohol, fines, personal entertainment]
262
-
263
- ## What every claim needs
264
- - An itemised receipt showing the amount, date and merchant
265
- - [Pre-approval for anything over a limit]
266
- - Submitted within [e.g. 60 days]
267
- `,
268
- }],
269
- rules: [
270
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
271
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
272
- { name: 'template-drafts-allowed', tool: 'gmail_create_draft', decision: 'allow' },
273
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
274
- { name: 'template-emails-need-approval', tool: 'gmail_send', decision: 'ask' },
275
- ],
276
- schedule: { every: '15m', name: 'Check for new claims', message: 'Check the new expense claims in the inbox.' },
277
- needsApprovals: true,
278
- notes: [policiesNote('your expense policy', 'expense-policy'), 'Set EXPENSE_APPROVER_EMAIL (Environment tab) to the approver\'s address'],
279
- },
280
- },
281
- {
282
- id: 'lead-follow-up',
283
- category: 'Sales',
284
- candidate: {
285
- title: 'Lead follow-up',
286
- summary: 'Every hour, picks up new leads in HubSpot, researches each on the web, and drafts a short, personal first email. Sending and updating the CRM wait for your approval.',
287
- trigger: { kind: 'schedule', description: 'every hour, for new leads', every: '1h' },
288
- hoursPerWeek: 3,
289
- fit: 6,
290
- risk: 'medium',
291
- asksFirst: ['sending the first email', 'updating the lead'],
292
- needs: [
293
- { kind: 'ability', name: '@loopengine-co/hubspot', description: 'your leads in HubSpot' },
294
- { kind: 'app', name: 'Firecrawl', description: 'researching each lead on the web' },
295
- { kind: 'ability', name: '@loopengine-co/gmail', description: 'the inbox to send from' },
296
- ],
297
- agent: {
298
- name: 'lead-follow-up',
299
- systemPrompt: `You follow up with new leads for our business.
300
-
301
- What we sell (edit this):
302
- - [What your product does, in one sentence]
303
- - [Who it is for]
304
- - [The one result customers care about most]
305
-
306
- When asked to follow up with new leads:
307
- 1. Find them with hubspot_find_contacts: lead_status "NEW", created_in_last_days 2.
308
- 2. For each, read hubspot_get_contact. Skip anyone with an owner, recent notes, or a last-contacted date: someone is already on it.
309
- 3. Research them with firecrawl_search and firecrawl_scrape: what their company does, its size, and anything recent that matters (funding, hiring, a launch). Note your sources.
310
- 4. Work out what they most likely need from us, in one sentence.
311
- 5. Send a short first email (under 120 words) with gmail_send: a specific opening based on your research, one sentence on how we help with that need, and one easy question to reply to. A person approves it first.
312
- 6. Log what you sent and found with hubspot_add_note, and set the lead to "ATTEMPTED_TO_CONTACT" with hubspot_update_contact.
313
-
314
- Rules:
315
- - Never invent facts about a lead. If research finds nothing reliable, write a simple, honest email instead.
316
- - No pressure tactics, fake urgency or discounts you weren't told about.
317
- - If the lead looks like a competitor, a student or spam, add a note saying so and don't email them.`,
318
- scheduleMessage: 'Follow up with the new leads in HubSpot.',
319
- exampleMessage: 'Follow up with the new leads in HubSpot.',
320
- },
321
- },
322
- setup: {
323
- abilities: ['@loopengine-co/hubspot', '@loopengine-co/gmail'],
324
- apps: [{ id: FIRECRAWL_APP, title: 'Firecrawl', tools: ['firecrawl_search', 'firecrawl_scrape'] }],
325
- keys: ['HUBSPOT_ACCESS_TOKEN', ...GMAIL_KEYS],
326
- rules: [
327
- { name: 'template-search-allowed', tool: 'firecrawl_search', decision: 'allow' },
328
- { name: 'template-read-pages-allowed', tool: 'firecrawl_scrape', decision: 'allow' },
329
- { name: 'template-notes-allowed', tool: 'hubspot_add_note', decision: 'allow' },
330
- { name: 'template-lead-updates-need-approval', tool: 'hubspot_update_contact', decision: 'ask' },
331
- { name: 'template-no-new-contacts', tool: 'hubspot_create_contact', decision: 'deny' },
332
- { name: 'template-emails-need-approval', tool: 'gmail_send', decision: 'ask' },
333
- ],
334
- schedule: { every: '1h', name: 'Follow up with new leads', message: 'Follow up with the new leads in HubSpot.' },
335
- needsApprovals: true,
336
- notes: ['Edit "What we sell" at the top of its instructions (Overview tab)'],
337
- },
338
- },
339
- {
340
- id: 'inbox-to-tasks',
341
- category: 'Operations',
342
- candidate: {
343
- title: 'Inbox to tasks',
344
- 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.',
345
- trigger: { kind: 'schedule', description: 'every 10 minutes, for new emails', every: '10m' },
346
- hoursPerWeek: 3,
347
- fit: 7,
348
- risk: 'low',
349
- asksFirst: ['replying to the sender'],
350
- needs: [
351
- { kind: 'ability', name: '@loopengine-co/gmail', description: 'the shared inbox' },
352
- { kind: 'app', name: 'GitHub', description: 'tasks, as GitHub issues' },
353
- ],
354
- agent: {
355
- name: 'inbox-to-tasks',
356
- systemPrompt: `You turn requests in our shared inbox into tasks, kept as GitHub issues.
357
-
358
- Tasks live in this GitHub repository: [owner/name] (edit this). Pass its owner and repo to every GitHub tool.
359
-
360
- Who handles what (edit this; GitHub usernames):
361
- - Billing and invoices: [username]
362
- - Technical problems: [username]
363
- - Everything else: [username]
364
-
365
- When asked to handle new emails:
366
- 1. Find them with gmail_search, query "is:unread in:inbox". Read each with gmail_get_thread.
367
- 2. Decide whether it's a request someone needs to act on. Newsletters, receipts, out-of-office replies and spam are not: mark them read and move on.
368
- 3. Check whether a task already exists with github_search_issues (the sender and subject). If so, add the new details with github_add_issue_comment instead.
369
- 4. Otherwise open one with github_issue_write (method create): a short title starting with a verb, a two-line summary, the sender, the due date (the date they asked for, or 2 working days if they didn't say), and the owner from the list above as the assignee.
370
- 5. Reply to the sender with gmail_send (reply_to_message_id set): who is handling it and by when. A person approves it first.
371
- 6. Mark the email read with gmail_modify_labels.
372
-
373
- Rules:
374
- - If an email holds several requests, make one task for each.
375
- - Never promise a fix or a refund in the reply; only say who is handling it and by when.`,
376
- scheduleMessage: 'Turn the new emails in the shared inbox into tasks.',
377
- exampleMessage: 'Turn the new emails in the shared inbox into tasks.',
378
- },
379
- },
380
- setup: {
381
- abilities: ['@loopengine-co/gmail'],
382
- apps: [{ id: GITHUB_APP, title: 'GitHub', tools: ['search_issues', 'issue_write', 'add_issue_comment'] }],
383
- keys: [...GMAIL_KEYS],
384
- rules: [
385
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
386
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
387
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
388
- { name: 'template-tasks-allowed', tool: 'github_issue_write', decision: 'allow' },
389
- { name: 'template-task-updates-allowed', tool: 'github_add_issue_comment', decision: 'allow' },
390
- { name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
391
- ],
392
- schedule: { every: '10m', name: 'Check the shared inbox', message: 'Turn the new emails in the shared inbox into tasks.' },
393
- needsApprovals: true,
394
- notes: ['Edit "Who handles what" at the top of its instructions (Overview tab)', 'Point GITHUB_DEFAULT_REPO at the repository your team keeps tasks in'],
395
- },
396
- },
397
- {
398
- id: 'weekly-research-digest',
399
- category: 'Research',
400
- candidate: {
401
- title: 'Weekly research digest',
402
- summary: 'Every Monday morning, searches the web for news on the topics you choose and sends a short digest with links to Slack.',
403
- trigger: { kind: 'schedule', description: 'Mondays at 9:00', every: '7d' },
404
- hoursPerWeek: 2,
405
- fit: 6,
406
- risk: 'low',
407
- asksFirst: [],
408
- needs: [
409
- { kind: 'app', name: 'Firecrawl', description: 'searching the web and reading pages' },
410
- { kind: 'messaging', name: 'Slack', description: 'where each digest is sent (its Messaging tab)' },
411
- ],
412
- agent: {
413
- name: 'weekly-research-digest',
414
- systemPrompt: `You write a weekly research digest. Your answer is the digest: it's sent to Slack as it is.
415
-
416
- Topics to follow (edit this list):
417
- - [Your industry or market]
418
- - [Competitors, by name]
419
- - [Technologies or regulations you care about]
420
-
421
- Each week:
422
- 1. Search the web with firecrawl_search for what happened in the last 7 days on each topic.
423
- 2. Open the most relevant pages with firecrawl_scrape and read them; don't rely on search snippets.
424
- 3. Write the digest: for each topic, up to three items, each with a one-sentence summary, why it matters to us, and the link.
425
- 4. End with "Worth a closer look": the single most important item, and why.
426
- 5. Answer with the digest alone, nothing before or after it.
427
-
428
- Rules:
429
- - Only include things published in the last 7 days, and give each item's date.
430
- - Prefer original sources over aggregators. Never invent a link.
431
- - If a topic had no real news, say so in one line.`,
432
- scheduleMessage: 'Write this week\'s research digest.',
433
- exampleMessage: 'Write this week\'s research digest.',
434
- },
435
- },
2
+ import { WORKFLOW_EXAMPLES } from './workflow-examples.js';
3
+ import { workflowTemplate } from './workflow-planner.js';
4
+ /** @deprecated Templates are now workflow examples
5
+ * (./workflow-examples.ts). Kept for a project server from an older
6
+ * template, whose Templates page still lists these: each example as the
7
+ * template it creates as a new agent. */
8
+ export const STARTER_TEMPLATES = WORKFLOW_EXAMPLES.map(({ id, category, plan }) => {
9
+ const template = workflowTemplate(plan);
10
+ const [own, ...rest] = template.setup.skills ?? [];
11
+ return {
12
+ ...template,
13
+ id,
14
+ category,
436
15
  setup: {
437
- abilities: [],
438
- apps: [{ id: FIRECRAWL_APP, title: 'Firecrawl', tools: ['firecrawl_search', 'firecrawl_scrape'] }],
439
- keys: [],
440
- rules: [
441
- { name: 'template-search-allowed', tool: 'firecrawl_search', decision: 'allow' },
442
- { name: 'template-read-pages-allowed', tool: 'firecrawl_scrape', decision: 'allow' },
443
- ],
444
- schedule: { every: '7d', at: '09:00', weekday: 1, name: 'Weekly research digest', message: 'Write this week\'s research digest.', notify: true },
445
- needsApprovals: false,
446
- notes: ['Edit "Topics to follow" at the top of its instructions (Overview tab)'],
16
+ ...template.setup,
17
+ // Its own skill is the workflow its schedule runs.
18
+ skills: [{ ...own, workflow: { title: plan.name, ...(Object.keys(plan.parameters).length ? { parameters: plan.parameters } : {}) } }, ...rest],
447
19
  },
448
- },
449
- ];
20
+ };
21
+ });
450
22
  export function starterTemplate(id) {
451
23
  return STARTER_TEMPLATES.find((t) => t.id === id);
452
24
  }
@@ -0,0 +1,10 @@
1
+ import type { WorkflowPlan } from './workflow-planner.js';
2
+ export interface WorkflowExample {
3
+ /** Stable id, used in links: lowercase-hyphenated. */
4
+ id: string;
5
+ /** Groups examples on the page: "Customer service", "Operations"… */
6
+ category: string;
7
+ plan: WorkflowPlan;
8
+ }
9
+ export declare const WORKFLOW_EXAMPLES: readonly WorkflowExample[];
10
+ export declare function workflowExample(id: string): WorkflowExample | undefined;