aimeat 1.32.0 → 1.32.1
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/locales/en.json +19 -1
- package/dist/locales/fi.json +19 -1
- package/dist/public/css/views/profile.css +44 -0
- package/dist/public/css/views/secretary.css +127 -0
- package/dist/public/js/services/organisms.js +32 -0
- package/dist/public/llms-template.txt +5 -0
- package/dist/public/spa.html +23 -0
- package/dist/public/views/profile/organisms/workspace.js +206 -28
- package/dist/public/views/secretary.js +75 -0
- package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
- package/dist/src/cli/connect/tool-call.js +72 -0
- package/dist/src/cli/connect/tool-call.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +25 -0
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions.js +11 -1
- package/dist/src/mcp/catalog/definitions.js.map +1 -1
- package/dist/src/mcp/catalog/scopes.d.ts +1 -0
- package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
- package/dist/src/mcp/catalog/scopes.js +6 -0
- package/dist/src/mcp/catalog/scopes.js.map +1 -1
- package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
- package/dist/src/mcp/catalog/surfaces.js +2 -0
- package/dist/src/mcp/catalog/surfaces.js.map +1 -1
- package/dist/src/routes/catalogue.d.ts +11 -0
- package/dist/src/routes/catalogue.d.ts.map +1 -1
- package/dist/src/routes/catalogue.js +14 -1
- package/dist/src/routes/catalogue.js.map +1 -1
- package/dist/src/routes/openrouter.d.ts +1 -0
- package/dist/src/routes/openrouter.d.ts.map +1 -1
- package/dist/src/routes/openrouter.js +15 -0
- package/dist/src/routes/openrouter.js.map +1 -1
- package/dist/src/routes/organisms.d.ts.map +1 -1
- package/dist/src/routes/organisms.js +4 -2
- package/dist/src/routes/organisms.js.map +1 -1
- package/dist/src/routes/portal.d.ts.map +1 -1
- package/dist/src/routes/portal.js +1 -0
- package/dist/src/routes/portal.js.map +1 -1
- package/dist/src/services/handbooks/agent.d.ts +4 -1
- package/dist/src/services/handbooks/agent.d.ts.map +1 -1
- package/dist/src/services/handbooks/agent.js +9 -0
- package/dist/src/services/handbooks/agent.js.map +1 -1
- package/dist/src/services/kpi-rollup.d.ts +53 -0
- package/dist/src/services/kpi-rollup.d.ts.map +1 -0
- package/dist/src/services/kpi-rollup.js +64 -0
- package/dist/src/services/kpi-rollup.js.map +1 -0
- package/dist/src/services/manifest-schema.d.ts +8 -1
- package/dist/src/services/manifest-schema.d.ts.map +1 -1
- package/dist/src/services/manifest-schema.js +48 -1
- package/dist/src/services/manifest-schema.js.map +1 -1
- package/dist/src/services/secretary.d.ts +33 -0
- package/dist/src/services/secretary.d.ts.map +1 -0
- package/dist/src/services/secretary.js +63 -0
- package/dist/src/services/secretary.js.map +1 -0
- package/dist/src/services/structure-overview.d.ts +27 -0
- package/dist/src/services/structure-overview.d.ts.map +1 -1
- package/dist/src/services/structure-overview.js +89 -2
- package/dist/src/services/structure-overview.js.map +1 -1
- package/package.json +1 -1
|
@@ -20,6 +20,18 @@
|
|
|
20
20
|
* top of the space; each record is a separated card with breathing room.
|
|
21
21
|
* v1.4.0 — 2026-06-23 — Accept an `initialSpace` prop: a deep-link from the organism mindmap opens
|
|
22
22
|
* the workspace straight on that space's tab (instead of always the overview).
|
|
23
|
+
* v1.5.0 — 2026-06-23 — Measurability: render the manifest's objectives[] as an Objectives card
|
|
24
|
+
* (each KPI's current vs target, ✅/⚠️, computed-vs-self-reported) at the top of the workspace,
|
|
25
|
+
* fed by getWorkspaceOverviewFull + refreshed on live updates. Design:
|
|
26
|
+
* docs/internal/2026-06-23-organism-measurability-design.md (Phase G).
|
|
27
|
+
* v1.6.0 — 2026-06-23 — Findability pass (vaihe 1): the "What happened here" feed shows the
|
|
28
|
+
* document/record title instead of the raw id (instanceTitle); multi-part documents
|
|
29
|
+
* ("… — osa N") collapse under one expandable series row (niputus) in both the doc-space index
|
|
30
|
+
* and the Overview space list — pure display grouping, never touches stored sections/data.
|
|
31
|
+
* v1.7.0 — 2026-06-23 — Findability pass (vaihe 2): optional color tags. A small preset palette
|
|
32
|
+
* (ColorPicker + TAG_PALETTE → theme tokens) lets sections, documents and records be tagged with
|
|
33
|
+
* a color (left-rail accent). Section colors persist on the section object; per-item colors in a
|
|
34
|
+
* parallel meta.colors map (getAllColors/saveColors). Colors are optional and default to none.
|
|
23
35
|
*/
|
|
24
36
|
import { h } from 'preact';
|
|
25
37
|
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
@@ -57,6 +69,29 @@ import { SourcesPanel } from '/views/profile/organisms/sources-panel.js';
|
|
|
57
69
|
|
|
58
70
|
const PRIMARY_FIELD = { goal: 'title', plan: 'approach', deliverable: 'title', resource: 'label', decision: 'summary' };
|
|
59
71
|
|
|
72
|
+
// Optional color tags for sections / documents / records. A small fixed palette of theme tokens
|
|
73
|
+
// (kept in CSS as .pj-tag-{key} → --pj-tag) so colors always match the active light/dark theme —
|
|
74
|
+
// never a free hex that could clash. null/absent = no color (the default, neutral).
|
|
75
|
+
const TAG_PALETTE = ['red', 'orange', 'yellow', 'green', 'blue', 'purple', 'gray'];
|
|
76
|
+
|
|
77
|
+
// A dot that opens an inline swatch palette; picking a color (or ∅) calls onPick(key|null) and closes.
|
|
78
|
+
// Self-contained so it can sit in a section head, a document row or a record row alike.
|
|
79
|
+
function ColorPicker({ value, onPick, title }) {
|
|
80
|
+
const [open, setOpen] = useState(false);
|
|
81
|
+
return html`
|
|
82
|
+
<span class="pj-cp">
|
|
83
|
+
<button class="pj-cp-dot ${value ? 'pj-colored pj-tag-' + value : 'pj-cp-empty'}" title=${title || (t('organisms.color') || 'Color')}
|
|
84
|
+
onClick=${(e) => { e.stopPropagation(); e.preventDefault(); setOpen(o => !o); }}></button>
|
|
85
|
+
${open ? html`
|
|
86
|
+
<span class="pj-cp-pop" onMouseLeave=${() => setOpen(false)}>
|
|
87
|
+
<button class="pj-cp-sw pj-cp-none" title=${t('organisms.noColor') || 'No color'}
|
|
88
|
+
onClick=${(e) => { e.stopPropagation(); e.preventDefault(); onPick(null); setOpen(false); }}>∅</button>
|
|
89
|
+
${TAG_PALETTE.map(c => html`<button key=${c} class="pj-cp-sw pj-colored pj-tag-${c}"
|
|
90
|
+
onClick=${(e) => { e.stopPropagation(); e.preventDefault(); onPick(c); setOpen(false); }}></button>`)}
|
|
91
|
+
</span>` : null}
|
|
92
|
+
</span>`;
|
|
93
|
+
}
|
|
94
|
+
|
|
60
95
|
export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialSpace }) {
|
|
61
96
|
const orgId = org.id;
|
|
62
97
|
// Registry name for the breadcrumb (the manifest's name can differ from the workspace's name).
|
|
@@ -111,7 +146,8 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
111
146
|
// NOT persisted: opening a workspace must show the overview, not whatever tab was last open.
|
|
112
147
|
// Exception: a deep-link from the organism mindmap (initialSpace) opens straight on that space tab.
|
|
113
148
|
const [tab, setTab] = useState(initialSpace ? 'space:' + initialSpace : 'overview');
|
|
114
|
-
const [sectionsByType, setSectionsByType] = useState({}); // { typeName: [{id,name,parentId,documents:[docId]}] }
|
|
149
|
+
const [sectionsByType, setSectionsByType] = useState({}); // { typeName: [{id,name,parentId,documents:[docId],color?}] }
|
|
150
|
+
const [colorsByType, setColorsByType] = useState({}); // { typeName: { instanceId: colorKey } } — optional per-item color tags
|
|
115
151
|
const [editingSec, setEditingSec] = useState(null); // section id currently being renamed inline
|
|
116
152
|
const draggedDoc = useRef(null); // { type, id } of the doc being dragged
|
|
117
153
|
const [showRegenerate, setShowRegenerate] = useState(false);
|
|
@@ -120,6 +156,7 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
120
156
|
const [addingInitial, setAddingInitial] = useState(null); // record being edited (null = new draft)
|
|
121
157
|
const [addingId, setAddingId] = useState(null); // its id, preserved so save overwrites
|
|
122
158
|
const [expandedRec, setExpandedRec] = useState({}); // { "type:id": true } — records expanded to view fields
|
|
159
|
+
const [expandedSeries, setExpandedSeries] = useState({}); // { "type:base": true } — a multi-part doc series expanded in the index
|
|
123
160
|
const [showSpaces, setShowSpaces] = useState(false); // inline add-space form at the workspace top
|
|
124
161
|
const [showFlow, setShowFlow] = useState(true); // the manifest-defined edit-flow chart (first item)
|
|
125
162
|
const [share, setShare] = useState(null); // { public, spaces, docs } — lazy-loaded when Settings opens
|
|
@@ -161,14 +198,15 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
161
198
|
const load = useCallback(async () => {
|
|
162
199
|
const w = await orgService.getWorkspace(orgId, wsId).catch(() => null);
|
|
163
200
|
if (w && w.manifest) {
|
|
164
|
-
const [ap, cfg, secs, act] = await Promise.all([
|
|
201
|
+
const [ap, cfg, secs, act, cols] = await Promise.all([
|
|
165
202
|
orgService.listApprovals(orgId, 'pending').catch(() => []),
|
|
166
203
|
orgService.getConfig(orgId).catch(() => ({})),
|
|
167
204
|
orgService.getAllSections(orgId, wsId).catch(() => ({})),
|
|
168
205
|
orgService.getWorkspaceActivity(orgId, wsId).catch(() => ({ events: [] })),
|
|
206
|
+
orgService.getAllColors(orgId, wsId).catch(() => ({})),
|
|
169
207
|
]);
|
|
170
208
|
setApprovals(ap); setGateOn(!!(cfg?.gates?.publish?.enabled)); setSectionsByType(secs);
|
|
171
|
-
setWsEvents(act?.events || []);
|
|
209
|
+
setWsEvents(act?.events || []); setColorsByType(cols);
|
|
172
210
|
}
|
|
173
211
|
setWs(w && w.manifest ? w : null);
|
|
174
212
|
}, [orgId, wsId]);
|
|
@@ -179,15 +217,16 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
179
217
|
// loaded alongside, refreshed on live updates.
|
|
180
218
|
const [wsGraph, setWsGraph] = useState(null);
|
|
181
219
|
const [wsTocSeed, setWsTocSeed] = useState('');
|
|
220
|
+
const [wsObjectives, setWsObjectives] = useState([]); // measurability objectives + resolved KPIs
|
|
182
221
|
useEffect(() => {
|
|
183
222
|
let cancelled = false;
|
|
184
223
|
const loadGraph = async () => {
|
|
185
|
-
const [g,
|
|
224
|
+
const [g, ov] = await Promise.all([
|
|
186
225
|
orgService.getWorkspaceGraph(orgId, wsId).catch(() => null),
|
|
187
|
-
orgService.
|
|
226
|
+
orgService.getWorkspaceOverviewFull(orgId, wsId).catch(() => ({ markdown: '', objectives: [] })),
|
|
188
227
|
]);
|
|
189
228
|
if (cancelled) return;
|
|
190
|
-
setWsGraph(g); setWsTocSeed(
|
|
229
|
+
setWsGraph(g); setWsTocSeed(ov.markdown || ''); setWsObjectives(ov.objectives || []);
|
|
191
230
|
};
|
|
192
231
|
loadGraph();
|
|
193
232
|
const off = onLiveUpdate(['organisms'], loadGraph);
|
|
@@ -499,6 +538,20 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
499
538
|
setSectionsByType(s => ({ ...s, [typeName]: (s[typeName] || []).map(x => x.id === secId ? { ...x, name } : x) }));
|
|
500
539
|
const commitSecName = (typeName) => { setEditingSec(null); orgService.saveSections(orgId, wsId, typeName, sectionsRef.current[typeName] || []).catch(() => {}); };
|
|
501
540
|
|
|
541
|
+
// ── Optional color tags. Section colors live on the section object (persisted via updateSections);
|
|
542
|
+
// per-document/record colors live in a parallel meta.colors map (persisted via saveColors). ──
|
|
543
|
+
const setSectionColor = (typeName, secId, color) =>
|
|
544
|
+
updateSections(typeName, (sectionsByType[typeName] || []).map(s => s.id === secId ? { ...s, color: color || undefined } : s));
|
|
545
|
+
const itemColor = (typeName, id) => (colorsByType[typeName] || {})[id] || null;
|
|
546
|
+
const setItemColor = useCallback((typeName, id, color) => {
|
|
547
|
+
setColorsByType(prev => {
|
|
548
|
+
const m = { ...(prev[typeName] || {}) };
|
|
549
|
+
if (color) m[id] = color; else delete m[id];
|
|
550
|
+
orgService.saveColors(orgId, wsId, typeName, m).catch(e => showToast((e && e.message) || 'Failed to save color'));
|
|
551
|
+
return { ...prev, [typeName]: m };
|
|
552
|
+
});
|
|
553
|
+
}, [orgId, wsId, showToast]);
|
|
554
|
+
|
|
502
555
|
const publish = useCallback(async (ot, instanceId) => {
|
|
503
556
|
setBusy(true);
|
|
504
557
|
try {
|
|
@@ -809,6 +862,40 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
809
862
|
catch { showToast(t('organisms.copyFailed') || 'Copy failed'); }
|
|
810
863
|
};
|
|
811
864
|
|
|
865
|
+
// Resolve an activity-feed event's instance id to a human title (document title / record primary
|
|
866
|
+
// field), so the feed reads "published · Techstack-matriisi" instead of "published · doc-76qchtb".
|
|
867
|
+
// Falls back to the id when the item isn't in the loaded set (deleted, or not readable).
|
|
868
|
+
const instanceTitle = (typeName, id) => {
|
|
869
|
+
const rec = [...objectsFor(typeName), ...draftsFor(typeName)].find(x => x.id === id);
|
|
870
|
+
if (!rec) return id;
|
|
871
|
+
return rec.title || rec.label || rec.summary || rec.name || rec[PRIMARY_FIELD[typeName]] || id;
|
|
872
|
+
};
|
|
873
|
+
|
|
874
|
+
// ── Series niputus: collapse multi-part documents ("Foo — osa 2", "Foo — part 3") under one
|
|
875
|
+
// expandable parent so a 4-part doc reads as ONE line, not four near-identical ones. PURELY a
|
|
876
|
+
// display grouping — it never touches the stored sections or the documents themselves. The base
|
|
877
|
+
// title is whatever precedes a trailing "— osa N / part N / #N"; a document with no such suffix is
|
|
878
|
+
// its own base (so the lead "Foo" groups with "Foo — osa 2/3"). Only bases with ≥2 members collapse.
|
|
879
|
+
const seriesParse = (title) => {
|
|
880
|
+
const m = /^(.*?)[\s—–-]+(?:osa|part|pt\.?|#)\s*(\d+)\s*$/i.exec(String(title || ''));
|
|
881
|
+
return (m && m[1].trim()) ? { base: m[1].trim(), part: parseInt(m[2], 10) } : { base: String(title || '').trim(), part: 0 };
|
|
882
|
+
};
|
|
883
|
+
const groupDocs = (list) => {
|
|
884
|
+
const map = new Map(); const order = [];
|
|
885
|
+
for (const d of list) {
|
|
886
|
+
const { base, part } = seriesParse(d.title || d.label || d.id || '');
|
|
887
|
+
const k = base.toLowerCase();
|
|
888
|
+
if (!map.has(k)) { map.set(k, { base, parts: [] }); order.push(k); }
|
|
889
|
+
map.get(k).parts.push({ ...d, _part: part });
|
|
890
|
+
}
|
|
891
|
+
return order.map(k => {
|
|
892
|
+
const g = map.get(k);
|
|
893
|
+
if (g.parts.length < 2) return { single: g.parts[0] };
|
|
894
|
+
g.parts.sort((a, b) => a._part - b._part);
|
|
895
|
+
return { base: g.base, parts: g.parts };
|
|
896
|
+
});
|
|
897
|
+
};
|
|
898
|
+
|
|
812
899
|
// A document-space: left index (section tree + documents, with an Unsorted group) + a main
|
|
813
900
|
// area showing the active document (view/edit). Sections nest via parentId; documents are
|
|
814
901
|
// tied to a section's documents[] (or unsorted). Edits to the tree persist immediately.
|
|
@@ -822,11 +909,12 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
822
909
|
const isActive = (d) => activeDoc?.type === ot.name && activeDoc.page?.id === d.id;
|
|
823
910
|
|
|
824
911
|
const docItem = (d) => html`
|
|
825
|
-
<div class="pj-doc-item ${isActive(d) ? 'active' : ''}" key=${'di' + d.id}
|
|
912
|
+
<div class="pj-doc-item ${isActive(d) ? 'active' : ''} ${itemColor(ot.name, d.id) ? 'pj-colored pj-tag-' + itemColor(ot.name, d.id) : ''}" key=${'di' + d.id}
|
|
826
913
|
draggable=${true}
|
|
827
914
|
onDragStart=${(e) => { draggedDoc.current = { type: ot.name, id: d.id }; if (e.dataTransfer) { e.dataTransfer.effectAllowed = 'move'; try { e.dataTransfer.setData('text/plain', d.id); } catch (x) { /* noop */ } } }}
|
|
828
915
|
onDragEnd=${() => { draggedDoc.current = null; }}>
|
|
829
916
|
<span class="pj-grip" title=${t('organisms.dragHint') || 'Drag into a section'}>⠿</span>
|
|
917
|
+
<${ColorPicker} value=${itemColor(ot.name, d.id)} onPick=${(c) => setItemColor(ot.name, d.id, c)} />
|
|
830
918
|
<button class="pj-doc-link" onClick=${() => setActiveDoc({ type: ot.name, mode: 'view', page: d })}>
|
|
831
919
|
${d._draft ? html`<span class="badge badge-warn pj-mini">${t('organisms.draft') || 'draft'}</span> ` : ''}${d.title || d.id}
|
|
832
920
|
</button>
|
|
@@ -837,9 +925,28 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
837
925
|
const dropOn = (secId) => (e) => { e.preventDefault(); e.stopPropagation(); if (draggedDoc.current?.type === ot.name) { moveDocToSection(ot.name, draggedDoc.current.id, secId); draggedDoc.current = null; } };
|
|
838
926
|
const allowDrop = (e) => { e.preventDefault(); if (e.dataTransfer) e.dataTransfer.dropEffect = 'move'; };
|
|
839
927
|
|
|
928
|
+
// Render a document list with multi-part series collapsed (see groupDocs). A series auto-opens
|
|
929
|
+
// when the active document is one of its parts; otherwise it toggles on the header click.
|
|
930
|
+
const renderDocList = (list) => groupDocs(list).map((g) => {
|
|
931
|
+
if (g.single) return docItem(g.single);
|
|
932
|
+
const key = ot.name + ':' + g.base;
|
|
933
|
+
const open = g.parts.some(isActive) || !!expandedSeries[key];
|
|
934
|
+
return html`
|
|
935
|
+
<div class="pj-doc-series ${open ? 'open' : ''}" key=${'ser-' + g.base}>
|
|
936
|
+
<button class="pj-doc-series-head" onClick=${() => setExpandedSeries(s => ({ ...s, [key]: !open }))}>
|
|
937
|
+
<span class="pj-ov-chevron">${open ? '▾' : '▸'}</span>
|
|
938
|
+
<span class="pj-doc-series-name">${g.base}</span>
|
|
939
|
+
<span class="pj-org-tab-count">${g.parts.length}</span>
|
|
940
|
+
${g.parts.some(p => p._draft) ? html`<span class="badge badge-warn pj-mini">${t('organisms.draft') || 'draft'}</span>` : null}
|
|
941
|
+
</button>
|
|
942
|
+
${open ? html`<div class="pj-doc-series-parts">${g.parts.map(docItem)}</div>` : null}
|
|
943
|
+
</div>`;
|
|
944
|
+
});
|
|
945
|
+
|
|
840
946
|
const renderSection = (sec) => html`
|
|
841
|
-
<div class="pj-sec" key=${sec.id} onDragOver=${allowDrop} onDrop=${dropOn(sec.id)}>
|
|
947
|
+
<div class="pj-sec ${sec.color ? 'pj-colored pj-tag-' + sec.color : ''}" key=${sec.id} onDragOver=${allowDrop} onDrop=${dropOn(sec.id)}>
|
|
842
948
|
<div class="pj-sec-head">
|
|
949
|
+
<${ColorPicker} value=${sec.color} onPick=${(c) => setSectionColor(ot.name, sec.id, c)} />
|
|
843
950
|
${editingSec === sec.id
|
|
844
951
|
? html`<input class="input-field input-xs pj-sec-name" autofocus placeholder=${t('organisms.sectionName') || 'Section name'}
|
|
845
952
|
value=${sec.name} onInput=${e => setSecName(ot.name, sec.id, e.target.value)}
|
|
@@ -850,7 +957,7 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
850
957
|
<button class="pj-icon-btn" title=${t('organisms.addSubsection') || 'Sub-section'} onClick=${() => addSection(ot.name, sec.id)}>⊕</button>
|
|
851
958
|
<button class="pj-icon-btn" title=${t('organisms.remove') || 'Remove'} onClick=${() => removeSection(ot.name, sec.id, sec.name)}>✕</button>
|
|
852
959
|
</div>
|
|
853
|
-
${(sec.documents || []).map(id => docById[id]).filter(Boolean)
|
|
960
|
+
${renderDocList((sec.documents || []).map(id => docById[id]).filter(Boolean))}
|
|
854
961
|
${childrenOf(sec.id).map(renderSection)}
|
|
855
962
|
</div>`;
|
|
856
963
|
|
|
@@ -866,7 +973,7 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
866
973
|
<div class="pj-doc-index">
|
|
867
974
|
${childrenOf(null).map(renderSection)}
|
|
868
975
|
${unsorted.length > 0 ? html`
|
|
869
|
-
<div class="pj-sec" onDragOver=${allowDrop} onDrop=${dropOn(null)}><div class="pj-sec-head"><span class="pj-sec-name pj-muted">${t('organisms.unsorted') || 'Unsorted'}</span></div>${unsorted
|
|
976
|
+
<div class="pj-sec" onDragOver=${allowDrop} onDrop=${dropOn(null)}><div class="pj-sec-head"><span class="pj-sec-name pj-muted">${t('organisms.unsorted') || 'Unsorted'}</span></div>${renderDocList(unsorted)}</div>` : null}
|
|
870
977
|
${docs.length === 0 && secs.length === 0 ? html`<${EmptyState} text=${t('organisms.noneYet') || 'none yet'} />` : null}
|
|
871
978
|
</div>
|
|
872
979
|
<div class="pj-doc-main">
|
|
@@ -917,8 +1024,9 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
917
1024
|
: html`<${Spinner} />`)}
|
|
918
1025
|
|
|
919
1026
|
${draftsFor(ot.name).map((d, i) => html`
|
|
920
|
-
<div class="pj-rec" key=${'d' + i}>
|
|
1027
|
+
<div class="pj-rec ${itemColor(ot.name, d.id) ? 'pj-colored pj-tag-' + itemColor(ot.name, d.id) : ''}" key=${'d' + i}>
|
|
921
1028
|
<div class="pj-item pj-item-draft">
|
|
1029
|
+
<${ColorPicker} value=${itemColor(ot.name, d.id)} onPick=${(c) => setItemColor(ot.name, d.id, c)} />
|
|
922
1030
|
<span class="badge badge-warn">${t('organisms.draft') || 'draft'}</span>
|
|
923
1031
|
<button class="pj-rec-title" onClick=${() => toggleExpand(ot, d.id)}>${String(d[PRIMARY_FIELD[ot.name] || 'title'] || d.id || '')}</button>
|
|
924
1032
|
<button class="btn-ghost btn-sm" onClick=${() => startEdit(ot, d)} disabled=${busy}>${t('organisms.edit') || 'Edit'}</button>
|
|
@@ -938,8 +1046,9 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
938
1046
|
${objectsFor(ot.name).length === 0 && draftsFor(ot.name).length === 0
|
|
939
1047
|
? html`<${EmptyState} text=${t('organisms.noneYet') || 'none yet'} />`
|
|
940
1048
|
: objectsFor(ot.name).map((o, i) => html`
|
|
941
|
-
<div class="pj-rec" key=${'o' + i}>
|
|
1049
|
+
<div class="pj-rec ${itemColor(ot.name, o.id) ? 'pj-colored pj-tag-' + itemColor(ot.name, o.id) : ''}" key=${'o' + i}>
|
|
942
1050
|
<div class="pj-item">
|
|
1051
|
+
<${ColorPicker} value=${itemColor(ot.name, o.id)} onPick=${(c) => setItemColor(ot.name, o.id, c)} />
|
|
943
1052
|
<button class="pj-rec-title" onClick=${() => toggleExpand(ot, o.id)}>${String(o[PRIMARY_FIELD[ot.name] || 'title'] || o.summary || o.id || '')}</button>
|
|
944
1053
|
${o.status ? html`<span class="badge badge-info">${(o.status)}</span>` : null}
|
|
945
1054
|
${!draftsFor(ot.name).some(dr => dr.id === o.id) ? html`
|
|
@@ -1127,7 +1236,9 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
1127
1236
|
<button class="btn-ghost btn-sm" onClick=${() => ovAddNew(ot, docMode)}>${'+ '}${docMode ? (t('organisms.newPage') || 'New document') : (t('organisms.addDraft') || 'Add draft')}</button>
|
|
1128
1237
|
</div>`;
|
|
1129
1238
|
const open = ovOpen[ot.name] ?? !isMobileView();
|
|
1130
|
-
|
|
1239
|
+
// Multi-part documents collapse into one series row here too (see groupDocs); records pass through.
|
|
1240
|
+
const display = docMode ? groupDocs(items) : items.map(d => ({ single: d }));
|
|
1241
|
+
const shown = display.slice(0, 5);
|
|
1131
1242
|
return html`
|
|
1132
1243
|
<div class="pj-ov-sec" key=${ot.name}>
|
|
1133
1244
|
<div class="pj-ov-sec-head" onClick=${() => setOvOpen(s => ({ ...s, [ot.name]: !open }))}>
|
|
@@ -1141,26 +1252,91 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
1141
1252
|
${open ? html`
|
|
1142
1253
|
${spaceDesc(ot) ? html`<div class="section-desc pj-ov-desc">${spaceDesc(ot)}</div>` : null}
|
|
1143
1254
|
<div class="pj-ov-items">
|
|
1144
|
-
${shown.map(
|
|
1145
|
-
|
|
1146
|
-
|
|
1255
|
+
${shown.map((g) => {
|
|
1256
|
+
// A collapsed multi-part series (docMode only) → one row; clicking opens the first part
|
|
1257
|
+
// in the space tab, where the full series index lives.
|
|
1258
|
+
if (g.parts) return html`
|
|
1259
|
+
<div class="pj-ov-doc" key=${'ser-' + g.base}>
|
|
1260
|
+
<button class="pj-ov-item" onClick=${() => openOvDoc(ot, g.parts[0])}>
|
|
1261
|
+
${g.parts.some(p => p._draft) ? html`<span class="badge badge-warn pj-mini">${t('organisms.draft') || 'draft'}</span>` : null}
|
|
1262
|
+
<span class="pj-ov-item-title">${g.base}</span>
|
|
1263
|
+
<span class="pj-org-tab-count">${g.parts.length}</span>
|
|
1264
|
+
<span class="pj-ov-preview">${firstLine(g.parts[0].markdown)}</span>
|
|
1265
|
+
</button>
|
|
1266
|
+
</div>`;
|
|
1267
|
+
const d = g.single;
|
|
1268
|
+
return docMode ? html`
|
|
1269
|
+
<div class="pj-ov-doc" key=${d.id}>
|
|
1270
|
+
<button class="pj-ov-item" onClick=${() => openOvDoc(ot, d)}>
|
|
1271
|
+
${d._draft ? html`<span class="badge badge-warn pj-mini">${t('organisms.draft') || 'draft'}</span>` : null}
|
|
1272
|
+
<span class="pj-ov-item-title">${d.title || d.id}</span>
|
|
1273
|
+
<span class="pj-ov-preview">${firstLine(d.markdown)}</span>
|
|
1274
|
+
</button>
|
|
1275
|
+
${ovDoc && ovDoc.type === ot.name && ovDoc.id === d.id ? renderOvDocInline(ot, d) : null}
|
|
1276
|
+
</div>` : html`
|
|
1277
|
+
<button class="pj-ov-item" key=${d.id} onClick=${() => openOvRec(ot, d)}>
|
|
1147
1278
|
${d._draft ? html`<span class="badge badge-warn pj-mini">${t('organisms.draft') || 'draft'}</span>` : null}
|
|
1148
|
-
<span class="pj-ov-item-title">${d.title || d.id}</span>
|
|
1149
|
-
|
|
1150
|
-
</button
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
<button class="pj-ov-item" key=${d.id} onClick=${() => openOvRec(ot, d)}>
|
|
1154
|
-
${d._draft ? html`<span class="badge badge-warn pj-mini">${t('organisms.draft') || 'draft'}</span>` : null}
|
|
1155
|
-
<span class="pj-ov-item-title">${String(d[PRIMARY_FIELD[ot.name] || 'title'] || d.summary || d.id || '')}</span>
|
|
1156
|
-
${d.status ? html`<span class="badge badge-info">${String(d.status)}</span>` : null}
|
|
1157
|
-
</button>`)}
|
|
1158
|
-
${items.length > 5 ? html`
|
|
1279
|
+
<span class="pj-ov-item-title">${String(d[PRIMARY_FIELD[ot.name] || 'title'] || d.summary || d.id || '')}</span>
|
|
1280
|
+
${d.status ? html`<span class="badge badge-info">${String(d.status)}</span>` : null}
|
|
1281
|
+
</button>`;
|
|
1282
|
+
})}
|
|
1283
|
+
${display.length > 5 ? html`
|
|
1159
1284
|
<button class="pj-ov-more" onClick=${() => pickTab('space:' + ot.name)}>${(t('organisms.ovShowAll') || 'Show all {n} →').replace('{n}', String(items.length))}</button>` : null}
|
|
1160
1285
|
</div>` : null}
|
|
1161
1286
|
</div>`;
|
|
1162
1287
|
};
|
|
1163
1288
|
|
|
1289
|
+
// ── Measurability objectives (the manifest's objectives[] + each KPI's resolved current vs target).
|
|
1290
|
+
// The server (overview endpoint) already computed `current` for from:records KPIs; we only render.
|
|
1291
|
+
const kpiTargetText = (tg) => {
|
|
1292
|
+
if (!tg || typeof tg.op !== 'string') return '';
|
|
1293
|
+
if (tg.op === 'between' && Array.isArray(tg.values) && tg.values.length === 2) return `${tg.values[0]}–${tg.values[1]}`;
|
|
1294
|
+
const sym = tg.op === '<=' ? '≤' : tg.op === '>=' ? '≥' : tg.op === '==' ? '=' : tg.op;
|
|
1295
|
+
return typeof tg.value === 'number' ? `${sym} ${tg.value}` : '';
|
|
1296
|
+
};
|
|
1297
|
+
const kpiMeets = (cur, tg) => {
|
|
1298
|
+
if (cur === null || cur === undefined || !tg || typeof tg.op !== 'string') return null;
|
|
1299
|
+
const v = tg.value;
|
|
1300
|
+
switch (tg.op) {
|
|
1301
|
+
case '<': return typeof v === 'number' ? cur < v : null;
|
|
1302
|
+
case '<=': return typeof v === 'number' ? cur <= v : null;
|
|
1303
|
+
case '>': return typeof v === 'number' ? cur > v : null;
|
|
1304
|
+
case '>=': return typeof v === 'number' ? cur >= v : null;
|
|
1305
|
+
case '==': return typeof v === 'number' ? cur === v : null;
|
|
1306
|
+
case 'between': return (Array.isArray(tg.values) && tg.values.length === 2) ? (cur >= tg.values[0] && cur <= tg.values[1]) : null;
|
|
1307
|
+
default: return null;
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
const renderObjectives = () => html`
|
|
1311
|
+
<div class="pj-obj">
|
|
1312
|
+
<div class="pj-obj-title">${t('organisms.objectivesTitle') || 'Objectives'}</div>
|
|
1313
|
+
${wsObjectives.map((o, oi) => html`
|
|
1314
|
+
<div class="pj-obj-card" key=${o.id || oi}>
|
|
1315
|
+
<div class="pj-obj-statement">
|
|
1316
|
+
${(o.statement || o.id)}
|
|
1317
|
+
${o.status === 'met' ? html`<span class="badge badge-success pj-obj-status">${t('organisms.objStatusMet') || 'met'}</span>` : null}
|
|
1318
|
+
${o.status === 'abandoned' ? html`<span class="badge pj-obj-status">${t('organisms.objStatusAbandoned') || 'abandoned'}</span>` : null}
|
|
1319
|
+
</div>
|
|
1320
|
+
${o.why ? html`<div class="pj-obj-why">${(o.why)}</div>` : null}
|
|
1321
|
+
${(o.kpis && o.kpis.length) ? html`
|
|
1322
|
+
<div class="pj-obj-kpis">
|
|
1323
|
+
${o.kpis.map((k, ki) => {
|
|
1324
|
+
const ok = kpiMeets(k.current, k.target);
|
|
1325
|
+
const tgt = kpiTargetText(k.target);
|
|
1326
|
+
const unit = k.unit ? ` ${k.unit}` : '';
|
|
1327
|
+
const val = (k.current === null || k.current === undefined) ? '—' : String(k.current);
|
|
1328
|
+
return html`
|
|
1329
|
+
<div class="pj-obj-kpi ${ok === true ? 'met' : ok === false ? 'off' : ''}" key=${k.name || ki}>
|
|
1330
|
+
<span class="pj-obj-kpi-name">${k.name}</span>
|
|
1331
|
+
<span class="pj-obj-kpi-val">${val}${unit}${ok === true ? ' ✅' : ok === false ? ' ⚠️' : ''}</span>
|
|
1332
|
+
${tgt ? html`<span class="pj-obj-kpi-target">${(t('organisms.kpiTarget') || 'target {t}').replace('{t}', tgt)}</span>` : null}
|
|
1333
|
+
${k.computed === false ? html`<span class="pj-obj-kpi-declared" title=${t('organisms.kpiDeclaredHint') || 'Self-reported — not computed from records'}>${t('organisms.kpiDeclared') || 'self-reported'}</span>` : null}
|
|
1334
|
+
</div>`;
|
|
1335
|
+
})}
|
|
1336
|
+
</div>` : null}
|
|
1337
|
+
</div>`)}
|
|
1338
|
+
</div>`;
|
|
1339
|
+
|
|
1164
1340
|
const renderOverview = () => {
|
|
1165
1341
|
const recent = wsEvents.slice(0, 8);
|
|
1166
1342
|
return html`
|
|
@@ -1173,7 +1349,7 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
1173
1349
|
<span class="pj-act-dot ${e.action}"></span>
|
|
1174
1350
|
<span class="pj-ov-event-who">${shortActor(e.actor)}${e.agent ? html` <span class="pj-act-agent" title=${t('organisms.viaAgent') || 'via this agent'}>🤖 ${e.agent}</span>` : null}</span>
|
|
1175
1351
|
<span class="pj-ov-event-act">${e.action === 'publish' ? (t('organisms.publishedVerb') || 'published') : (t('organisms.editedVerb') || 'edited')}</span>
|
|
1176
|
-
<span class="pj-ov-event-what">${e.mode === 'document' ? '📄' : '🗂'} ${(wsT('type.' + e.type) || e.type)}${' / '}${e.instance}</span>
|
|
1352
|
+
<span class="pj-ov-event-what">${e.mode === 'document' ? '📄' : '🗂'} ${(wsT('type.' + e.type) || e.type)}${' / '}${instanceTitle(e.type, e.instance)}</span>
|
|
1177
1353
|
<span class="pj-ov-event-time">${relTime(e.at)}</span>
|
|
1178
1354
|
</button>`)}
|
|
1179
1355
|
</div>` : null}
|
|
@@ -1197,6 +1373,8 @@ export function Workspace({ org, wsId, showToast, onBack, onBackToList, initialS
|
|
|
1197
1373
|
</div>
|
|
1198
1374
|
${ws.manifest?.summary ? html`<div class="section-desc">${(ws.manifest.summary)}</div>` : null}
|
|
1199
1375
|
|
|
1376
|
+
${wsObjectives.length ? renderObjectives() : null}
|
|
1377
|
+
|
|
1200
1378
|
<${ReadmePanel} markdown=${ws.readme || ''} canEdit=${wsCanEdit} kind="workspace" name=${ws.manifest?.name || 'Workspace'}
|
|
1201
1379
|
aiPromptSeed=${wsTocSeed} onSave=${saveWsReadme} />
|
|
1202
1380
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file secretary.js
|
|
3
|
+
* @description Secretary view (Phase 0 shell). Confirms the per-owner Secretary agent is provisioned
|
|
4
|
+
* and shows its identity + granted scopes. Reached from the header Secretary button, which appears
|
|
5
|
+
* once OpenRouter is configured. The full interactive Secretary (onboarding interview, brain, chat,
|
|
6
|
+
* feed, calendar) lands in later phases — see docs/plans/2026-06-23-secretary-feature.md.
|
|
7
|
+
* @structure SECRETARY_ICON (heart-shield + quill svg) · SecretaryView (default) — fetch /v1/agents,
|
|
8
|
+
* find the `system:secretary` agent, show provisioning status + identity + scopes.
|
|
9
|
+
* @usage routed at /v1/secretary by spa.html.
|
|
10
|
+
* @version-history v0.1.0 — 2026-06-23 — Phase 0 shell: provisioning status + identity.
|
|
11
|
+
*/
|
|
12
|
+
import { h } from 'preact';
|
|
13
|
+
import htm from 'htm';
|
|
14
|
+
const html = htm.bind(h);
|
|
15
|
+
import { useState, useEffect } from 'preact/hooks';
|
|
16
|
+
import { t } from '/js/i18n.js';
|
|
17
|
+
import { apiGet } from '/js/api.js';
|
|
18
|
+
import { escHtml } from '/js/utils.js';
|
|
19
|
+
import { useViewCSS } from '/components/useViewCSS.js';
|
|
20
|
+
|
|
21
|
+
// Heart-shaped shield with a quill across it — the Secretary's mark.
|
|
22
|
+
export const SECRETARY_ICON = html`
|
|
23
|
+
<svg viewBox="0 0 24 24" width="40" height="40" aria-hidden="true">
|
|
24
|
+
<path d="M12 20.6C6.4 16.7 3.4 13.2 3.4 9.2 3.4 6.5 5.4 4.6 7.9 4.6c1.7 0 3.2.9 4.1 2.4.9-1.5 2.4-2.4 4.1-2.4 2.5 0 4.5 1.9 4.5 4.6 0 4-3 7.5-8.6 11.4z"
|
|
25
|
+
fill="none" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
|
|
26
|
+
<path d="M16 8l-5.6 5.6M10.4 13.6l-1.5 2.4 2.4-1.5"
|
|
27
|
+
fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
|
|
28
|
+
</svg>`;
|
|
29
|
+
|
|
30
|
+
export default function SecretaryView() {
|
|
31
|
+
useViewCSS('/css/views/secretary.css');
|
|
32
|
+
// undefined = loading, null = not provisioned yet, object = the Secretary agent.
|
|
33
|
+
const [secretary, setSecretary] = useState(undefined);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
apiGet('/v1/agents')
|
|
37
|
+
.then((r) => {
|
|
38
|
+
const agents = (r && r.data && r.data.agents) || [];
|
|
39
|
+
const sec = agents.find((a) => (a.tags || []).includes('system:secretary'))
|
|
40
|
+
|| agents.find((a) => a.name === 'secretary')
|
|
41
|
+
|| null;
|
|
42
|
+
setSecretary(sec);
|
|
43
|
+
})
|
|
44
|
+
.catch(() => setSecretary(null));
|
|
45
|
+
}, []);
|
|
46
|
+
|
|
47
|
+
return html`
|
|
48
|
+
<div class="sec">
|
|
49
|
+
<header class="sec-hero">
|
|
50
|
+
<span class="sec-hero-icon">${SECRETARY_ICON}</span>
|
|
51
|
+
<div>
|
|
52
|
+
<h1 class="sec-title">${t('secretary.title')}</h1>
|
|
53
|
+
<p class="sec-desc">${t('secretary.desc')}</p>
|
|
54
|
+
</div>
|
|
55
|
+
</header>
|
|
56
|
+
|
|
57
|
+
${secretary === undefined
|
|
58
|
+
? html`<div class="sec-empty">…</div>`
|
|
59
|
+
: secretary === null
|
|
60
|
+
? html`<div class="sec-empty">${t('secretary.notReady')}</div>`
|
|
61
|
+
: html`
|
|
62
|
+
<section class="sec-card">
|
|
63
|
+
<div class="sec-status"><span class="sec-dot"></span> ${t('secretary.ready')}</div>
|
|
64
|
+
<dl class="sec-meta">
|
|
65
|
+
<dt>${t('secretary.identity')}</dt>
|
|
66
|
+
<dd><code>${escHtml(secretary.gaii)}</code></dd>
|
|
67
|
+
<dt>${t('secretary.scopes')}</dt>
|
|
68
|
+
<dd class="sec-scopes">
|
|
69
|
+
${(secretary.default_scopes || []).map((s) => html`<span class="sec-scope" key=${s}>${s}</span>`)}
|
|
70
|
+
</dd>
|
|
71
|
+
</dl>
|
|
72
|
+
<p class="sec-soon">${t('secretary.soon')}</p>
|
|
73
|
+
</section>`}
|
|
74
|
+
</div>`;
|
|
75
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-call.d.ts","sourceRoot":"","sources":["../../../../src/cli/connect/tool-call.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAC/H,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAA+B,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEpF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,UAAU,wBAAwB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CAC9E;AAED,UAAU,eAAe;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACrB;AAkHD,eAAO,MAAM,iBAAiB,EAAE,wBAAwB,
|
|
1
|
+
{"version":3,"file":"tool-call.d.ts","sourceRoot":"","sources":["../../../../src/cli/connect/tool-call.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,EAA0D,KAAK,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAC/H,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAEjE,OAAO,EAA+B,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEpF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE1C,UAAU,wBAAwB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IACvC,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,UAAU,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;CAC9E;AAED,UAAU,eAAe;IACrB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,SAAS,EAAE,MAAM,CAAC;CACrB;AAkHD,eAAO,MAAM,iBAAiB,EAAE,wBAAwB,EAioCvD,CAAC;AAyDF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAS/D;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAahE;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAyD5G"}
|
|
@@ -353,6 +353,29 @@ export const CONNECT_CLI_TOOLS = [
|
|
|
353
353
|
return client.post('/v1/messages', body);
|
|
354
354
|
},
|
|
355
355
|
},
|
|
356
|
+
{
|
|
357
|
+
name: 'aimeat_dm_ask',
|
|
358
|
+
handler: ({ client }, input) => {
|
|
359
|
+
const questions = optionalArray(input, 'questions');
|
|
360
|
+
if (!questions)
|
|
361
|
+
throw new Error('Missing required array field: questions');
|
|
362
|
+
const submitLabel = optionalString(input, 'submit_label');
|
|
363
|
+
const body = {
|
|
364
|
+
to: requiredString(input, 'to'),
|
|
365
|
+
interactive: { role: 'questions', v: 1, questions, ...(submitLabel ? { submitLabel } : {}) },
|
|
366
|
+
};
|
|
367
|
+
const intro = optionalString(input, 'body');
|
|
368
|
+
if (intro)
|
|
369
|
+
body.body = intro;
|
|
370
|
+
const subject = optionalString(input, 'subject');
|
|
371
|
+
if (subject)
|
|
372
|
+
body.subject = subject;
|
|
373
|
+
const conversationId = optionalString(input, 'conversation_id');
|
|
374
|
+
if (conversationId)
|
|
375
|
+
body.conversation_id = conversationId;
|
|
376
|
+
return client.post('/v1/messages', body);
|
|
377
|
+
},
|
|
378
|
+
},
|
|
356
379
|
{
|
|
357
380
|
name: 'aimeat_dm_inbox',
|
|
358
381
|
description: 'Read recent federated DMs addressed to this agent (replies + messages people sent you), newest first.',
|
|
@@ -520,6 +543,22 @@ export const CONNECT_CLI_TOOLS = [
|
|
|
520
543
|
name: 'aimeat_catalogue_search',
|
|
521
544
|
handler: ({ client }, input) => client.get(`/v1/catalogue${query({ q: optionalString(input, 'query') })}`),
|
|
522
545
|
},
|
|
546
|
+
{
|
|
547
|
+
name: 'aimeat_discover',
|
|
548
|
+
handler: ({ client }, input) => {
|
|
549
|
+
const q = query({
|
|
550
|
+
q: optionalString(input, 'q'),
|
|
551
|
+
type: optionalString(input, 'type'),
|
|
552
|
+
tags: optionalString(input, 'tags'),
|
|
553
|
+
segment: optionalString(input, 'segment'),
|
|
554
|
+
scope: optionalString(input, 'scope'),
|
|
555
|
+
per_page: optionalNumber(input, 'limit'),
|
|
556
|
+
});
|
|
557
|
+
// mode=map → facet counts only; mode=find (default) → ranked entries.
|
|
558
|
+
const path = optionalString(input, 'mode') === 'map' ? '/v1/discover/facets' : '/v1/discover';
|
|
559
|
+
return client.get(`${path}${q}`);
|
|
560
|
+
},
|
|
561
|
+
},
|
|
523
562
|
{
|
|
524
563
|
name: 'aimeat_agent_profile',
|
|
525
564
|
handler: ({ client }, input) => client.get(`/v1/agents/${encodeURIComponent(requiredString(input, 'gaii'))}`),
|
|
@@ -800,6 +839,35 @@ export const CONNECT_CLI_TOOLS = [
|
|
|
800
839
|
name: 'aimeat_organism_get',
|
|
801
840
|
handler: ({ client }, input) => client.get(`/v1/organisms/${encodeURIComponent(requiredString(input, 'id'))}`),
|
|
802
841
|
},
|
|
842
|
+
{
|
|
843
|
+
name: 'aimeat_organism_overview',
|
|
844
|
+
handler: ({ client }, input) => client.get(`/v1/organisms/${encodeURIComponent(requiredString(input, 'organism_id'))}/overview`),
|
|
845
|
+
},
|
|
846
|
+
{
|
|
847
|
+
name: 'aimeat_organism_update',
|
|
848
|
+
handler: ({ client }, input) => {
|
|
849
|
+
const body = {};
|
|
850
|
+
const name = optionalString(input, 'name');
|
|
851
|
+
if (name !== undefined)
|
|
852
|
+
body.name = name;
|
|
853
|
+
const description = optionalString(input, 'description');
|
|
854
|
+
if (description !== undefined)
|
|
855
|
+
body.description = description;
|
|
856
|
+
const readme = optionalString(input, 'readme');
|
|
857
|
+
if (readme !== undefined)
|
|
858
|
+
body.readme = readme;
|
|
859
|
+
const interests = optionalArray(input, 'interests');
|
|
860
|
+
if (interests)
|
|
861
|
+
body.interests = interests;
|
|
862
|
+
const joinPolicy = optionalString(input, 'join_policy');
|
|
863
|
+
if (joinPolicy)
|
|
864
|
+
body.join_policy = joinPolicy;
|
|
865
|
+
const visibility = optionalString(input, 'visibility');
|
|
866
|
+
if (visibility)
|
|
867
|
+
body.visibility = visibility;
|
|
868
|
+
return client.put(`/v1/organisms/${encodeURIComponent(requiredString(input, 'organism_id'))}`, body);
|
|
869
|
+
},
|
|
870
|
+
},
|
|
803
871
|
{
|
|
804
872
|
name: 'aimeat_organism_join',
|
|
805
873
|
handler: ({ client }, input) => client.post(`/v1/organisms/${encodeURIComponent(requiredString(input, 'id'))}/join`),
|
|
@@ -938,6 +1006,10 @@ export const CONNECT_CLI_TOOLS = [
|
|
|
938
1006
|
name: 'aimeat_workspace_read',
|
|
939
1007
|
handler: ({ client }, input) => client.get(`/v1/organisms/${encodeURIComponent(requiredString(input, 'organism_id'))}/workspace${query({ ws: requiredString(input, 'ws') })}`),
|
|
940
1008
|
},
|
|
1009
|
+
{
|
|
1010
|
+
name: 'aimeat_workspace_overview',
|
|
1011
|
+
handler: ({ client }, input) => client.get(`/v1/organisms/${encodeURIComponent(requiredString(input, 'organism_id'))}/workspace/overview${query({ ws: requiredString(input, 'ws') })}`),
|
|
1012
|
+
},
|
|
941
1013
|
{
|
|
942
1014
|
name: 'aimeat_workspace_write',
|
|
943
1015
|
handler: async ({ client }, input) => {
|