loopengine 0.3.9 → 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.
@@ -1,386 +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 SLACK_KEYS = ['SLACK_BOT_TOKEN', 'SLACK_DEFAULT_CHANNEL'];
6
- const WEB_SEARCH_KEYS = [['TAVILY_API_KEY', 'BRAVE_API_KEY', 'FIRECRAWL_API_KEY']];
7
- const DOCS_NOTE = 'Put your policies and help articles in the project\'s docs folder, as Markdown or text files (or point DOCS_SEARCH_PATH at another folder)';
8
- export const STARTER_TEMPLATES = [
9
- {
10
- id: 'customer-email-replies',
11
- category: 'Customer service',
12
- candidate: {
13
- title: 'Answer customer emails',
14
- 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.',
15
- trigger: { kind: 'schedule', description: 'every 10 minutes, for new emails', every: '10m' },
16
- hoursPerWeek: 5,
17
- fit: 7,
18
- risk: 'medium',
19
- asksFirst: ['sending a reply'],
20
- needs: [
21
- { kind: 'ability', name: 'lp-gmail', description: 'your support inbox' },
22
- { kind: 'ability', name: 'lp-shopify', description: 'your store\'s orders' },
23
- { kind: 'ability', name: 'lp-docs-search', description: 'your help articles and policies' },
24
- ],
25
- agent: {
26
- name: 'customer-email-replies',
27
- systemPrompt: `You answer customer emails for our business, from our support inbox.
28
-
29
- When asked to handle new emails:
30
- 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.
31
- 2. For each customer email, read the whole conversation with gmail_get_thread first.
32
- 3. Work out what they want: order status, a product question, a return or refund, a complaint, or something else.
33
- 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 with search_docs. Never guess an order detail, a date or a policy.
34
- 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.
35
- 6. Mark the email read with gmail_modify_labels (remove "UNREAD").
36
-
37
- Rules:
38
- - 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.
39
- - 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.
40
- - If you can't find the order or the answer, say so and ask for the order number; don't invent one.
41
- - Never share one customer's information with anyone else.`,
42
- scheduleMessage: 'Handle new customer emails in the support inbox.',
43
- exampleMessage: 'Handle new customer emails in the support inbox.',
44
- },
45
- },
46
- setup: {
47
- abilities: ['lp-gmail', 'lp-shopify', 'lp-docs-search'],
48
- keys: [...GMAIL_KEYS, ...SHOPIFY_KEYS],
49
- env: { DOCS_SEARCH_PATH: 'docs' },
50
- rules: [
51
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
52
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
53
- { name: 'template-drafts-allowed', tool: 'gmail_create_draft', decision: 'allow' },
54
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
55
- { name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
56
- { name: 'template-no-refunds', tool: 'shopify_create_refund', decision: 'deny' },
57
- ],
58
- schedule: { every: '10m', name: 'Check the support inbox', message: 'Handle new customer emails in the support inbox.' },
59
- needsApprovals: true,
60
- notes: [DOCS_NOTE],
61
- },
62
- },
63
- {
64
- id: 'refund-approvals',
65
- category: 'Customer service',
66
- candidate: {
67
- title: 'Refund approvals',
68
- 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.',
69
- trigger: { kind: 'schedule', description: 'every 10 minutes, for new refund requests', every: '10m' },
70
- hoursPerWeek: 3,
71
- fit: 7,
72
- risk: 'high',
73
- asksFirst: ['issuing a refund', 'sending the reply'],
74
- needs: [
75
- { kind: 'ability', name: 'lp-gmail', description: 'your support inbox' },
76
- { kind: 'ability', name: 'lp-shopify', description: 'your store\'s orders and refunds' },
77
- { kind: 'ability', name: 'lp-docs-search', description: 'your return policy' },
78
- ],
79
- agent: {
80
- name: 'refund-approvals',
81
- systemPrompt: `You handle refund and return requests for our store, from our support inbox.
82
-
83
- When asked to handle new refund requests:
84
- 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.
85
- 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.
86
- 3. Check the request against our return policy with search_docs: the return window, whether the item is eligible, and whether the reason is covered.
87
- 4. Decide what to propose: a full refund, a partial refund, refunding shipping, or no refund (and which part of the policy says so).
88
- 5. Work out the exact amount with shopify_preview_refund. Never state an amount you haven't previewed.
89
- 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.
90
- 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.
91
-
92
- Rules:
93
- - Never refund more than was paid, and never refund an order twice: check refunds on the order first.
94
- - 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.
95
- - If the order can't be found, ask the customer for the order number; don't guess.
96
- - If the customer mentions a chargeback, a lawyer or a safety problem, don't act; star the email for a person.`,
97
- scheduleMessage: 'Handle new refund and return requests in the support inbox.',
98
- exampleMessage: 'Handle new refund and return requests in the support inbox.',
99
- },
100
- },
101
- setup: {
102
- abilities: ['lp-gmail', 'lp-shopify', 'lp-docs-search'],
103
- keys: [...GMAIL_KEYS, ...SHOPIFY_KEYS],
104
- env: { DOCS_SEARCH_PATH: 'docs' },
105
- rules: [
106
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
107
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
108
- { name: 'template-drafts-allowed', tool: 'gmail_create_draft', decision: 'allow' },
109
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
110
- { name: 'template-preview-allowed', tool: 'shopify_preview_refund', decision: 'allow' },
111
- { name: 'template-refunds-need-approval', tool: 'shopify_create_refund', decision: 'ask' },
112
- { name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
113
- ],
114
- schedule: { every: '10m', name: 'Check for refund requests', message: 'Handle new refund and return requests in the support inbox.' },
115
- needsApprovals: true,
116
- notes: [DOCS_NOTE + ' — especially your return policy'],
117
- },
118
- },
119
- {
120
- id: 'incident-triage',
121
- category: 'Operations',
122
- candidate: {
123
- title: 'Triage incidents',
124
- 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 posts a summary to Slack. Opening or commenting on issues waits for approval.',
125
- trigger: { kind: 'on_request', description: 'when an alert or report is passed to it in Chat, Slack or over HTTP' },
126
- hoursPerWeek: 3,
127
- fit: 6,
128
- risk: 'medium',
129
- asksFirst: ['opening or commenting on issues'],
130
- needs: [
131
- { kind: 'app', name: 'GitHub', description: 'your repository\'s commits, deploys and issues' },
132
- { kind: 'ability', name: 'lp-slack-notifier', description: 'posting the summary to Slack' },
133
- ],
134
- agent: {
135
- name: 'incident-triage',
136
- systemPrompt: `You help the on-call engineer triage incidents.
137
-
138
- Our repository on GitHub: [owner/name] (edit this). Pass its owner and repo to every GitHub tool.
139
-
140
- When you get an alert, error or bug report:
141
- 1. Summarise it in two sentences: what is failing and who is affected.
142
- 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).
143
- 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.
144
- 4. Check whether it's already known with github_search_issues; read a match with github_issue_read (method get).
145
- 5. Post a short summary to Slack with post_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).
146
- 6. 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.
147
-
148
- Rules:
149
- - Only read on your own; never roll anything back yourself.
150
- - Say how sure you are. If the evidence is thin, say what would confirm it instead of guessing.
151
- - Keep the Slack summary short enough to read on a phone.`,
152
- exampleMessage: 'Alert: checkout-api error rate 18% (normally under 1%) since 14:05 UTC. Errors: "timeout connecting to payments".',
153
- },
154
- },
155
- setup: {
156
- abilities: ['lp-slack-notifier'],
157
- apps: [{ id: GITHUB_APP, title: 'GitHub', tools: ['list_commits', 'list_pull_requests', 'actions_list', 'search_issues', 'issue_read', 'issue_write', 'add_issue_comment'] }],
158
- keys: [...SLACK_KEYS],
159
- rules: [
160
- { name: 'template-slack-summary-allowed', tool: 'post_to_slack', decision: 'allow' },
161
- { name: 'template-new-issues-need-approval', tool: 'github_issue_write', decision: 'ask' },
162
- { name: 'template-comments-need-approval', tool: 'github_add_issue_comment', decision: 'ask' },
163
- ],
164
- needsApprovals: true,
165
- notes: ['Send it alerts: paste one into Chat, have your alerting tool call it over HTTP, or reply to it in Slack'],
166
- },
167
- },
168
- {
169
- id: 'expense-approvals',
170
- category: 'Finance',
171
- candidate: {
172
- title: 'Check expense claims',
173
- 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.',
174
- trigger: { kind: 'schedule', description: 'every 15 minutes, for new claims', every: '15m' },
175
- hoursPerWeek: 3,
176
- fit: 7,
177
- risk: 'high',
178
- asksFirst: ['replying to the claimant'],
179
- needs: [
180
- { kind: 'ability', name: 'lp-gmail', description: 'the inbox where claims arrive' },
181
- { kind: 'ability', name: 'lp-docs-search', description: 'your expense policy' },
182
- ],
183
- agent: {
184
- name: 'expense-approvals',
185
- systemPrompt: `You check expense claims against our expense policy.
186
-
187
- When asked to check new claims:
188
- 1. Find them with gmail_search, query "is:unread in:inbox". Read each with gmail_get_thread.
189
- 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).
190
- 3. Check it against the expense policy with search_docs: 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).
191
- 4. Write a one-line verdict: "Within policy" or "Outside policy: <reason>", then the details you checked.
192
- 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.
193
-
194
- Rules:
195
- - Never approve or pay a claim yourself. A person always decides.
196
- - 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.
197
- - If the policy doesn't cover the case, say which part is unclear rather than deciding.`,
198
- scheduleMessage: 'Check the new expense claims in the inbox.',
199
- exampleMessage: 'Check the new expense claims in the inbox.',
200
- },
201
- },
202
- setup: {
203
- abilities: ['lp-gmail', 'lp-docs-search'],
204
- keys: [...GMAIL_KEYS],
205
- env: { DOCS_SEARCH_PATH: 'docs' },
206
- rules: [
207
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
208
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
209
- { name: 'template-drafts-allowed', tool: 'gmail_create_draft', decision: 'allow' },
210
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
211
- { name: 'template-emails-need-approval', tool: 'gmail_send', decision: 'ask' },
212
- ],
213
- schedule: { every: '15m', name: 'Check for new claims', message: 'Check the new expense claims in the inbox.' },
214
- needsApprovals: true,
215
- notes: [DOCS_NOTE + ' — especially your expense policy', 'Set EXPENSE_APPROVER_EMAIL (Environment tab) to the approver\'s address'],
216
- },
217
- },
218
- {
219
- id: 'lead-follow-up',
220
- category: 'Sales',
221
- candidate: {
222
- title: 'Lead follow-up',
223
- 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.',
224
- trigger: { kind: 'schedule', description: 'every hour, for new leads', every: '1h' },
225
- hoursPerWeek: 3,
226
- fit: 6,
227
- risk: 'medium',
228
- asksFirst: ['sending the first email', 'updating the lead'],
229
- needs: [
230
- { kind: 'ability', name: 'lp-hubspot', description: 'your leads in HubSpot' },
231
- { kind: 'ability', name: 'lp-web-search', description: 'researching each lead' },
232
- { kind: 'ability', name: 'lp-gmail', description: 'the inbox to send from' },
233
- ],
234
- agent: {
235
- name: 'lead-follow-up',
236
- systemPrompt: `You follow up with new leads for our business.
237
-
238
- What we sell (edit this):
239
- - [What your product does, in one sentence]
240
- - [Who it is for]
241
- - [The one result customers care about most]
242
-
243
- When asked to follow up with new leads:
244
- 1. Find them with hubspot_find_contacts: lead_status "NEW", created_in_last_days 2.
245
- 2. For each, read hubspot_get_contact. Skip anyone with an owner, recent notes, or a last-contacted date: someone is already on it.
246
- 3. Research them with web_search and web_fetch: what their company does, its size, and anything recent that matters (funding, hiring, a launch). Note your sources.
247
- 4. Work out what they most likely need from us, in one sentence.
248
- 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.
249
- 6. Log what you sent and found with hubspot_add_note, and set the lead to "ATTEMPTED_TO_CONTACT" with hubspot_update_contact.
250
-
251
- Rules:
252
- - Never invent facts about a lead. If research finds nothing reliable, write a simple, honest email instead.
253
- - No pressure tactics, fake urgency or discounts you weren't told about.
254
- - If the lead looks like a competitor, a student or spam, add a note saying so and don't email them.`,
255
- scheduleMessage: 'Follow up with the new leads in HubSpot.',
256
- exampleMessage: 'Follow up with the new leads in HubSpot.',
257
- },
258
- },
259
- setup: {
260
- abilities: ['lp-hubspot', 'lp-web-search', 'lp-gmail'],
261
- keys: ['HUBSPOT_ACCESS_TOKEN', ...WEB_SEARCH_KEYS, ...GMAIL_KEYS],
262
- rules: [
263
- { name: 'template-notes-allowed', tool: 'hubspot_add_note', decision: 'allow' },
264
- { name: 'template-lead-updates-need-approval', tool: 'hubspot_update_contact', decision: 'ask' },
265
- { name: 'template-no-new-contacts', tool: 'hubspot_create_contact', decision: 'deny' },
266
- { name: 'template-emails-need-approval', tool: 'gmail_send', decision: 'ask' },
267
- ],
268
- schedule: { every: '1h', name: 'Follow up with new leads', message: 'Follow up with the new leads in HubSpot.' },
269
- needsApprovals: true,
270
- notes: ['Edit "What we sell" at the top of its instructions (Overview tab)'],
271
- },
272
- },
273
- {
274
- id: 'inbox-to-tasks',
275
- category: 'Operations',
276
- candidate: {
277
- title: 'Inbox to tasks',
278
- 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.',
279
- trigger: { kind: 'schedule', description: 'every 10 minutes, for new emails', every: '10m' },
280
- hoursPerWeek: 3,
281
- fit: 7,
282
- risk: 'low',
283
- asksFirst: ['replying to the sender'],
284
- needs: [
285
- { kind: 'ability', name: 'lp-gmail', description: 'the shared inbox' },
286
- { kind: 'app', name: 'GitHub', description: 'tasks, as GitHub issues' },
287
- ],
288
- agent: {
289
- name: 'inbox-to-tasks',
290
- systemPrompt: `You turn requests in our shared inbox into tasks, kept as GitHub issues.
291
-
292
- Tasks live in this GitHub repository: [owner/name] (edit this). Pass its owner and repo to every GitHub tool.
293
-
294
- Who handles what (edit this; GitHub usernames):
295
- - Billing and invoices: [username]
296
- - Technical problems: [username]
297
- - Everything else: [username]
298
-
299
- When asked to handle new emails:
300
- 1. Find them with gmail_search, query "is:unread in:inbox". Read each with gmail_get_thread.
301
- 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.
302
- 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.
303
- 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.
304
- 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.
305
- 6. Mark the email read with gmail_modify_labels.
306
-
307
- Rules:
308
- - If an email holds several requests, make one task for each.
309
- - Never promise a fix or a refund in the reply; only say who is handling it and by when.`,
310
- scheduleMessage: 'Turn the new emails in the shared inbox into tasks.',
311
- exampleMessage: 'Turn the new emails in the shared inbox into tasks.',
312
- },
313
- },
314
- setup: {
315
- abilities: ['lp-gmail'],
316
- apps: [{ id: GITHUB_APP, title: 'GitHub', tools: ['search_issues', 'issue_write', 'add_issue_comment'] }],
317
- keys: [...GMAIL_KEYS],
318
- rules: [
319
- { name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
320
- { name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
321
- { name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
322
- { name: 'template-tasks-allowed', tool: 'github_issue_write', decision: 'allow' },
323
- { name: 'template-task-updates-allowed', tool: 'github_add_issue_comment', decision: 'allow' },
324
- { name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
325
- ],
326
- schedule: { every: '10m', name: 'Check the shared inbox', message: 'Turn the new emails in the shared inbox into tasks.' },
327
- needsApprovals: true,
328
- 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'],
329
- },
330
- },
331
- {
332
- id: 'weekly-research-digest',
333
- category: 'Research',
334
- candidate: {
335
- title: 'Weekly research digest',
336
- summary: 'Every Monday morning, searches the web for news on the topics you choose and posts a short digest with links to Slack.',
337
- trigger: { kind: 'schedule', description: 'Mondays at 9:00', every: '7d' },
338
- hoursPerWeek: 2,
339
- fit: 6,
340
- risk: 'low',
341
- asksFirst: [],
342
- needs: [
343
- { kind: 'ability', name: 'lp-web-search', description: 'web search' },
344
- { kind: 'ability', name: 'lp-slack-notifier', description: 'posting the digest to Slack' },
345
- ],
346
- agent: {
347
- name: 'weekly-research-digest',
348
- systemPrompt: `You write a weekly research digest and post it to Slack.
349
-
350
- Topics to follow (edit this list):
351
- - [Your industry or market]
352
- - [Competitors, by name]
353
- - [Technologies or regulations you care about]
354
-
355
- Each week:
356
- 1. Search the web with web_search for what happened in the last 7 days on each topic.
357
- 2. Open the most relevant pages with web_fetch and read them; don't rely on search snippets.
358
- 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.
359
- 4. End with "Worth a closer look": the single most important item, and why.
360
- 5. Post it with post_to_slack.
361
-
362
- Rules:
363
- - Only include things published in the last 7 days, and give each item's date.
364
- - Prefer original sources over aggregators. Never invent a link.
365
- - If a topic had no real news, say so in one line.`,
366
- scheduleMessage: 'Write and post this week\'s research digest.',
367
- exampleMessage: 'Write and post this week\'s research digest.',
368
- },
369
- },
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,
370
15
  setup: {
371
- abilities: ['lp-web-search', 'lp-slack-notifier'],
372
- keys: [...WEB_SEARCH_KEYS, ...SLACK_KEYS],
373
- rules: [
374
- { name: 'template-search-allowed', tool: 'web_search', decision: 'allow' },
375
- { name: 'template-fetch-allowed', tool: 'web_fetch', decision: 'allow' },
376
- { name: 'template-digest-post-allowed', tool: 'post_to_slack', decision: 'allow' },
377
- ],
378
- schedule: { every: '7d', at: '09:00', weekday: 1, name: 'Weekly research digest', message: 'Write and post this week\'s research digest.' },
379
- needsApprovals: false,
380
- 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],
381
19
  },
382
- },
383
- ];
20
+ };
21
+ });
384
22
  export function starterTemplate(id) {
385
23
  return STARTER_TEMPLATES.find((t) => t.id === id);
386
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;