loopengine 0.3.6 → 0.3.7

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.
@@ -49,6 +49,7 @@ export const agentsConfigPageHtml = `<!doctype html>
49
49
  form.add-source .schedule-when input[type="number"] { width: 80px; }
50
50
  form.add-source .schedule-when select, form.add-source .schedule-when input[type="datetime-local"] { width: auto; }
51
51
  .workflow-card { margin-bottom: 10px; }
52
+ tr.schedule-focus td { background: var(--le-accent-soft, #e6f4ef); transition: background 0.3s; }
52
53
  .workflow-starts { margin: 10px 0 0; padding: 10px 12px; border: 1px solid var(--le-line, #e5e7eb); border-radius: 8px; background: var(--le-surface, #f6f7f9); }
53
54
  .workflow-starts h4 { margin: 0; font-size: 13.5px; font-weight: 600; }
54
55
  .wf-start { padding: 10px 0; border-top: 1px solid var(--le-line, #e5e7eb); display: grid; gap: 6px; font-size: 13px; }
@@ -709,7 +710,7 @@ ${consoleShellScript}
709
710
  return text.charAt(0).toUpperCase() + text.slice(1);
710
711
  }
711
712
 
712
- // How it starts: four ways, each with a ? that says what it is. A
713
+ // Ways to run it: four, each with a ? that says what it is. A
713
714
  // schedule (pause, resume, remove, add), a webhook, Run now with the
714
715
  // values a run is given, and the API call an app makes.
715
716
  function wfStartSection(key, title, help, body) {
@@ -722,14 +723,15 @@ ${consoleShellScript}
722
723
  function workflowStartsHtml(w, agentName, paramsHtml) {
723
724
  var schedules = (w.schedules || []).map(function (x) {
724
725
  var state = x.waiting ? '<span class="le-pill ask">Waiting for a decision</span>'
725
- : x.status === 'paused' ? '<span class="le-pill">Paused</span>'
726
+ : x.status === 'paused' ? '<span class="le-pill warn">Paused</span>'
726
727
  : x.status === 'running' ? '<span class="le-pill accent">Running</span>'
727
728
  : (x.nextRunAt ? '<span class="hint">next ' + escapeHtml(new Date(x.nextRunAt).toLocaleString()) + '</span>' : '');
728
729
  var values = x.params ? Object.keys(x.params).map(function (k) { return escapeHtml(k) + ': ' + escapeHtml(x.params[k]); }).join(' ' + String.fromCharCode(183) + ' ') : '';
729
730
  return '<li style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">' +
730
- '<span>' + escapeHtml(x.describe.charAt(0).toUpperCase() + x.describe.slice(1)) + (x.name ? ' <span class="hint">(' + escapeHtml(x.name) + ')</span>' : '') + '</span>' + state +
731
+ '<span><a href="?agent=' + encodeURIComponent(agentName) + '&amp;tab=schedules#schedule-' + escapeHtml(x.taskId) + '" class="workflow-schedule-link" data-task="' + escapeHtml(x.taskId) + '" title="See it in the Schedules tab">' +
732
+ escapeHtml(x.describe.charAt(0).toUpperCase() + x.describe.slice(1)) + '</a></span>' + state +
731
733
  (values ? '<span class="hint">' + values + '</span>' : '') +
732
- (x.notify ? '<span class="hint">sends you each result</span>' : '') +
734
+ (x.notify ? '<span class="hint">results go to your messaging app</span>' : '') +
733
735
  '<span style="display:flex;gap:6px;margin-left:auto">' +
734
736
  (x.status === 'paused' ? '<button type="button" class="workflow-schedule-action" data-id="' + escapeHtml(x.taskId) + '" data-action="resume">Resume</button>'
735
737
  : x.status === 'scheduled' ? '<button type="button" class="workflow-schedule-action" data-id="' + escapeHtml(x.taskId) + '" data-action="pause">Pause</button>' : '') +
@@ -737,11 +739,11 @@ ${consoleShellScript}
737
739
  '</span></li>';
738
740
  }).join('');
739
741
  var paramNames = Object.keys(w.parameters || {});
740
- var example = paramNames.length
741
- ? '{ "params": { ' + paramNames.map(function (p) { return '"' + p + '": "' + String.fromCharCode(8230) + '"'; }).join(', ') + ' } }'
742
- : '{}';
742
+ // A body that shows what a call can carry: its values, and waiting
743
+ // for the result.
744
+ var example = '{ ' + (paramNames.length ? '"params": { ' + paramNames.map(function (p) { return '"' + p + '": "' + String.fromCharCode(8230) + '"'; }).join(', ') + ' }, ' : '') + '"wait": true }';
743
745
  var apiUrl = location.origin + '/agents/' + encodeURIComponent(agentName) + '/workflows/' + encodeURIComponent(w.id) + '/run';
744
- return '<div class="workflow-starts"><h4>How it starts</h4>' +
746
+ return '<div class="workflow-starts"><h4>Ways to run it</h4>' +
745
747
  wfStartSection('schedule-' + w.id, 'Schedule',
746
748
  'Runs it on a timer, like every day at 9:00, with the values you give the schedule. Nobody needs to be there: approvals and questions go to the agent' + String.fromCharCode(8217) + 's messaging app.',
747
749
  (schedules ? '<ul class="schedule-history">' + schedules + '</ul>' : '<span class="hint">None yet.</span>') +
@@ -756,7 +758,9 @@ ${consoleShellScript}
756
758
  '<div><button type="button" class="workflow-run primary">Run now</button></div>') +
757
759
  wfStartSection('api-' + w.id, 'API',
758
760
  'Your own app starts a run by calling this address with an API key. Add "wait": true to get the result in the answer, or "callback_url" to have it posted to you when the run ends.',
759
- '<pre>POST ' + escapeHtml(apiUrl) + String.fromCharCode(10) + escapeHtml(example) + '</pre>') +
761
+ '<pre>POST ' + escapeHtml(apiUrl) + String.fromCharCode(10) + escapeHtml(example) + '</pre>' +
762
+ '<p class="hint" style="margin:0">' + (paramNames.length ? '<code>params</code>: ' + paramNames.map(function (p) { return '<code>' + escapeHtml(p) + '</code>' + (w.parameters[p].required ? ' (required)' : '') + (w.parameters[p].description ? ', ' + escapeHtml(w.parameters[p].description) : ''); }).join('; ') + '. ' : 'It takes no params. ') +
763
+ 'Optional: <code>"wait": true</code> answers with the result; <code>"notify": true</code> sends it through the agent' + String.fromCharCode(8217) + 's messaging app; <code>"callback_url"</code> posts it to your app when the run ends. Leave them out to get the run' + String.fromCharCode(8217) + 's id back at once.</p>') +
760
764
  '</div>';
761
765
  }
762
766
 
@@ -771,7 +775,7 @@ ${consoleShellScript}
771
775
  : s.waiting
772
776
  ? '<span class="le-pill ask">Waiting for a decision</span>'
773
777
  : s.status === 'paused'
774
- ? '<span class="le-pill">Schedule paused</span>'
778
+ ? '<span class="le-pill warn">Schedule paused</span>'
775
779
  : s.status === 'running'
776
780
  ? '<span class="le-pill accent">Running</span>'
777
781
  : '<span class="le-pill good">On</span>' + (s.nextRunAt ? ' <span class="hint">next ' + escapeHtml(new Date(s.nextRunAt).toLocaleString()) + '</span>' : '');
@@ -833,6 +837,24 @@ ${consoleShellScript}
833
837
  var workflowsRefreshTimer = null;
834
838
  // Set by a workflow's "Add a schedule", for the Schedules tab's form.
835
839
  var pendingScheduleWorkflow = null;
840
+ // Set by a schedule on a workflow card (or a #schedule-<id> link): the
841
+ // Schedules tab's row to show.
842
+ var pendingScheduleFocus = /^#schedule-/.test(location.hash) ? location.hash.slice('#schedule-'.length) : null;
843
+
844
+ // Scrolls to that row and marks it for a moment, once the tab has drawn
845
+ // it (it may still be loading).
846
+ function focusScheduleRow(tries) {
847
+ if (!pendingScheduleFocus) return;
848
+ var row = detail.querySelector('#schedule-' + CSS.escape(pendingScheduleFocus));
849
+ if (!row) {
850
+ if ((tries || 0) < 40) setTimeout(function () { focusScheduleRow((tries || 0) + 1); }, 150);
851
+ return;
852
+ }
853
+ pendingScheduleFocus = null;
854
+ row.scrollIntoView({ block: 'center' });
855
+ row.classList.add('schedule-focus');
856
+ setTimeout(function () { row.classList.remove('schedule-focus'); }, 2500);
857
+ }
836
858
  var workflowsById = {};
837
859
  // Set while a workflow's editor is open, so the running-run refresh
838
860
  // doesn't redraw the card under someone typing.
@@ -1134,6 +1156,17 @@ ${consoleShellScript}
1134
1156
  var result = card.querySelector('.workflow-result');
1135
1157
  var base = '/agents/' + encodeURIComponent(name) + '/workflows/' + encodeURIComponent(id);
1136
1158
  card.querySelector('.workflow-edit').addEventListener('click', function () { openWorkflowEditor(name, card); });
1159
+ // A schedule opens its own row in the Schedules tab.
1160
+ var scheduleLinks = card.querySelectorAll('.workflow-schedule-link');
1161
+ for (var sl = 0; sl < scheduleLinks.length; sl++) {
1162
+ scheduleLinks[sl].addEventListener('click', function (ev) {
1163
+ if (ev.metaKey || ev.ctrlKey || ev.shiftKey) return;
1164
+ ev.preventDefault();
1165
+ pendingScheduleFocus = ev.currentTarget.dataset.task;
1166
+ switchTab('schedules');
1167
+ focusScheduleRow();
1168
+ });
1169
+ }
1137
1170
  // The ? beside each way it starts shows or hides what it is.
1138
1171
  var helps = card.querySelectorAll('.help-q');
1139
1172
  for (var hq = 0; hq < helps.length; hq++) {
@@ -2345,7 +2378,7 @@ ${consoleShellScript}
2345
2378
  return '<li><span class="le-pill ' + (h.status === 'ok' ? 'good' : 'bad') + '">' + escapeHtml(h.status) + '</span> <span class="hint">' + escapeHtml(fmtScheduleTime(h.ran_at)) + '</span>' +
2346
2379
  '<div class="schedule-result">' + escapeHtml(h.status === 'ok' ? (h.result || '') : (h.error || '')) + '</div></li>';
2347
2380
  }).join('');
2348
- return '<tr>' +
2381
+ return '<tr id="schedule-' + escapeHtml(t.task_id) + '">' +
2349
2382
  '<td style="min-width:180px"><strong>' + escapeHtml(t.task_name || 'Untitled') + '</strong>' +
2350
2383
  (t.workflow
2351
2384
  ? '<div><span class="le-pill accent">Workflow</span> ' + escapeHtml(t.workflow.title) + '</div><div class="schedule-result hint">' + escapeHtml(t.workflow.start_message) +
@@ -2381,7 +2414,7 @@ ${consoleShellScript}
2381
2414
  return '<option value="' + escapeHtml(w.id) + '">' + escapeHtml(w.title) + '</option>';
2382
2415
  }).join('') + '</select></div>' +
2383
2416
  '<div class="workflow-params" id="scheduleParams" style="margin:0 0 4px 22px"></div>' +
2384
- '<p class="hint" style="margin:0 0 4px 22px">Each run starts with the workflow&#39;s start message as it is then, so changing the workflow changes what runs.</p>' +
2417
+ '<p class="hint" style="margin:0 0 4px 22px">Each run follows the workflow&#39;s instructions as they are then, so changing the workflow changes what runs.</p>' +
2385
2418
  '<div class="schedule-when"><input type="radio" name="runs" value="message" id="runsMessage"><label for="runsMessage">A message</label></div>' +
2386
2419
  '</div>'
2387
2420
  : '') +
@@ -2480,6 +2513,7 @@ ${consoleShellScript}
2480
2513
  form.scrollIntoView({ block: 'start' });
2481
2514
  }
2482
2515
  pendingScheduleWorkflow = null;
2516
+ focusScheduleRow();
2483
2517
  renderScheduleParams();
2484
2518
  if (workflowSelect) workflowSelect.addEventListener('change', renderScheduleParams);
2485
2519
  for (var k = 0; k < runsRadios.length; k++) {
@@ -4413,7 +4447,7 @@ ${consoleShellScript}
4413
4447
  '<div class="tab-panel" data-tab-panel="overview">' + renderOverviewHtml(cfg) + '</div>' +
4414
4448
  // Workflows: the jobs this agent does, each with what starts it.
4415
4449
  '<div class="tab-panel" data-tab-panel="workflows">' +
4416
- '<p class="muted" style="margin:0 0 20px">The jobs this agent does on its own: what it does, what starts it (schedules, webhooks, Run now or an app&#39;s API call) and how its runs went. In Chat it can do them too, when you ask.</p>' +
4450
+ '<p class="muted" style="margin:0 0 20px">The jobs this agent does on its own: what it does, the ways to run it (a schedule, a webhook, Run now or an app&#39;s API call) and how its runs went. In Chat it can do them too, when you ask.</p>' +
4417
4451
  '<section id="workflowsSection"><div id="workflowsContent"><p class="hint">Loading&hellip;</p></div></section>' +
4418
4452
  '</div>' +
4419
4453
  // Advanced: the agent's own tools (and Composio, subagents), then its
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopengine",
3
- "version": "0.3.6",
3
+ "version": "0.3.7",
4
4
  "type": "module",
5
5
  "description": "Runtime for tool-using AI agents with a transparent ReAct loop.",
6
6
  "license": "MIT",