loopengine 0.1.91 → 0.1.93
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/README.md +16 -4
- package/dist/bin/cli.js +3 -2
- package/dist/core/agent-config.d.ts +19 -7
- package/dist/core/email-senders.d.ts +26 -0
- package/dist/core/email-senders.js +108 -0
- package/dist/core/http-notifier.js +6 -6
- package/dist/core/http-notify-triggers/email.d.ts +14 -8
- package/dist/core/http-notify-triggers/email.js +70 -28
- package/dist/core/notifier-file.d.ts +11 -3
- package/dist/core/notifier-file.js +37 -5
- package/dist/core/scheduler.d.ts +6 -0
- package/dist/core/scheduler.js +24 -6
- package/dist/core/system-agents/templates.d.ts +53 -0
- package/dist/core/system-agents/templates.js +396 -0
- package/dist/core/system-tools/schedule.js +2 -2
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -1
- package/dist/web/agents-config-page.js +25 -7
- package/dist/web/agents-list-page.js +2 -2
- package/dist/web/console-shell.js +2 -1
- package/dist/web/dev-ui-styles.js +1 -0
- package/dist/web/interview-admin.d.ts +12 -0
- package/dist/web/interview-admin.js +46 -22
- package/dist/web/notifier-admin.js +11 -2
- package/dist/web/templates-admin.d.ts +93 -0
- package/dist/web/templates-admin.js +227 -0
- package/dist/web/templates-page.d.ts +1 -0
- package/dist/web/templates-page.js +155 -0
- package/package.json +3 -1
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
// The console's Templates page (/templates): ready-made agents
|
|
2
|
+
// (core/system-agents/templates.ts) a person can preview and create in
|
|
3
|
+
// one click (web/templates-admin.ts behind /starter-templates/*). Same
|
|
4
|
+
// shell and styles as the other console pages. Like them, its inline
|
|
5
|
+
// script avoids backslashes and template placeholders, since it lives
|
|
6
|
+
// inside a template literal.
|
|
7
|
+
import { devUiCss } from './dev-ui-styles.js';
|
|
8
|
+
import { consoleShellScript, consoleSidebarHtml } from './console-shell.js';
|
|
9
|
+
export const templatesPageHtml = `<!doctype html>
|
|
10
|
+
<html lang="en">
|
|
11
|
+
<head>
|
|
12
|
+
<meta charset="utf-8">
|
|
13
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
14
|
+
<title>Templates</title>
|
|
15
|
+
<style>${devUiCss}
|
|
16
|
+
.tp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 12px; max-width: 1000px; }
|
|
17
|
+
.tp-card { border: 1px solid var(--line); border-radius: var(--radius); padding: 14px 16px; display: grid; gap: 8px; align-content: start; }
|
|
18
|
+
.tp-card h3 { margin: 0; font-size: 15px; }
|
|
19
|
+
.tp-cat { justify-self: start; font-size: 11.5px; font-weight: 600; color: var(--ink-muted); background: var(--surface); padding: 2px 8px; border-radius: 999px; }
|
|
20
|
+
.tp-card .meta { font-size: 12.5px; color: var(--ink-muted); display: grid; gap: 4px; }
|
|
21
|
+
.tp-card .row { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
22
|
+
.tp-preview { border-top: 1px solid var(--line); padding-top: 10px; display: grid; gap: 8px; font-size: 13px; }
|
|
23
|
+
.tp-preview pre { white-space: pre-wrap; max-height: 220px; overflow: auto; margin: 0; }
|
|
24
|
+
.tp-btn { display: inline-block; padding: 6px 12px; border-radius: 6px; text-decoration: none; font-size: 13px; font-weight: 500; background: var(--accent); color: var(--on-accent); }
|
|
25
|
+
.tp-btn.plain { background: none; color: var(--ink); border: 1px solid var(--line); }
|
|
26
|
+
.hint { font-size: 12px; color: var(--ink-muted); }
|
|
27
|
+
.error { font-size: 13px; color: var(--bad); }
|
|
28
|
+
</style>
|
|
29
|
+
</head>
|
|
30
|
+
<body data-le-page="templates">
|
|
31
|
+
<div class="le-app">
|
|
32
|
+
${consoleSidebarHtml}
|
|
33
|
+
<main class="le-main">
|
|
34
|
+
<div class="le-head plain">
|
|
35
|
+
<div class="le-head-row">
|
|
36
|
+
<div class="le-title"><button type="button" class="le-menu-btn" data-le-menu aria-label="Open navigation">☰</button>
|
|
37
|
+
<div><h1>Templates</h1><p>Ready-made agents to start from. Each one asks you before it sends, pays, changes or deletes anything.</p></div>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<div class="le-scroll"><div class="le-page" id="tpPage"><p class="hint">Loading…</p></div></div>
|
|
42
|
+
</main>
|
|
43
|
+
</div>
|
|
44
|
+
${consoleShellScript}
|
|
45
|
+
<script>
|
|
46
|
+
(function () {
|
|
47
|
+
var page = document.getElementById('tpPage');
|
|
48
|
+
var ELLIPSIS = String.fromCharCode(8230);
|
|
49
|
+
|
|
50
|
+
function esc(s) {
|
|
51
|
+
return String(s == null ? '' : s).replace(/[&<>"']/g, function (c) {
|
|
52
|
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function api(method, path) {
|
|
56
|
+
return fetch(path, { method: method, cache: 'no-store' })
|
|
57
|
+
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); });
|
|
58
|
+
}
|
|
59
|
+
function checklistHtml(items) {
|
|
60
|
+
if (!items.length) return '<p>Nothing else to set up.</p>';
|
|
61
|
+
return '<ul style="margin:0;padding-left:18px">' + items.map(function (it) {
|
|
62
|
+
return '<li>' + (it.href ? '<a href="' + esc(it.href) + '">' + esc(it.label) + '</a>' : esc(it.label)) + '</li>';
|
|
63
|
+
}).join('') + '</ul>';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
var canInstall = true;
|
|
67
|
+
var DECISIONS = { allow: 'runs on its own', ask: 'asks you first', deny: 'never' };
|
|
68
|
+
var CHECK = String.fromCharCode(10003);
|
|
69
|
+
|
|
70
|
+
function cardHtml(t) {
|
|
71
|
+
return '<div class="tp-card" data-id="' + esc(t.id) + '">' +
|
|
72
|
+
'<span class="tp-cat">' + esc(t.category) + '</span>' +
|
|
73
|
+
'<h3>' + esc(t.title) + '</h3>' +
|
|
74
|
+
'<div>' + esc(t.summary) + '</div>' +
|
|
75
|
+
'<div class="meta">' +
|
|
76
|
+
'<div><strong>Runs:</strong> ' + esc(t.trigger) + '</div>' +
|
|
77
|
+
(t.asksFirst.length ? '<div><strong>Asks you first:</strong> ' + esc(t.asksFirst.join(', ')) + '</div>' : '') +
|
|
78
|
+
'<div><strong>Installs:</strong> ' + t.abilities.map(function (a) { return '<code>' + esc(a) + '</code>'; }).join(' ') + '</div>' +
|
|
79
|
+
'</div>' +
|
|
80
|
+
'<div class="row"><button type="button" class="primary tp-use">Use this template</button></div>' +
|
|
81
|
+
'<div class="tp-slot"></div></div>';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function listHtml(items) {
|
|
85
|
+
return '<ul style="margin:4px 0 0;padding-left:18px">' + items.join('') + '</ul>';
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function show() {
|
|
89
|
+
api('GET', '/starter-templates').then(function (r) {
|
|
90
|
+
if (!r.ok) { page.innerHTML = '<p class="error">' + esc(r.body.error || 'Could not load the templates') + '</p>'; return; }
|
|
91
|
+
canInstall = r.body.canInstallAbilities !== false;
|
|
92
|
+
page.innerHTML = '<div class="tp-grid">' + r.body.templates.map(cardHtml).join('') + '</div>' +
|
|
93
|
+
'<p class="hint" style="margin-top:16px">Not what you need? <a href="/interview">Find your first agent</a> suggests one from your own work, or <a href="/agents?new=1">start from a blank agent</a>.</p>';
|
|
94
|
+
Array.prototype.forEach.call(page.querySelectorAll('.tp-use'), function (btn) {
|
|
95
|
+
btn.addEventListener('click', function () { preview(btn.closest('.tp-card')); });
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function preview(card) {
|
|
101
|
+
var id = card.getAttribute('data-id');
|
|
102
|
+
var slot = card.querySelector('.tp-slot');
|
|
103
|
+
slot.innerHTML = '<p class="hint">Loading' + ELLIPSIS + '</p>';
|
|
104
|
+
api('GET', '/starter-templates/' + encodeURIComponent(id) + '/preview').then(function (r) {
|
|
105
|
+
if (!r.ok) { slot.innerHTML = '<p class="error">' + esc(r.body.error || 'Could not prepare it') + '</p>'; return; }
|
|
106
|
+
var p = r.body;
|
|
107
|
+
var settings = Object.keys(p.settings || {});
|
|
108
|
+
slot.innerHTML = '<div class="tp-preview">' +
|
|
109
|
+
'<div><strong>Agent:</strong> <code>' + esc(p.name) + '</code></div>' +
|
|
110
|
+
'<div><strong>Instructions</strong> <span class="hint">(edit them later in its Overview tab)</span><pre>' + esc(p.systemPrompt) + '</pre></div>' +
|
|
111
|
+
'<div><strong>Installs</strong> <span class="hint">(abilities: their tools, skills and permission rules)</span>' +
|
|
112
|
+
listHtml(p.abilities.map(function (a) { return '<li><code>' + esc(a) + '</code></li>'; })) +
|
|
113
|
+
(canInstall ? '' : '<p class="error" style="margin:4px 0 0">Installing abilities needs <code>LOOPENGINE_ADMIN_AUTH</code> set on the server. Without it, the agent is created and its abilities head the checklist.</p>') + '</div>' +
|
|
114
|
+
'<div><strong>Permissions</strong> <span class="hint">(anything not listed asks you first)</span>' +
|
|
115
|
+
listHtml(p.rules.map(function (rule) { return '<li><code>' + esc(rule.tool) + '</code>: ' + esc(DECISIONS[rule.decision] || rule.decision) + '</li>'; })) + '</div>' +
|
|
116
|
+
'<div><strong>Keys it needs</strong>' +
|
|
117
|
+
listHtml(p.keys.map(function (k) {
|
|
118
|
+
var names = k.names.length > 1 ? 'one of ' + k.names.join(', ') : k.names[0];
|
|
119
|
+
return '<li>' + esc(names) + (k.set ? ' <span class="hint">' + CHECK + ' already set</span>' : '') + '</li>';
|
|
120
|
+
})) + '</div>' +
|
|
121
|
+
(settings.length ? '<div><strong>Settings filled in:</strong> ' + settings.map(function (s) { return '<code>' + esc(s) + '=' + esc(p.settings[s]) + '</code>'; }).join(', ') + '</div>' : '') +
|
|
122
|
+
(p.schedule ? '<div><strong>Schedule:</strong> ' + esc(p.schedule) + '</div>' : '') +
|
|
123
|
+
(p.needsApprovals ? '<div><strong>Approvals:</strong> requests reach you wherever its Messaging tab sends them (Slack, email, Lark or a webhook).</div>' : '') +
|
|
124
|
+
(p.notes.length ? '<div><strong>You also</strong>' + listHtml(p.notes.map(function (n) { return '<li>' + esc(n) + '</li>'; })) + '</div>' : '') +
|
|
125
|
+
'<div class="row"><button type="button" class="primary tp-create">Create it</button><button type="button" class="tp-cancel">Cancel</button></div>' +
|
|
126
|
+
'<div class="hint tp-status"></div></div>';
|
|
127
|
+
slot.querySelector('.tp-cancel').addEventListener('click', function () { slot.innerHTML = ''; });
|
|
128
|
+
slot.querySelector('.tp-create').addEventListener('click', function (ev) {
|
|
129
|
+
var btn = ev.currentTarget;
|
|
130
|
+
btn.disabled = true;
|
|
131
|
+
slot.querySelector('.tp-status').textContent = 'Creating it and installing its abilities' + ELLIPSIS + ' (this can take a minute)';
|
|
132
|
+
api('POST', '/starter-templates/' + encodeURIComponent(id) + '/create').then(function (r2) {
|
|
133
|
+
if (!r2.ok) { btn.disabled = false; slot.querySelector('.tp-status').textContent = r2.body.error || 'Could not create it'; return; }
|
|
134
|
+
var c = r2.body;
|
|
135
|
+
var installed = c.abilities.installed.map(function (a) { return '<code>' + esc(a) + '</code>'; }).join(' ');
|
|
136
|
+
slot.innerHTML = '<div class="tp-preview">' +
|
|
137
|
+
'<div><strong>Created <code>' + esc(c.name) + '</code>.</strong> Anything its permissions don' + String.fromCharCode(8217) + 't cover asks you first.</div>' +
|
|
138
|
+
(installed ? '<div>' + CHECK + ' Installed ' + installed + '</div>' : '') +
|
|
139
|
+
(c.schedule ? '<div>' + CHECK + ' Schedule created, paused: ' + esc(c.schedule.description) + (c.schedule.next_run_at ? ' (first run ' + esc(new Date(c.schedule.next_run_at).toLocaleString()) + ' once turned on)' : '') + '</div>' : '') +
|
|
140
|
+
(c.loadError ? '<div class="error">It was written but couldn' + String.fromCharCode(8217) + 't be loaded: ' + esc(c.loadError) + '</div>' : '') +
|
|
141
|
+
(c.scheduleError ? '<div class="error">Its schedule wasn' + String.fromCharCode(8217) + 't created: ' + esc(c.scheduleError) + '. Add one in its Schedules tab.</div>' : '') +
|
|
142
|
+
'<div><strong>Finish setup:</strong>' + checklistHtml(c.checklist) + '</div>' +
|
|
143
|
+
(c.exampleMessage ? '<div><strong>Try it</strong> in Chat with: <em>' + esc(c.exampleMessage) + '</em></div>' : '') +
|
|
144
|
+
'<div class="row"><a class="tp-btn" href="/agents/config?agent=' + encodeURIComponent(c.name) + '">Open ' + esc(c.name) + '</a>' +
|
|
145
|
+
'<a class="tp-btn plain" href="' + esc(leShell.tabHref(c.name, 'chat')) + '">Try it in Chat</a></div></div>';
|
|
146
|
+
}).catch(function () { btn.disabled = false; slot.querySelector('.tp-status').textContent = 'Could not reach the server; try again.'; });
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
show();
|
|
152
|
+
})();
|
|
153
|
+
</script>
|
|
154
|
+
</body>
|
|
155
|
+
</html>`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loopengine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.93",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Runtime for tool-using AI agents with a transparent ReAct loop.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
"dist/core/trusted-proxy.d.ts",
|
|
47
47
|
"dist/core/usage.js",
|
|
48
48
|
"dist/core/usage.d.ts",
|
|
49
|
+
"dist/core/email-senders.js",
|
|
50
|
+
"dist/core/email-senders.d.ts",
|
|
49
51
|
"dist/core/notifier-file.js",
|
|
50
52
|
"dist/core/notifier-file.d.ts",
|
|
51
53
|
"dist/core/notifier-deliveries.js",
|