loopengine 0.1.97 → 0.1.99
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/dist/bin/ability-manager.d.ts +35 -2
- package/dist/bin/ability-manager.js +168 -35
- package/dist/core/ability-details.js +11 -0
- package/dist/core/mcp-abilities.d.ts +91 -0
- package/dist/core/mcp-abilities.js +400 -0
- package/dist/core/mcp-ability-generator.d.ts +59 -0
- package/dist/core/mcp-ability-generator.js +193 -0
- package/dist/core/mcp-apps.d.ts +95 -0
- package/dist/core/mcp-apps.js +254 -0
- package/dist/core/mcp-directory.d.ts +32 -0
- package/dist/core/mcp-directory.js +191 -0
- package/dist/core/run-agent.js +5 -2
- package/dist/core/system-agents/interviewer.d.ts +1 -1
- package/dist/core/system-agents/interviewer.js +1 -1
- package/dist/core/system-agents/templates.d.ts +11 -0
- package/dist/core/system-agents/templates.js +22 -16
- package/dist/core/system-agents/workflow-planner.d.ts +36 -3
- package/dist/core/system-agents/workflow-planner.js +24 -10
- package/dist/core/workspace-abilities.js +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/web/abilities-admin.d.ts +1 -1
- package/dist/web/abilities-admin.js +1 -1
- package/dist/web/agents-config-page.js +187 -11
- package/dist/web/console-shell.js +7 -5
- package/dist/web/dev-ui-styles.js +1 -1
- package/dist/web/global-config-page.js +293 -13
- package/dist/web/interview-admin.js +2 -0
- package/dist/web/templates-admin.d.ts +10 -2
- package/dist/web/templates-admin.js +36 -3
- package/dist/web/templates-page.js +1 -0
- package/dist/web/workflow-admin.d.ts +46 -0
- package/dist/web/workflow-admin.js +195 -3
- package/dist/web/workflow-page.js +33 -3
- package/package.json +10 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { nextTimeOfDay, parseTimeOfDay, workspaceTimeZone } from '../time-zone.js';
|
|
2
2
|
const GMAIL_KEYS = ['GMAIL_CLIENT_ID', 'GMAIL_CLIENT_SECRET', 'GMAIL_REFRESH_TOKEN'];
|
|
3
3
|
const SHOPIFY_KEYS = ['SHOPIFY_STORE_DOMAIN', 'SHOPIFY_ADMIN_TOKEN'];
|
|
4
|
-
const
|
|
4
|
+
const GITHUB_APP = 'io.github.github/github-mcp-server';
|
|
5
5
|
const SLACK_KEYS = ['SLACK_BOT_TOKEN', 'SLACK_DEFAULT_CHANNEL'];
|
|
6
6
|
const WEB_SEARCH_KEYS = [['TAVILY_API_KEY', 'BRAVE_API_KEY', 'FIRECRAWL_API_KEY']];
|
|
7
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)';
|
|
@@ -128,20 +128,22 @@ Rules:
|
|
|
128
128
|
risk: 'medium',
|
|
129
129
|
asksFirst: ['opening or commenting on issues'],
|
|
130
130
|
needs: [
|
|
131
|
-
{ kind: '
|
|
131
|
+
{ kind: 'app', name: 'GitHub', description: 'your repository\'s commits, deploys and issues' },
|
|
132
132
|
{ kind: 'ability', name: 'lp-slack-notifier', description: 'posting the summary to Slack' },
|
|
133
133
|
],
|
|
134
134
|
agent: {
|
|
135
135
|
name: 'incident-triage',
|
|
136
136
|
systemPrompt: `You help the on-call engineer triage incidents.
|
|
137
137
|
|
|
138
|
+
Our repository on GitHub: [owner/name] (edit this). Pass its owner and repo to every GitHub tool.
|
|
139
|
+
|
|
138
140
|
When you get an alert, error or bug report:
|
|
139
141
|
1. Summarise it in two sentences: what is failing and who is affected.
|
|
140
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).
|
|
141
|
-
3. Find what changed shortly before it started:
|
|
142
|
-
4. Check whether it's already known with github_search_issues; read a match with
|
|
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).
|
|
143
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).
|
|
144
|
-
6. If an issue already tracks it, add your findings with
|
|
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.
|
|
145
147
|
|
|
146
148
|
Rules:
|
|
147
149
|
- Only read on your own; never roll anything back yourself.
|
|
@@ -151,12 +153,13 @@ Rules:
|
|
|
151
153
|
},
|
|
152
154
|
},
|
|
153
155
|
setup: {
|
|
154
|
-
abilities: ['lp-
|
|
155
|
-
|
|
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],
|
|
156
159
|
rules: [
|
|
157
160
|
{ name: 'template-slack-summary-allowed', tool: 'post_to_slack', decision: 'allow' },
|
|
158
|
-
{ name: 'template-new-issues-need-approval', tool: '
|
|
159
|
-
{ name: 'template-comments-need-approval', tool: '
|
|
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' },
|
|
160
163
|
],
|
|
161
164
|
needsApprovals: true,
|
|
162
165
|
notes: ['Send it alerts: paste one into Chat, have your alerting tool call it over HTTP, or reply to it in Slack'],
|
|
@@ -280,12 +283,14 @@ Rules:
|
|
|
280
283
|
asksFirst: ['replying to the sender'],
|
|
281
284
|
needs: [
|
|
282
285
|
{ kind: 'ability', name: 'lp-gmail', description: 'the shared inbox' },
|
|
283
|
-
{ kind: '
|
|
286
|
+
{ kind: 'app', name: 'GitHub', description: 'tasks, as GitHub issues' },
|
|
284
287
|
],
|
|
285
288
|
agent: {
|
|
286
289
|
name: 'inbox-to-tasks',
|
|
287
290
|
systemPrompt: `You turn requests in our shared inbox into tasks, kept as GitHub issues.
|
|
288
291
|
|
|
292
|
+
Tasks live in this GitHub repository: [owner/name] (edit this). Pass its owner and repo to every GitHub tool.
|
|
293
|
+
|
|
289
294
|
Who handles what (edit this; GitHub usernames):
|
|
290
295
|
- Billing and invoices: [username]
|
|
291
296
|
- Technical problems: [username]
|
|
@@ -294,8 +299,8 @@ Who handles what (edit this; GitHub usernames):
|
|
|
294
299
|
When asked to handle new emails:
|
|
295
300
|
1. Find them with gmail_search, query "is:unread in:inbox". Read each with gmail_get_thread.
|
|
296
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.
|
|
297
|
-
3. Check whether a task already exists with github_search_issues (the sender and subject). If so, add the new details with
|
|
298
|
-
4. Otherwise open one with
|
|
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.
|
|
299
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.
|
|
300
305
|
6. Mark the email read with gmail_modify_labels.
|
|
301
306
|
|
|
@@ -307,14 +312,15 @@ Rules:
|
|
|
307
312
|
},
|
|
308
313
|
},
|
|
309
314
|
setup: {
|
|
310
|
-
abilities: ['lp-gmail'
|
|
311
|
-
|
|
315
|
+
abilities: ['lp-gmail'],
|
|
316
|
+
apps: [{ id: GITHUB_APP, title: 'GitHub', tools: ['search_issues', 'issue_write', 'add_issue_comment'] }],
|
|
317
|
+
keys: [...GMAIL_KEYS],
|
|
312
318
|
rules: [
|
|
313
319
|
{ name: 'template-read-mail-allowed', tool: 'gmail_search', decision: 'allow' },
|
|
314
320
|
{ name: 'template-read-thread-allowed', tool: 'gmail_get_thread', decision: 'allow' },
|
|
315
321
|
{ name: 'template-labels-allowed', tool: 'gmail_modify_labels', decision: 'allow' },
|
|
316
|
-
{ name: 'template-tasks-allowed', tool: '
|
|
317
|
-
{ name: 'template-task-updates-allowed', tool: '
|
|
322
|
+
{ name: 'template-tasks-allowed', tool: 'github_issue_write', decision: 'allow' },
|
|
323
|
+
{ name: 'template-task-updates-allowed', tool: 'github_add_issue_comment', decision: 'allow' },
|
|
318
324
|
{ name: 'template-replies-need-approval', tool: 'gmail_send', decision: 'ask' },
|
|
319
325
|
],
|
|
320
326
|
schedule: { every: '10m', name: 'Check the shared inbox', message: 'Turn the new emails in the shared inbox into tasks.' },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ToolSchema } from '#core/agent-config.js';
|
|
2
2
|
import type { ModelCall } from '#core/run-agent.js';
|
|
3
|
-
import type { StarterTemplate } from './templates.js';
|
|
3
|
+
import type { StarterTemplate, TemplateApp } from './templates.js';
|
|
4
4
|
export type Decision = 'allow' | 'ask' | 'deny';
|
|
5
5
|
export interface WorkflowStep {
|
|
6
6
|
title: string;
|
|
@@ -18,6 +18,30 @@ export interface WorkflowStep {
|
|
|
18
18
|
* an ability): the source's name. It's copied to the agent that runs the
|
|
19
19
|
* workflow, not installed. */
|
|
20
20
|
connection?: string;
|
|
21
|
+
/** For a step done by an MCP server's tool, when no ability has it: the
|
|
22
|
+
* server's id (search_mcp_servers). `tool` is then the server's own
|
|
23
|
+
* tool name, if the planner could see them. On Create the server
|
|
24
|
+
* becomes a private ability (core/mcp-ability-generator.ts) and the
|
|
25
|
+
* step points at its tool instead. */
|
|
26
|
+
mcp?: string;
|
|
27
|
+
}
|
|
28
|
+
/** An MCP server a plan would set up as a private ability. */
|
|
29
|
+
export interface PlannedMcpServer {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
url: string;
|
|
33
|
+
description: string;
|
|
34
|
+
/** Saved by the workspace, or on its publisher's verified domain. */
|
|
35
|
+
verified: boolean;
|
|
36
|
+
source: 'saved' | 'registry';
|
|
37
|
+
/** The private ability it becomes, e.g. mcp-linear. */
|
|
38
|
+
ability: string;
|
|
39
|
+
/** The setting its key is saved as, when it takes one. */
|
|
40
|
+
keyEnv: string;
|
|
41
|
+
/** Whether that setting already has a value. */
|
|
42
|
+
keySet: boolean;
|
|
43
|
+
/** The workspace already has an ability made from this server. */
|
|
44
|
+
existing?: string;
|
|
21
45
|
}
|
|
22
46
|
export interface WorkflowPlan {
|
|
23
47
|
name: string;
|
|
@@ -64,6 +88,11 @@ export interface WorkflowPlan {
|
|
|
64
88
|
};
|
|
65
89
|
/** What the workspace can't do yet, in plain words. */
|
|
66
90
|
gaps: string[];
|
|
91
|
+
/** MCP servers its steps use, set up on Create (filled in after planning). */
|
|
92
|
+
mcpServers?: PlannedMcpServer[];
|
|
93
|
+
/** Connected apps it adds to the agent, with just the tools its steps
|
|
94
|
+
* use (filled in on Create, from mcpServers). */
|
|
95
|
+
apps?: TemplateApp[];
|
|
67
96
|
}
|
|
68
97
|
export interface WorkflowPlannerTools {
|
|
69
98
|
/** Every agent: its tools, installed abilities, rules and messaging. */
|
|
@@ -81,9 +110,13 @@ export interface WorkflowPlannerTools {
|
|
|
81
110
|
/** An ability's tools (with the decision each ships with) and settings. */
|
|
82
111
|
abilityDetails(spec: string): Promise<unknown>;
|
|
83
112
|
listConnections(): Promise<Record<string, unknown>>;
|
|
113
|
+
/** MCP servers matching a query (core/mcp-directory.ts). Optional: without it the planner only plans with abilities. */
|
|
114
|
+
searchMcpServers?(query: string): Promise<unknown>;
|
|
115
|
+
/** A server's tools, when it can be listed without a key (or with one already set). */
|
|
116
|
+
mcpServerTools?(id: string): Promise<unknown>;
|
|
84
117
|
}
|
|
85
|
-
export declare const WORKFLOW_PLANNER_PROMPT = "You turn a workflow a person describes into a plan for a LoopEngine agent.\n\nLoopEngine agents call tools. Tools come from abilities (installable packages: public ones on npm, or the workspace's own private ones) or are built in. Each tool has a permission: allow (runs on its own), ask (waits for a person to approve, edit or deny it) or deny. When nobody is in Chat, questions and approvals go to the agent's messaging app (Slack, Telegram, Lark or email) and the run waits, for days if needed, then carries on.\n\nHow to plan:\n1. Look before you plan: list_agents (what exists, and what each agent's tools and rules are), list_workspace_abilities, search_public_abilities for each kind of step
|
|
86
|
-
export declare function workflowPlannerTools(): ToolSchema[];
|
|
118
|
+
export declare const WORKFLOW_PLANNER_PROMPT = "You turn a workflow a person describes into a plan for a LoopEngine agent.\n\nLoopEngine agents call tools. Tools come from abilities (installable packages: public ones on npm, or the workspace's own private ones) or are built in. Each tool has a permission: allow (runs on its own), ask (waits for a person to approve, edit or deny it) or deny. When nobody is in Chat, questions and approvals go to the agent's messaging app (Slack, Telegram, Lark or email) and the run waits, for days if needed, then carries on.\n\nHow to plan:\n1. Look before you plan: list_agents (what exists, and what each agent's tools and rules are), list_workspace_abilities, search_public_abilities for each kind of step, ability_details for any ability you might use (its exact tool names and the settings it needs), and list_connections.\n2. Split the workflow into steps, in order. For each step:\n - something to fetch or do: kind \"tool\", with the exact tool name and where it comes from. Pick the source in this order:\n a. A tool the target agent already has (from list_agents): use it, nothing to install.\n b. An ability: a workspace ability (list_workspace_abilities) or a public one (search_public_abilities, e.g. \"firecrawl\" or \"web search\" for searching and reading the web). Set \"ability\" to its install spec (the npm name, or a workspace ability's spec) and use the exact tool name from ability_details.\n c. Only when no ability can do it, and search_mcp_servers is available: an app's MCP server. Search for the app (e.g. \"linear\", \"stripe\"), pick the app's own server (official), else a saved or verified one, then call mcp_server_tools. Set \"mcp\" to the server's id, and \"tool\" to its exact tool name when mcp_server_tools lists them (leave \"tool\" empty when it needs a key first, and say in \"detail\" what the step does). It's set up as a private ability when the workflow is created, which picks its tools and their permissions, so add no rules for MCP tools.\n - the person chooses or answers (\"ask me which\", \"let me pick\"): kind \"ask\", tool \"system_ask_user\" (built in, no ability). Say what options are offered. It reaches them through the agent's messaging app by itself, so don't add a separate step to post to Slack or another chat first, and don't install a chat ability just to ask.\n - the person reviews or approves before something happens (\"let me review it\", \"before it's sent\"): kind \"approval\", on the tool that does that thing; its rule becomes \"ask\".\n - the agent's own work (writing, summarising, choosing): kind \"other\".\n - nothing found can do it: set \"missing\" to what's needed, and add it to gaps.\n3. Rules: every tool the workflow uses gets one. Reading and searching: allow. Anything a person should see first (sending, publishing, paying, creating something others see): ask. Never set deny unless asked.\n4. Skill: the steps in order, as numbered instructions the agent follows, naming the tools and saying exactly when to stop and ask. Write it so the agent keeps going after each answer or decision.\n5. Trigger: a schedule (\"every Monday at 9\" is every \"7d\", at \"09:00\", weekday 1) or on_request (started from Chat or a message). startMessage is what the agent receives when it starts.\n6. Target: an existing agent only when it already has (or can install) the tools, none of the workflow's rules would change a decision it already has, and the same people would approve. Otherwise a new agent, with a short lowercase-hyphenated name and a system prompt saying who it is and what it's for. Give the reason either way.\n7. keys: the setting names its abilities need that list_connections or the agents don't show as set (from ability_details settings). Not for MCP servers: their keys are asked for on the plan.\n\nThen call save_plan once, with the whole plan. Don't ask the person questions: if something is unclear, plan the most reasonable version and note the assumption in gaps.";
|
|
119
|
+
export declare function workflowPlannerTools(withMcp?: boolean): ToolSchema[];
|
|
87
120
|
/** lowercase-hyphenated, 2–40 characters. */
|
|
88
121
|
export declare function slugify(value: string, fallback?: string): string;
|
|
89
122
|
/** A plan from the model's save_plan input, or undefined when it lacks a
|
|
@@ -4,13 +4,12 @@ export const WORKFLOW_PLANNER_PROMPT = `You turn a workflow a person describes i
|
|
|
4
4
|
LoopEngine agents call tools. Tools come from abilities (installable packages: public ones on npm, or the workspace's own private ones) or are built in. Each tool has a permission: allow (runs on its own), ask (waits for a person to approve, edit or deny it) or deny. When nobody is in Chat, questions and approvals go to the agent's messaging app (Slack, Telegram, Lark or email) and the run waits, for days if needed, then carries on.
|
|
5
5
|
|
|
6
6
|
How to plan:
|
|
7
|
-
1. Look before you plan: list_agents (what exists, and what each agent's tools and rules are), list_workspace_abilities, search_public_abilities for each kind of step
|
|
7
|
+
1. Look before you plan: list_agents (what exists, and what each agent's tools and rules are), list_workspace_abilities, search_public_abilities for each kind of step, ability_details for any ability you might use (its exact tool names and the settings it needs), and list_connections.
|
|
8
8
|
2. Split the workflow into steps, in order. For each step:
|
|
9
9
|
- something to fetch or do: kind "tool", with the exact tool name and where it comes from. Pick the source in this order:
|
|
10
10
|
a. A tool the target agent already has (from list_agents): use it, nothing to install.
|
|
11
11
|
b. An ability: a workspace ability (list_workspace_abilities) or a public one (search_public_abilities, e.g. "firecrawl" or "web search" for searching and reading the web). Set "ability" to its install spec (the npm name, or a workspace ability's spec) and use the exact tool name from ability_details.
|
|
12
|
-
c. Only when no ability
|
|
13
|
-
An ability beats a connected tool even when another agent already has the connected one: it's faster, needs only a key, and travels with the agent. If you chose a connected tool, say why in gaps.
|
|
12
|
+
c. Only when no ability can do it, and search_mcp_servers is available: an app's MCP server. Search for the app (e.g. "linear", "stripe"), pick the app's own server (official), else a saved or verified one, then call mcp_server_tools. Set "mcp" to the server's id, and "tool" to its exact tool name when mcp_server_tools lists them (leave "tool" empty when it needs a key first, and say in "detail" what the step does). It's set up as a private ability when the workflow is created, which picks its tools and their permissions, so add no rules for MCP tools.
|
|
14
13
|
- the person chooses or answers ("ask me which", "let me pick"): kind "ask", tool "system_ask_user" (built in, no ability). Say what options are offered. It reaches them through the agent's messaging app by itself, so don't add a separate step to post to Slack or another chat first, and don't install a chat ability just to ask.
|
|
15
14
|
- the person reviews or approves before something happens ("let me review it", "before it's sent"): kind "approval", on the tool that does that thing; its rule becomes "ask".
|
|
16
15
|
- the agent's own work (writing, summarising, choosing): kind "other".
|
|
@@ -19,10 +18,10 @@ How to plan:
|
|
|
19
18
|
4. Skill: the steps in order, as numbered instructions the agent follows, naming the tools and saying exactly when to stop and ask. Write it so the agent keeps going after each answer or decision.
|
|
20
19
|
5. Trigger: a schedule ("every Monday at 9" is every "7d", at "09:00", weekday 1) or on_request (started from Chat or a message). startMessage is what the agent receives when it starts.
|
|
21
20
|
6. Target: an existing agent only when it already has (or can install) the tools, none of the workflow's rules would change a decision it already has, and the same people would approve. Otherwise a new agent, with a short lowercase-hyphenated name and a system prompt saying who it is and what it's for. Give the reason either way.
|
|
22
|
-
7. keys: the setting names its abilities need that list_connections or the agents don't show as set (from ability_details settings).
|
|
21
|
+
7. keys: the setting names its abilities need that list_connections or the agents don't show as set (from ability_details settings). Not for MCP servers: their keys are asked for on the plan.
|
|
23
22
|
|
|
24
23
|
Then call save_plan once, with the whole plan. Don't ask the person questions: if something is unclear, plan the most reasonable version and note the assumption in gaps.`;
|
|
25
|
-
export function workflowPlannerTools() {
|
|
24
|
+
export function workflowPlannerTools(withMcp = false) {
|
|
26
25
|
const step = {
|
|
27
26
|
type: 'object',
|
|
28
27
|
properties: {
|
|
@@ -30,9 +29,9 @@ export function workflowPlannerTools() {
|
|
|
30
29
|
kind: { type: 'string', enum: ['tool', 'ask', 'approval', 'other'] },
|
|
31
30
|
tool: { type: 'string' },
|
|
32
31
|
ability: { type: 'string' },
|
|
32
|
+
mcp: { type: 'string', description: "an MCP server's id, for a step no ability can do" },
|
|
33
33
|
detail: { type: 'string' },
|
|
34
34
|
missing: { type: 'string' },
|
|
35
|
-
connection: { type: 'string' },
|
|
36
35
|
},
|
|
37
36
|
required: ['title', 'kind'],
|
|
38
37
|
};
|
|
@@ -42,6 +41,10 @@ export function workflowPlannerTools() {
|
|
|
42
41
|
{ name: 'search_public_abilities', description: 'Search public LoopEngine abilities on npm, e.g. "web search", "wordpress", "slack".', input_schema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] } },
|
|
43
42
|
{ name: 'ability_details', description: "An ability's exact tool names, what each does, the permission each ships with, and the settings it needs. spec: its npm name or workspace spec.", input_schema: { type: 'object', properties: { spec: { type: 'string' } }, required: ['spec'] } },
|
|
44
43
|
{ name: 'list_connections', description: 'What is set up: model providers with keys, messaging apps per agent.', input_schema: { type: 'object', properties: {} } },
|
|
44
|
+
...(withMcp ? [
|
|
45
|
+
{ name: 'search_mcp_servers', description: "Search apps' MCP servers (the workspace's saved ones, then the public MCP Registry), for a step no ability can do. query: the app's name, e.g. \"linear\".", input_schema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] } },
|
|
46
|
+
{ name: 'mcp_server_tools', description: "An MCP server's tools (name, description, read-only or not), or a note that it needs a key before they can be seen. id: from search_mcp_servers.", input_schema: { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] } },
|
|
47
|
+
] : []),
|
|
45
48
|
{
|
|
46
49
|
name: 'save_plan',
|
|
47
50
|
description: 'Save the workflow plan. Call once, at the end.',
|
|
@@ -113,6 +116,7 @@ export function validateWorkflowPlan(raw) {
|
|
|
113
116
|
...(str(step.detail, 600) ? { detail: str(step.detail, 600) } : {}),
|
|
114
117
|
...(str(step.missing, 300) ? { missing: str(step.missing, 300) } : {}),
|
|
115
118
|
...(str(step.connection, 120) ? { connection: str(step.connection, 120) } : {}),
|
|
119
|
+
...(str(step.mcp, 200) && !str(step.ability, 300) ? { mcp: str(step.mcp, 200) } : {}),
|
|
116
120
|
}];
|
|
117
121
|
});
|
|
118
122
|
if (!steps.length)
|
|
@@ -128,14 +132,17 @@ export function validateWorkflowPlan(raw) {
|
|
|
128
132
|
...(typeof triggerRaw.weekday === 'number' && triggerRaw.weekday >= 0 && triggerRaw.weekday <= 7 ? { weekday: triggerRaw.weekday } : {}),
|
|
129
133
|
}
|
|
130
134
|
: { kind: 'on_request', description: str(triggerRaw.description, 200) ?? 'when asked' };
|
|
135
|
+
// An MCP step's permissions are set when its server becomes an ability.
|
|
136
|
+
const mcpTools = new Set(steps.flatMap((s) => (s.mcp && s.tool ? [s.tool] : [])));
|
|
131
137
|
const rules = (Array.isArray(r.rules) ? r.rules : []).flatMap((x) => {
|
|
132
138
|
const rule = (x ?? {});
|
|
133
139
|
const tool = str(rule.tool, 120);
|
|
134
140
|
return tool && isDecision(rule.decision) && tool !== 'system_ask_user' ? [{ tool, decision: rule.decision }] : [];
|
|
135
|
-
});
|
|
136
|
-
// An approval step's tool always asks first
|
|
141
|
+
}).filter((rule) => !mcpTools.has(rule.tool));
|
|
142
|
+
// An approval step's tool always asks first (an MCP step's tool gets
|
|
143
|
+
// that when its ability is made).
|
|
137
144
|
for (const step of steps) {
|
|
138
|
-
if (step.kind !== 'approval' || !step.tool)
|
|
145
|
+
if (step.kind !== 'approval' || !step.tool || step.mcp)
|
|
139
146
|
continue;
|
|
140
147
|
const rule = rules.find((x) => x.tool === step.tool);
|
|
141
148
|
if (rule)
|
|
@@ -178,7 +185,7 @@ export async function planWorkflowWithModel(modelCall, description, tools) {
|
|
|
178
185
|
for (let round = 0; round <= MAX_TOOL_ROUNDS; round++) {
|
|
179
186
|
let response;
|
|
180
187
|
try {
|
|
181
|
-
response = await modelCall(messages, WORKFLOW_PLANNER_PROMPT, workflowPlannerTools());
|
|
188
|
+
response = await modelCall(messages, WORKFLOW_PLANNER_PROMPT, workflowPlannerTools(Boolean(tools.searchMcpServers)));
|
|
182
189
|
}
|
|
183
190
|
catch (err) {
|
|
184
191
|
// A reply cut off part-way (its tool arguments end mid-JSON) usually
|
|
@@ -229,6 +236,12 @@ export async function planWorkflowWithModel(modelCall, description, tools) {
|
|
|
229
236
|
case 'list_connections':
|
|
230
237
|
output = await tools.listConnections();
|
|
231
238
|
break;
|
|
239
|
+
case 'search_mcp_servers':
|
|
240
|
+
output = tools.searchMcpServers ? await tools.searchMcpServers(String(input.query ?? '')) : 'not available';
|
|
241
|
+
break;
|
|
242
|
+
case 'mcp_server_tools':
|
|
243
|
+
output = tools.mcpServerTools ? await tools.mcpServerTools(String(input.id ?? '')) : 'not available';
|
|
244
|
+
break;
|
|
232
245
|
case 'save_plan':
|
|
233
246
|
plan = validateWorkflowPlan(input);
|
|
234
247
|
output = plan ? 'saved' : 'that plan needs a name, summary, at least one step, a skill body and newAgent.systemPrompt; fix and call again';
|
|
@@ -266,6 +279,7 @@ export function workflowTemplate(plan) {
|
|
|
266
279
|
},
|
|
267
280
|
setup: {
|
|
268
281
|
abilities: plan.abilities,
|
|
282
|
+
...(plan.apps?.length ? { apps: plan.apps } : {}),
|
|
269
283
|
keys: plan.keys,
|
|
270
284
|
skills: [{ id: plan.id, description: plan.skill.description, body: plan.skill.body }],
|
|
271
285
|
rules: plan.rules.map((r) => ({ name: slugify(`${plan.id}-${r.tool}`, `${plan.id}-rule`), tool: r.tool, decision: r.decision })),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// The workspace's own copies of private abilities: installed once from a
|
|
2
2
|
// private source (core/ability-sources.ts) on the console's Abilities
|
|
3
|
-
// page, then added to any agent from its
|
|
3
|
+
// page, then added to any agent from its Apps tab. Each copy is a
|
|
4
4
|
// folder under abilities/ in the project root (or the folder
|
|
5
5
|
// LOOPENGINE_ABILITIES_DIR names), and abilities/workspace.json says
|
|
6
6
|
// where each came from:
|
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,9 @@ export type { PublicAbilitySearchResult } from './web/abilities-admin.js';
|
|
|
99
99
|
export { AbilitySourceError, abilityOrigin, readAbilitySources, saveAbilitySource, removeAbilitySource, listSourceAbilities, sourceTokenVar, type AbilitySource, type SourceAbility, type AbilityOrigin } from './core/ability-sources.js';
|
|
100
100
|
export { describeAbilitySources, listPrivateAbilities, saveAbilitySourceFromConsole, removeAbilitySourceFromConsole } from './web/ability-sources-admin.js';
|
|
101
101
|
export { readAbilityDetails, readToolDefinitions, type AbilityDetails } from './core/ability-details.js';
|
|
102
|
+
export { loadMcpAbilityTools, mcpAbilityLoadError, closeMcpConnections, resolveMcpManifest, listMcpServerTools, isMcpAuthError, type McpServerManifest, type McpAuth, type InstalledMcpRecord, type McpToolInfo } from './core/mcp-abilities.js';
|
|
103
|
+
export { searchMcpServers, getMcpServer, savedMcpServers, type McpDirectoryServer } from './core/mcp-directory.js';
|
|
104
|
+
export { generateMcpAbility, mcpAbilityNames, McpAbilityGenerationError, type GeneratedMcpAbility, type McpAbilityRequest } from './core/mcp-ability-generator.js';
|
|
102
105
|
export { abilityDetails } from './web/ability-details-admin.js';
|
|
103
106
|
export { WorkspaceAbilityError, listWorkspaceAbilities, addWorkspaceAbility, removeWorkspaceAbility, workspaceAbilitiesDir, type WorkspaceAbility } from './core/workspace-abilities.js';
|
|
104
107
|
export { installWorkspaceAbility, installUploadedAbility, MAX_UPLOAD_BYTES, type AbilityUpload } from './web/workspace-abilities-admin.js';
|
package/dist/index.js
CHANGED
|
@@ -89,6 +89,9 @@ export { searchPublicAbilities, fetchLatestAbilityVersion } from './web/abilitie
|
|
|
89
89
|
export { AbilitySourceError, abilityOrigin, readAbilitySources, saveAbilitySource, removeAbilitySource, listSourceAbilities, sourceTokenVar } from './core/ability-sources.js';
|
|
90
90
|
export { describeAbilitySources, listPrivateAbilities, saveAbilitySourceFromConsole, removeAbilitySourceFromConsole } from './web/ability-sources-admin.js';
|
|
91
91
|
export { readAbilityDetails, readToolDefinitions } from './core/ability-details.js';
|
|
92
|
+
export { loadMcpAbilityTools, mcpAbilityLoadError, closeMcpConnections, resolveMcpManifest, listMcpServerTools, isMcpAuthError } from './core/mcp-abilities.js';
|
|
93
|
+
export { searchMcpServers, getMcpServer, savedMcpServers } from './core/mcp-directory.js';
|
|
94
|
+
export { generateMcpAbility, mcpAbilityNames, McpAbilityGenerationError } from './core/mcp-ability-generator.js';
|
|
92
95
|
export { abilityDetails } from './web/ability-details-admin.js';
|
|
93
96
|
export { WorkspaceAbilityError, listWorkspaceAbilities, addWorkspaceAbility, removeWorkspaceAbility, workspaceAbilitiesDir } from './core/workspace-abilities.js';
|
|
94
97
|
export { installWorkspaceAbility, installUploadedAbility, MAX_UPLOAD_BYTES } from './web/workspace-abilities-admin.js';
|
|
@@ -8,7 +8,7 @@ export declare function searchPublicAbilities(query?: string): Promise<PublicAbi
|
|
|
8
8
|
* `packageName`, or null if it can't be determined — not published
|
|
9
9
|
* there at all (a private/file:/git-installed ability, the common case
|
|
10
10
|
* during local development), a network hiccup, or a timeout. Best-effort
|
|
11
|
-
* only: the
|
|
11
|
+
* only: the Apps tab uses this to decide whether an installed
|
|
12
12
|
* ability's own "Upgrade" button is worth showing, not to block
|
|
13
13
|
* anything, so a failure here surfaces as "can't tell, show the button
|
|
14
14
|
* anyway" rather than an error the operator has to deal with. */
|
|
@@ -43,7 +43,7 @@ const NPM_LATEST_VERSION_TIMEOUT_MS = 5000;
|
|
|
43
43
|
* `packageName`, or null if it can't be determined — not published
|
|
44
44
|
* there at all (a private/file:/git-installed ability, the common case
|
|
45
45
|
* during local development), a network hiccup, or a timeout. Best-effort
|
|
46
|
-
* only: the
|
|
46
|
+
* only: the Apps tab uses this to decide whether an installed
|
|
47
47
|
* ability's own "Upgrade" button is worth showing, not to block
|
|
48
48
|
* anything, so a failure here surfaces as "can't tell, show the button
|
|
49
49
|
* anyway" rather than an error the operator has to deal with. */
|