aimeat 1.18.0 → 1.19.0

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.
Files changed (68) hide show
  1. package/README.md +22 -1
  2. package/dist/locales/en.json +28 -0
  3. package/dist/locales/fi.json +28 -0
  4. package/dist/public/css/views/agents-detail.css +145 -0
  5. package/dist/public/css/views/scheduler.css +16 -0
  6. package/dist/public/js/services/admin.js +3 -0
  7. package/dist/public/llms-template.txt +170 -2
  8. package/dist/public/oauth-consent.html +7 -1
  9. package/dist/public/views/admin/portal-tab.js +94 -9
  10. package/dist/public/views/profile/agents/agent-card.js +24 -4
  11. package/dist/public/views/profile/agents/tab-schedules.js +3 -32
  12. package/dist/public/views/profile/agents/tab-tasks.js +5 -2
  13. package/dist/public/views/profile/agents-tab.js +86 -3
  14. package/dist/public/views/profile/agents-tasks-subtab.js +71 -7
  15. package/dist/public/views/profile/schedule-item.js +177 -0
  16. package/dist/public/views/profile/scheduler-tab.js +5 -51
  17. package/dist/src/generated/api-types.d.ts +277 -2
  18. package/dist/src/generated/api-types.d.ts.map +1 -1
  19. package/dist/src/mcp/catalog/scopes.js +1 -1
  20. package/dist/src/mcp/catalog/scopes.js.map +1 -1
  21. package/dist/src/mcp/index.d.ts.map +1 -1
  22. package/dist/src/mcp/index.js +12 -0
  23. package/dist/src/mcp/index.js.map +1 -1
  24. package/dist/src/routes/bootstrap.d.ts +2 -1
  25. package/dist/src/routes/bootstrap.d.ts.map +1 -1
  26. package/dist/src/routes/bootstrap.js +14 -3
  27. package/dist/src/routes/bootstrap.js.map +1 -1
  28. package/dist/src/routes/cortex.d.ts +13 -0
  29. package/dist/src/routes/cortex.d.ts.map +1 -1
  30. package/dist/src/routes/cortex.js +211 -0
  31. package/dist/src/routes/cortex.js.map +1 -1
  32. package/dist/src/routes/extensions.d.ts +14 -0
  33. package/dist/src/routes/extensions.d.ts.map +1 -1
  34. package/dist/src/routes/extensions.js +255 -109
  35. package/dist/src/routes/extensions.js.map +1 -1
  36. package/dist/src/routes/lib-header.d.ts +22 -0
  37. package/dist/src/routes/lib-header.d.ts.map +1 -0
  38. package/dist/src/routes/lib-header.js +244 -0
  39. package/dist/src/routes/lib-header.js.map +1 -0
  40. package/dist/src/routes/libs.d.ts.map +1 -1
  41. package/dist/src/routes/libs.js +13 -0
  42. package/dist/src/routes/libs.js.map +1 -1
  43. package/dist/src/routes/schedules.d.ts.map +1 -1
  44. package/dist/src/routes/schedules.js +19 -5
  45. package/dist/src/routes/schedules.js.map +1 -1
  46. package/dist/src/routes/site.d.ts.map +1 -1
  47. package/dist/src/routes/site.js +39 -1
  48. package/dist/src/routes/site.js.map +1 -1
  49. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  50. package/dist/src/server-bootstrap/routes-loader.js +5 -2
  51. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  52. package/dist/src/services/prompt-defaults.d.ts.map +1 -1
  53. package/dist/src/services/prompt-defaults.js +35 -0
  54. package/dist/src/services/prompt-defaults.js.map +1 -1
  55. package/dist/src/services/prompt-seeder.d.ts.map +1 -1
  56. package/dist/src/services/prompt-seeder.js +6 -2
  57. package/dist/src/services/prompt-seeder.js.map +1 -1
  58. package/dist/src/services/site.d.ts +4 -0
  59. package/dist/src/services/site.d.ts.map +1 -1
  60. package/dist/src/services/site.js +29 -0
  61. package/dist/src/services/site.js.map +1 -1
  62. package/dist/src/storage/interface.d.ts +1 -1
  63. package/dist/src/storage/interface.d.ts.map +1 -1
  64. package/dist/src/utils/csp-nonce.d.ts +19 -0
  65. package/dist/src/utils/csp-nonce.d.ts.map +1 -0
  66. package/dist/src/utils/csp-nonce.js +25 -0
  67. package/dist/src/utils/csp-nonce.js.map +1 -0
  68. package/package.json +1 -1
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @file schedule-item.js
3
+ * @description Shared, editable card for one managed schedule — used by both the
4
+ * Profile › Scheduler master view and the per-agent Schedules sub-tab. Shows the
5
+ * schedule meta (kind, cron, timezone, agent, last/next run, runs), the full
6
+ * "what it dispatches each run" content on its own row (agent_task title+desc,
7
+ * ai prompt+keys, or extension action), and an inline editor (cron, name,
8
+ * timezone, purpose + the kind-specific payload). Handles pause/resume, run-now,
9
+ * edit (PATCH), and cancel itself; calls onChanged() to let the parent refetch.
10
+ * @version-history
11
+ * v1.0.0 -- 2026-06-03 -- Initial editable schedule card
12
+ */
13
+ import { h } from 'preact';
14
+ import { useState } from 'preact/hooks';
15
+ import htm from 'htm';
16
+ import { t } from '/js/i18n.js';
17
+ import { timeAgo } from '/js/utils.js';
18
+ import { setScheduleEnabled, triggerSchedule, deleteSchedule, updateSchedule } from '/js/services/schedules.js';
19
+
20
+ const html = htm.bind(h);
21
+
22
+ const KIND_BADGE = {
23
+ ai: 'sch-badge--ai', agent_task: 'sch-badge--agent', extension: 'sch-badge--ext', core: 'sch-badge--core',
24
+ };
25
+ const KIND_LABEL = {
26
+ ai: 'profile.scheduler.kind.ai', agent_task: 'profile.scheduler.kind.agent_task',
27
+ extension: 'profile.scheduler.kind.extension', core: 'profile.scheduler.kind.core',
28
+ };
29
+
30
+ /** Human "time until" for a future ISO timestamp (e.g. "20h 48min", "45min 30s"). */
31
+ export function formatUntil(iso) {
32
+ if (!iso) return '—';
33
+ const ms = new Date(iso).getTime() - Date.now();
34
+ if (Number.isNaN(ms)) return '—';
35
+ if (ms <= 0) return t('profile.scheduler.soon');
36
+ const s = Math.floor(ms / 1000);
37
+ const d = Math.floor(s / 86400);
38
+ const h = Math.floor((s % 86400) / 3600);
39
+ const m = Math.floor((s % 3600) / 60);
40
+ const sec = s % 60;
41
+ const parts = [];
42
+ if (d) parts.push(d + 'd');
43
+ if (h) parts.push(h + 'h');
44
+ if (m) parts.push(m + 'min');
45
+ if (!d && !h) parts.push(sec + 's'); // seconds only when under an hour
46
+ return parts.join(' ') || '0s';
47
+ }
48
+
49
+ /** What a schedule produces each fire (title + body) for display. */
50
+ function describeDispatch(s) {
51
+ if (s.type === 'agent_task') {
52
+ const tmpl = (s.input && s.input.taskTemplate) || {};
53
+ return { title: tmpl.title || '', body: tmpl.description || '' };
54
+ }
55
+ if (s.type === 'ai') {
56
+ const c = s.input || {};
57
+ const io = `${(c.inputKeys || []).join(', ') || '—'} → ${c.outputKey || t('profile.scheduler.autoKey')}`;
58
+ return { title: c.prompt || '', body: io };
59
+ }
60
+ if (s.type === 'extension') {
61
+ return { title: `${s.extensionName || ''}${s.actionId ? ' / ' + s.actionId : ''}`, body: '' };
62
+ }
63
+ return { title: '', body: '' };
64
+ }
65
+
66
+ export default function ScheduleItem({ schedule: s, onChanged, showToast }) {
67
+ const [editing, setEditing] = useState(false);
68
+ const [busy, setBusy] = useState(false);
69
+ const [f, setF] = useState(() => {
70
+ const c = s.input || {};
71
+ const tmpl = c.taskTemplate || {};
72
+ return {
73
+ display_name: s.displayName || '', cron: s.cron || '', timezone: s.timezone || '', purpose: s.purpose || '',
74
+ task_title: tmpl.title || '', task_description: tmpl.description || '',
75
+ prompt: c.prompt || '', input_keys: (c.inputKeys || []).join(', '), output_key: c.outputKey || '',
76
+ };
77
+ });
78
+ const set = (k, v) => setF(prev => ({ ...prev, [k]: v }));
79
+
80
+ const run = async (fn) => {
81
+ setBusy(true);
82
+ try { await fn(); onChanged?.(); } catch (e) { showToast?.(e.message, true); } finally { setBusy(false); }
83
+ };
84
+ const onToggle = () => run(() => setScheduleEnabled(s.id, !s.enabled));
85
+ const onTrigger = () => run(async () => { await triggerSchedule(s.id); showToast?.(t('profile.scheduler.triggered')); });
86
+ const onCancel = () => { if (window.confirm(t('profile.scheduler.confirmCancel'))) run(() => deleteSchedule(s.id)); };
87
+
88
+ const onSave = async () => {
89
+ const patch = {
90
+ display_name: f.display_name.trim(), cron: f.cron.trim(),
91
+ timezone: f.timezone.trim() || undefined, purpose: f.purpose.trim() || undefined,
92
+ };
93
+ if (s.type === 'agent_task') {
94
+ patch.input = { ...(s.input || {}), taskTemplate: { title: f.task_title.trim(), description: f.task_description.trim() } };
95
+ } else if (s.type === 'ai') {
96
+ patch.input = {
97
+ ...(s.input || {}),
98
+ prompt: f.prompt.trim(),
99
+ inputKeys: f.input_keys.split(',').map(x => x.trim()).filter(Boolean),
100
+ outputKey: f.output_key.trim() || undefined,
101
+ };
102
+ }
103
+ setBusy(true);
104
+ try { await updateSchedule(s.id, patch); showToast?.(t('profile.scheduler.saved')); setEditing(false); onChanged?.(); }
105
+ catch (e) { showToast?.(e.message, true); }
106
+ finally { setBusy(false); }
107
+ };
108
+
109
+ const d = describeDispatch(s);
110
+
111
+ return html`
112
+ <div class="sch-card">
113
+ <div class="sch-card-head">
114
+ <div class="sch-card-title">
115
+ <span class="sch-badge ${KIND_BADGE[s.type] || KIND_BADGE.core}">${t(KIND_LABEL[s.type] || KIND_LABEL.core)}</span>
116
+ <span class="sch-name">${s.displayName || s.name}</span>
117
+ ${s.createdByAgent ? html`<span class="sch-tag">${t('profile.scheduler.byAgent')}</span>` : null}
118
+ </div>
119
+ <div class="sch-actions">
120
+ <button class="btn-ghost sch-btn" disabled=${busy} onClick=${onToggle}>${s.enabled ? t('profile.scheduler.pause') : t('profile.scheduler.resume')}</button>
121
+ <button class="btn-ghost sch-btn" disabled=${busy} onClick=${onTrigger}>${t('profile.scheduler.runNow')}</button>
122
+ <button class="btn-ghost sch-btn" disabled=${busy} onClick=${() => setEditing(e => !e)}>${editing ? t('profile.scheduler.close') : t('profile.scheduler.edit')}</button>
123
+ <button class="btn-danger sch-btn" disabled=${busy} onClick=${onCancel}>${t('profile.scheduler.cancel')}</button>
124
+ </div>
125
+ </div>
126
+
127
+ <div class="sch-card-meta">
128
+ <span><span class="sch-meta-k">${t('profile.scheduler.col.cron')}:</span> <code class="sch-cron">${s.cron}</code>${s.timezone ? ' · ' + s.timezone : ''}</span>
129
+ ${s.agentName ? html`<span><span class="sch-meta-k">${t('profile.scheduler.col.agent')}:</span> ${s.agentName}</span>` : null}
130
+ <span><span class="sch-meta-k">${t('profile.scheduler.col.lastRun')}:</span> ${s.lastRunAt ? timeAgo(s.lastRunAt) : t('profile.scheduler.never')}${s.lastRunResult ? html` <span class="sch-badge ${s.lastRunResult === 'error' ? 'sch-badge--err' : 'sch-badge--ok'}">${s.lastRunResult}</span>` : ''}</span>
131
+ <span><span class="sch-meta-k">${t('profile.scheduler.col.nextRun')}:</span> ${s.enabled ? formatUntil(s.nextRunAt) : t('profile.scheduler.paused')}</span>
132
+ <span><span class="sch-meta-k">${t('profile.scheduler.col.runs')}:</span> ${s.runCount ?? 0}</span>
133
+ </div>
134
+
135
+ ${(d.title || d.body) && html`
136
+ <div class="sch-card-dispatch">
137
+ <div class="sch-dispatch-label">${t('profile.scheduler.dispatches')}</div>
138
+ ${d.title ? html`<div class="sch-dispatch-title">${d.title}</div>` : null}
139
+ ${d.body ? html`<div class="sch-dispatch-body">${d.body}</div>` : null}
140
+ </div>`}
141
+
142
+ ${s.purpose && !editing ? html`<div class="sch-muted sch-purpose">${s.purpose}</div>` : null}
143
+
144
+ ${editing && html`
145
+ <div class="sch-edit">
146
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.displayName')}</label>
147
+ <input type="text" value=${f.display_name} onInput=${e => set('display_name', e.target.value)} /></div>
148
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.schedule')} (cron)</label>
149
+ <input type="text" class="sch-cron-input" value=${f.cron} onInput=${e => set('cron', e.target.value)} placeholder="0 7 * * *" /></div>
150
+ <div class="sch-form-row"><label>${t('profile.scheduler.ph.timezone')}</label>
151
+ <input type="text" value=${f.timezone} onInput=${e => set('timezone', e.target.value)} placeholder="Europe/Helsinki" /></div>
152
+
153
+ ${s.type === 'agent_task' && html`
154
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.taskTitle')}</label>
155
+ <input type="text" value=${f.task_title} onInput=${e => set('task_title', e.target.value)} /></div>
156
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.taskDescription')}</label>
157
+ <textarea rows="3" value=${f.task_description} onInput=${e => set('task_description', e.target.value)}></textarea></div>
158
+ `}
159
+ ${s.type === 'ai' && html`
160
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.inputKeys')}</label>
161
+ <input type="text" value=${f.input_keys} onInput=${e => set('input_keys', e.target.value)} placeholder=${t('profile.scheduler.ph.inputKeys')} /></div>
162
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.prompt')}</label>
163
+ <textarea rows="3" value=${f.prompt} onInput=${e => set('prompt', e.target.value)}></textarea></div>
164
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.outputKey')}</label>
165
+ <input type="text" value=${f.output_key} onInput=${e => set('output_key', e.target.value)} placeholder=${t('profile.scheduler.ph.outputKey')} /></div>
166
+ `}
167
+
168
+ <div class="sch-form-row"><label>${t('profile.scheduler.field.purpose')}</label>
169
+ <input type="text" value=${f.purpose} onInput=${e => set('purpose', e.target.value)} /></div>
170
+
171
+ <div class="sch-form-actions">
172
+ <button class="btn-primary btn-sm" disabled=${busy} onClick=${onSave}>${busy ? t('profile.scheduler.saving') : t('profile.scheduler.save')}</button>
173
+ <button class="btn-outline btn-sm" disabled=${busy} onClick=${() => setEditing(false)}>${t('profile.scheduler.close')}</button>
174
+ </div>
175
+ </div>`}
176
+ </div>`;
177
+ }
@@ -10,6 +10,7 @@
10
10
  * @usage Registered in profile.js TABS as { id:'scheduler', component: SchedulerTab }.
11
11
  * @version-history
12
12
  * v1.0.0 -- 2026-06-03 -- Initial master scheduler view
13
+ * v1.0.1 -- 2026-06-03 -- Extension cron "Next run" uses formatUntil (was timeAgo → negative "ago")
13
14
  */
14
15
  import { h } from 'preact';
15
16
  import { useState, useEffect, useRef, useCallback } from 'preact/hooks';
@@ -17,10 +18,8 @@ import htm from 'htm';
17
18
  import { t } from '/js/i18n.js';
18
19
  import { timeAgo } from '/js/utils.js';
19
20
  import { listAgents } from '/js/services/agents.js';
20
- import {
21
- listAllSchedules, createSchedule, setScheduleEnabled,
22
- triggerSchedule, deleteSchedule,
23
- } from '/js/services/schedules.js';
21
+ import { listAllSchedules, createSchedule } from '/js/services/schedules.js';
22
+ import ScheduleItem, { formatUntil } from './schedule-item.js';
24
23
 
25
24
  const html = htm.bind(h);
26
25
 
@@ -79,42 +78,6 @@ export default function SchedulerTab({ showToast }) {
79
78
  return () => { window.removeEventListener('aimeat-live-update', handler); clearInterval(poller); };
80
79
  }, [loadData]);
81
80
 
82
- const onToggle = async (job) => {
83
- try { await setScheduleEnabled(job.id, !job.enabled); await loadData(); }
84
- catch (e) { showToast?.(e.message, true); }
85
- };
86
- const onTrigger = async (job) => {
87
- try { await triggerSchedule(job.id); showToast?.(t('profile.scheduler.triggered')); await loadData(); }
88
- catch (e) { showToast?.(e.message, true); }
89
- };
90
- const onCancel = async (job) => {
91
- if (!window.confirm(t('profile.scheduler.confirmCancel'))) return;
92
- try { await deleteSchedule(job.id); showToast?.(t('profile.scheduler.cancelled')); await loadData(); }
93
- catch (e) { showToast?.(e.message, true); }
94
- };
95
-
96
- const renderManagedRow = (job) => html`
97
- <tr key=${job.id}>
98
- <td>
99
- <div class="sch-name">${job.displayName || job.name}</div>
100
- ${job.purpose && html`<div class="sch-muted sch-purpose">${job.purpose}</div>`}
101
- ${job.createdByAgent && html`<span class="sch-tag">${t('profile.scheduler.byAgent')}</span>`}
102
- </td>
103
- <td>${kindBadge(job.type)}</td>
104
- <td><code class="sch-cron">${job.cron}</code>${job.timezone && html`<div class="sch-muted">${job.timezone}</div>`}</td>
105
- <td>${job.agentName || html`<span class="sch-muted">—</span>`}</td>
106
- <td>${job.lastRunAt ? timeAgo(job.lastRunAt) : html`<span class="sch-muted">${t('profile.scheduler.never')}</span>`} ${resultBadge(job.lastRunResult)}</td>
107
- <td>${job.nextRunAt ? timeAgo(job.nextRunAt) : html`<span class="sch-muted">—</span>`}</td>
108
- <td>${job.runCount ?? 0}</td>
109
- <td class="sch-actions">
110
- <button class="btn-ghost sch-btn" onClick=${() => onToggle(job)} title=${job.enabled ? t('profile.scheduler.pause') : t('profile.scheduler.resume')}>
111
- ${job.enabled ? t('profile.scheduler.pause') : t('profile.scheduler.resume')}
112
- </button>
113
- <button class="btn-ghost sch-btn" onClick=${() => onTrigger(job)}>${t('profile.scheduler.runNow')}</button>
114
- <button class="btn-danger sch-btn" onClick=${() => onCancel(job)}>${t('profile.scheduler.cancel')}</button>
115
- </td>
116
- </tr>`;
117
-
118
81
  if (loading) return html`<div class="sch-loading">${t('profile.scheduler.loading')}</div>`;
119
82
 
120
83
  return html`
@@ -139,16 +102,7 @@ export default function SchedulerTab({ showToast }) {
139
102
  <h3 class="sch-section-title">${t('profile.scheduler.managedTitle')}</h3>
140
103
  ${data.managed.length === 0
141
104
  ? html`<div class="sch-empty">${t('profile.scheduler.noManaged')}</div>`
142
- : html`<table class="sch-table"><thead><tr>
143
- <th>${t('profile.scheduler.col.name')}</th>
144
- <th>${t('profile.scheduler.col.kind')}</th>
145
- <th>${t('profile.scheduler.col.cron')}</th>
146
- <th>${t('profile.scheduler.col.agent')}</th>
147
- <th>${t('profile.scheduler.col.lastRun')}</th>
148
- <th>${t('profile.scheduler.col.nextRun')}</th>
149
- <th>${t('profile.scheduler.col.runs')}</th>
150
- <th></th>
151
- </tr></thead><tbody>${data.managed.map(renderManagedRow)}</tbody></table>`}
105
+ : html`<div class="sch-card-list">${data.managed.map(j => html`<${ScheduleItem} key=${j.id} schedule=${j} onChanged=${loadData} showToast=${showToast} />`)}</div>`}
152
106
  </div>
153
107
 
154
108
  <!-- Owner extension cron -->
@@ -166,7 +120,7 @@ export default function SchedulerTab({ showToast }) {
166
120
  <td><code class="sch-cron">${j.extensionName}${j.actionId ? '/' + j.actionId : ''}</code></td>
167
121
  <td><code class="sch-cron">${j.cron}</code></td>
168
122
  <td>${j.lastRunAt ? timeAgo(j.lastRunAt) : '—'} ${resultBadge(j.lastRunResult)}</td>
169
- <td>${j.nextRunAt ? timeAgo(j.nextRunAt) : '—'}</td>
123
+ <td>${formatUntil(j.nextRunAt)}</td>
170
124
  </tr>`)}
171
125
  </tbody></table>
172
126
  </div>`}
@@ -4588,6 +4588,71 @@ export interface paths {
4588
4588
  patch?: never;
4589
4589
  trace?: never;
4590
4590
  };
4591
+ "/v1/site/memory-keys": {
4592
+ parameters: {
4593
+ query?: never;
4594
+ header?: never;
4595
+ path?: never;
4596
+ cookie?: never;
4597
+ };
4598
+ /**
4599
+ * List portal memory keys
4600
+ * @description List memory keys under the `portal/*` namespace (site `__site__` owner)
4601
+ * with their values. These resolve in templates via `{{memory:portal/key}}`.
4602
+ * Operator-only (Tier 2).
4603
+ */
4604
+ get: operations["getSiteMemoryKeys"];
4605
+ put?: never;
4606
+ post?: never;
4607
+ delete?: never;
4608
+ options?: never;
4609
+ head?: never;
4610
+ patch?: never;
4611
+ trace?: never;
4612
+ };
4613
+ "/v1/site/memory": {
4614
+ parameters: {
4615
+ query?: never;
4616
+ header?: never;
4617
+ path?: never;
4618
+ cookie?: never;
4619
+ };
4620
+ get?: never;
4621
+ put?: never;
4622
+ /**
4623
+ * Set a portal memory key
4624
+ * @description Set a single portal memory key in the site (`__site__`) namespace so it
4625
+ * resolves in templates via `{{memory:portal/key}}`. The key must start with
4626
+ * `portal/`. Operator-only (Tier 2). Blocked in load-balancer mode.
4627
+ */
4628
+ post: operations["setSiteMemory"];
4629
+ delete?: never;
4630
+ options?: never;
4631
+ head?: never;
4632
+ patch?: never;
4633
+ trace?: never;
4634
+ };
4635
+ "/v1/site/memory/{key}": {
4636
+ parameters: {
4637
+ query?: never;
4638
+ header?: never;
4639
+ path?: never;
4640
+ cookie?: never;
4641
+ };
4642
+ get?: never;
4643
+ put?: never;
4644
+ post?: never;
4645
+ /**
4646
+ * Delete a portal memory key
4647
+ * @description Delete a single portal memory key from the site (`__site__`) namespace.
4648
+ * Operator-only (Tier 2). Blocked in load-balancer mode.
4649
+ */
4650
+ delete: operations["deleteSiteMemory"];
4651
+ options?: never;
4652
+ head?: never;
4653
+ patch?: never;
4654
+ trace?: never;
4655
+ };
4591
4656
  "/v1/site/changelog": {
4592
4657
  parameters: {
4593
4658
  query?: never;
@@ -4692,7 +4757,18 @@ export interface paths {
4692
4757
  * No authentication required (Tier 0).
4693
4758
  */
4694
4759
  get: operations["getExtension"];
4695
- put?: never;
4760
+ /**
4761
+ * Upsert (redeploy) an extension
4762
+ * @description Idempotent upsert. Creates the extension if it does not exist, otherwise replaces it in
4763
+ * place WITHOUT a destructive delete: the extension keeps its identity, its `ext:{name}`
4764
+ * namespace memory, and its instances. Action scripts + manifest are swapped atomically, so
4765
+ * the next `/v1/ext/...` call runs the new code and the action endpoint never disappears
4766
+ * mid-redeploy. For an active extension, schedules are re-registered from the new manifest
4767
+ * and `@activate` jobs re-run. Identical bytes are a safe 200 no-op, and updating never
4768
+ * consumes a quota slot. Create requires ext:write (like POST); update requires owning the
4769
+ * extension.
4770
+ */
4771
+ put: operations["upsertExtension"];
4696
4772
  post?: never;
4697
4773
  /**
4698
4774
  * Uninstall an extension
@@ -5267,7 +5343,19 @@ export interface paths {
5267
5343
  };
5268
5344
  /** Get cortex model details */
5269
5345
  get: operations["getCortexModel"];
5270
- put?: never;
5346
+ /**
5347
+ * Upsert (redeploy) a cortex model
5348
+ * @description Idempotent upsert. Creates the cortex if it does not exist, otherwise replaces it
5349
+ * in place WITHOUT a delete: the cortex keeps its identity and stays served for the
5350
+ * whole call (lib files are swapped in place, never delete-then-recreated for files
5351
+ * that persist, so GET /v1/cortex/{name}/libs/{libFile} never 404s mid-upsert). For an
5352
+ * active cortex, init is re-run so new behaviour goes live (unlike re-activate, which
5353
+ * skips init when already active). Updating an existing cortex never consumes a quota
5354
+ * slot, and an identical manifest + libs is a safe 200 no-op.
5355
+ * Same scope as POST /v1/cortex (cortex:write); owner/operator bypass scope, and only the
5356
+ * installing owner may update.
5357
+ */
5358
+ put: operations["upsertCortexModel"];
5271
5359
  post?: never;
5272
5360
  /** Delete cortex model */
5273
5361
  delete: operations["deleteCortexModel"];
@@ -17932,6 +18020,114 @@ export interface operations {
17932
18020
  422: components["responses"]["UnprocessableEntity"];
17933
18021
  };
17934
18022
  };
18023
+ getSiteMemoryKeys: {
18024
+ parameters: {
18025
+ query?: never;
18026
+ header?: never;
18027
+ path?: never;
18028
+ cookie?: never;
18029
+ };
18030
+ requestBody?: never;
18031
+ responses: {
18032
+ /** @description Portal memory keys */
18033
+ 200: {
18034
+ headers: {
18035
+ [name: string]: unknown;
18036
+ };
18037
+ content: {
18038
+ "application/json": components["schemas"]["AimeatEnvelope"] & {
18039
+ data?: {
18040
+ keys?: {
18041
+ key?: string;
18042
+ value?: unknown;
18043
+ }[];
18044
+ };
18045
+ };
18046
+ };
18047
+ };
18048
+ };
18049
+ };
18050
+ setSiteMemory: {
18051
+ parameters: {
18052
+ query?: never;
18053
+ header?: never;
18054
+ path?: never;
18055
+ cookie?: never;
18056
+ };
18057
+ requestBody: {
18058
+ content: {
18059
+ "application/json": {
18060
+ /**
18061
+ * @description Memory key, must start with `portal/`
18062
+ * @example portal/about
18063
+ */
18064
+ key: string;
18065
+ /** @description Value (may contain operator-trusted HTML) */
18066
+ value: string;
18067
+ };
18068
+ };
18069
+ };
18070
+ responses: {
18071
+ /** @description Memory key stored */
18072
+ 200: {
18073
+ headers: {
18074
+ [name: string]: unknown;
18075
+ };
18076
+ content: {
18077
+ "application/json": components["schemas"]["AimeatEnvelope"] & {
18078
+ data?: {
18079
+ stored?: boolean;
18080
+ key?: string;
18081
+ };
18082
+ };
18083
+ };
18084
+ };
18085
+ /** @description Portal is read-only (load-balancer mode) */
18086
+ 409: {
18087
+ headers: {
18088
+ [name: string]: unknown;
18089
+ };
18090
+ content?: never;
18091
+ };
18092
+ 422: components["responses"]["UnprocessableEntity"];
18093
+ };
18094
+ };
18095
+ deleteSiteMemory: {
18096
+ parameters: {
18097
+ query?: never;
18098
+ header?: never;
18099
+ path: {
18100
+ /** @description URL-encoded portal memory key (e.g. `portal%2Fabout`) */
18101
+ key: string;
18102
+ };
18103
+ cookie?: never;
18104
+ };
18105
+ requestBody?: never;
18106
+ responses: {
18107
+ /** @description Memory key deleted */
18108
+ 200: {
18109
+ headers: {
18110
+ [name: string]: unknown;
18111
+ };
18112
+ content: {
18113
+ "application/json": components["schemas"]["AimeatEnvelope"] & {
18114
+ data?: {
18115
+ deleted?: boolean;
18116
+ key?: string;
18117
+ };
18118
+ };
18119
+ };
18120
+ };
18121
+ 404: components["responses"]["NotFound"];
18122
+ /** @description Portal is read-only (load-balancer mode) */
18123
+ 409: {
18124
+ headers: {
18125
+ [name: string]: unknown;
18126
+ };
18127
+ content?: never;
18128
+ };
18129
+ };
18130
+ };
17935
18131
  getSiteChangelog: {
17936
18132
  parameters: {
17937
18133
  query?: {
@@ -18109,6 +18305,50 @@ export interface operations {
18109
18305
  404: components["responses"]["NotFound"];
18110
18306
  };
18111
18307
  };
18308
+ upsertExtension: {
18309
+ parameters: {
18310
+ query?: never;
18311
+ header?: never;
18312
+ path: {
18313
+ /** @description Extension name (must match metadata.name in the manifest) */
18314
+ name: string;
18315
+ };
18316
+ cookie?: never;
18317
+ };
18318
+ requestBody: {
18319
+ content: {
18320
+ "application/json": {
18321
+ /** @description Extension manifest as a YAML string. */
18322
+ manifest: string;
18323
+ /** @description Map of script filename to JavaScript source. */
18324
+ scripts: {
18325
+ [key: string]: string;
18326
+ };
18327
+ };
18328
+ };
18329
+ };
18330
+ responses: {
18331
+ 200: components["responses"]["AimeatSuccess"];
18332
+ 201: components["responses"]["AimeatSuccess"];
18333
+ 400: components["responses"]["BadRequest"];
18334
+ 401: components["responses"]["Unauthorized"];
18335
+ 403: components["responses"]["Forbidden"];
18336
+ /** @description Install limit exceeded. */
18337
+ 409: {
18338
+ headers: {
18339
+ [name: string]: unknown;
18340
+ };
18341
+ content?: never;
18342
+ };
18343
+ /** @description Per-owner extension limit exceeded. */
18344
+ 429: {
18345
+ headers: {
18346
+ [name: string]: unknown;
18347
+ };
18348
+ content?: never;
18349
+ };
18350
+ };
18351
+ };
18112
18352
  uninstallExtension: {
18113
18353
  parameters: {
18114
18354
  query?: never;
@@ -18760,6 +19000,41 @@ export interface operations {
18760
19000
  404: components["responses"]["NotFound"];
18761
19001
  };
18762
19002
  };
19003
+ upsertCortexModel: {
19004
+ parameters: {
19005
+ query?: never;
19006
+ header?: never;
19007
+ path: {
19008
+ name: string;
19009
+ };
19010
+ cookie?: never;
19011
+ };
19012
+ requestBody: {
19013
+ content: {
19014
+ "application/json": {
19015
+ /** @description Cortex manifest as a YAML string. metadata.name must match the URL name. */
19016
+ manifest: string;
19017
+ /** @description Map of lib filename to JavaScript source. */
19018
+ libs?: {
19019
+ [key: string]: string;
19020
+ };
19021
+ };
19022
+ };
19023
+ };
19024
+ responses: {
19025
+ 200: components["responses"]["AimeatSuccess"];
19026
+ 201: components["responses"]["AimeatSuccess"];
19027
+ 400: components["responses"]["BadRequest"];
19028
+ 403: components["responses"]["Forbidden"];
19029
+ /** @description Manifest, lib, or quota limit exceeded. */
19030
+ 413: {
19031
+ headers: {
19032
+ [name: string]: unknown;
19033
+ };
19034
+ content?: never;
19035
+ };
19036
+ };
19037
+ };
18763
19038
  deleteCortexModel: {
18764
19039
  parameters: {
18765
19040
  query?: never;