@elevasis/ui 2.30.0 → 2.32.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.
- package/dist/api/index.js +2 -2
- package/dist/app/index.css +38 -0
- package/dist/app/index.d.ts +190 -9
- package/dist/app/index.js +7 -6
- package/dist/charts/index.js +4 -5
- package/dist/{chunk-HXZQWMKE.js → chunk-2RJMVWFJ.js} +1 -6
- package/dist/{chunk-4VQ2PXMI.js → chunk-3FV6HBXS.js} +4 -4
- package/dist/{chunk-CW3UNAF2.js → chunk-4DYOXEH6.js} +410 -5
- package/dist/{chunk-JKTPRYGV.js → chunk-4MFNGNHF.js} +123 -192
- package/dist/{chunk-HYLERWRO.js → chunk-4QK76KIF.js} +7 -7
- package/dist/chunk-5FJJ72HU.js +13 -0
- package/dist/chunk-5J4PDX26.js +112 -0
- package/dist/{chunk-4SY6BTVZ.js → chunk-6DWD423K.js} +12 -9
- package/dist/{chunk-3GV5NHSS.js → chunk-7KZINJLP.js} +78 -232
- package/dist/{chunk-6WXDE5LZ.js → chunk-EPTHX4VZ.js} +1 -1
- package/dist/{chunk-6EFVZV6X.js → chunk-GCOQ3TBG.js} +243 -254
- package/dist/{chunk-LRZFLK2F.js → chunk-IQHU7O5Y.js} +4 -4
- package/dist/{chunk-X2SUMO3P.js → chunk-IZWTVFJ2.js} +16 -3
- package/dist/{chunk-IKQ42WHU.js → chunk-JFL3GRD4.js} +1 -1
- package/dist/{chunk-4FZYEEPK.js → chunk-LLRXA7D7.js} +5 -6
- package/dist/chunk-MOY4VOHF.js +347 -0
- package/dist/{chunk-XQQEKWTL.js → chunk-N55DVMAG.js} +6 -2
- package/dist/{chunk-7E3FUTND.js → chunk-ND5TDV2J.js} +1 -1
- package/dist/{chunk-WF227UBV.js → chunk-QQHOKTJA.js} +4 -4
- package/dist/{chunk-A7B7HLDF.js → chunk-QTI3KC7D.js} +5884 -515
- package/dist/chunk-QXCDKE2O.js +486 -0
- package/dist/chunk-R2XR4FCV.js +48 -0
- package/dist/chunk-R66W5UDG.js +26 -0
- package/dist/{chunk-T5Z7G2J2.js → chunk-RQA2EVN3.js} +6 -16
- package/dist/{chunk-SKXXT3E2.js → chunk-RQTWIXJ5.js} +4 -4
- package/dist/chunk-T35FWDAB.js +4342 -0
- package/dist/{chunk-DWK2QIAK.js → chunk-TYRUKGGD.js} +1 -1
- package/dist/{chunk-CN2HC4D4.js → chunk-UFTM5SZZ.js} +2 -2
- package/dist/{chunk-JCGD4GM6.js → chunk-UROTM5OR.js} +14 -1
- package/dist/{chunk-6YT4IKJ7.js → chunk-VNAZTCHA.js} +15 -0
- package/dist/{chunk-KVJ3LFH2.js → chunk-VNFR57DF.js} +4 -24
- package/dist/{chunk-SBCIB5TZ.js → chunk-VRNMNB3O.js} +6 -7
- package/dist/chunk-WQPX44YM.js +1626 -0
- package/dist/{chunk-T2PAD63Y.js → chunk-XZGSCABI.js} +1 -1
- package/dist/chunk-YLQEVSOR.js +299 -0
- package/dist/{chunk-P5WYW2GI.js → chunk-ZQOKIGZP.js} +152 -306
- package/dist/components/index.css +38 -0
- package/dist/components/index.d.ts +334 -38
- package/dist/components/index.js +42 -42
- package/dist/components/navigation/index.css +38 -0
- package/dist/components/navigation/index.js +3 -2
- package/dist/execution/index.d.ts +9 -3
- package/dist/features/auth/index.css +38 -0
- package/dist/features/auth/index.d.ts +212 -14
- package/dist/features/auth/index.js +41 -9
- package/dist/features/clients/index.css +649 -0
- package/dist/features/clients/index.d.ts +86 -0
- package/dist/features/clients/index.js +720 -0
- package/dist/features/crm/index.css +38 -0
- package/dist/features/crm/index.d.ts +228 -20
- package/dist/features/crm/index.js +20 -17
- package/dist/features/dashboard/index.css +38 -0
- package/dist/features/dashboard/index.d.ts +78 -3
- package/dist/features/dashboard/index.js +16 -16
- package/dist/features/delivery/index.css +38 -0
- package/dist/features/delivery/index.d.ts +201 -18
- package/dist/features/delivery/index.js +20 -18
- package/dist/features/knowledge/index.css +38 -0
- package/dist/features/knowledge/index.d.ts +20 -18
- package/dist/features/knowledge/index.js +116 -578
- package/dist/features/lead-gen/index.css +38 -0
- package/dist/features/lead-gen/index.d.ts +59 -51
- package/dist/features/lead-gen/index.js +20 -18
- package/dist/features/monitoring/index.css +38 -0
- package/dist/features/monitoring/index.d.ts +20 -18
- package/dist/features/monitoring/index.js +19 -18
- package/dist/features/monitoring/requests/index.css +38 -0
- package/dist/features/monitoring/requests/index.d.ts +21 -19
- package/dist/features/monitoring/requests/index.js +17 -15
- package/dist/features/operations/index.css +38 -0
- package/dist/features/operations/index.d.ts +945 -45
- package/dist/features/operations/index.js +24 -26
- package/dist/features/seo/index.d.ts +20 -18
- package/dist/features/seo/index.js +1 -1
- package/dist/features/settings/index.css +38 -0
- package/dist/features/settings/index.d.ts +201 -18
- package/dist/features/settings/index.js +18 -16
- package/dist/hooks/delivery/index.css +38 -0
- package/dist/hooks/delivery/index.d.ts +200 -0
- package/dist/hooks/delivery/index.js +2 -2
- package/dist/hooks/index.css +38 -0
- package/dist/hooks/index.d.ts +825 -47
- package/dist/hooks/index.js +14 -13
- package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +194 -3
- package/dist/hooks/published.css +38 -0
- package/dist/hooks/published.d.ts +825 -47
- package/dist/hooks/published.js +14 -13
- package/dist/index.css +38 -0
- package/dist/index.d.ts +1506 -1212
- package/dist/index.js +15 -14
- package/dist/initialization/index.d.ts +181 -0
- package/dist/knowledge/index.d.ts +813 -1068
- package/dist/knowledge/index.js +7370 -2869
- package/dist/{chunk-CLUP5H3C.js → knowledge-search-index-5KYPO746.js} +441 -963
- package/dist/layout/index.d.ts +6 -0
- package/dist/layout/index.js +4 -5
- package/dist/organization/index.css +38 -0
- package/dist/profile/index.d.ts +181 -0
- package/dist/provider/index.css +38 -0
- package/dist/provider/index.d.ts +909 -1156
- package/dist/provider/index.js +11 -10
- package/dist/provider/published.css +38 -0
- package/dist/provider/published.d.ts +906 -1153
- package/dist/provider/published.js +7 -6
- package/dist/supabase/index.d.ts +349 -0
- package/dist/test-utils/index.d.ts +16 -9
- package/dist/test-utils/index.js +39 -32
- package/dist/test-utils/setup.js +1 -1
- package/dist/theme/index.js +3 -3
- package/dist/theme/presets/index.js +1 -1
- package/dist/types/index.d.ts +378 -5
- package/dist/utils/index.d.ts +78 -3
- package/dist/utils/index.js +1 -1
- package/dist/vite/index.js +2 -2
- package/dist/vite-plugin-knowledge/index.js +1 -1
- package/package.json +47 -37
- package/src/provider/README.md +5 -5
- package/dist/chunk-2DIYILF7.js +0 -413
- package/dist/chunk-3MDNBHVB.js +0 -3868
- package/dist/chunk-6IXOKUBC.js +0 -347
- package/dist/chunk-AKOD52HS.js +0 -739
- package/dist/chunk-ECNNI3NT.js +0 -6
- package/dist/chunk-JDNEWB5F.js +0 -10
- package/dist/chunk-NITGGYH2.js +0 -476
- package/dist/chunk-OAVTMITG.js +0 -13
|
@@ -1,775 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Stack, Group, Title, Text, Box, Divider, useTree, Tree, UnstyledButton, TextInput } from '@mantine/core';
|
|
3
|
-
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import { useMemo, useState, useRef, useEffect } from 'react';
|
|
5
|
-
import { useClipboard } from '@mantine/hooks';
|
|
6
|
-
import { IconChevronDown, IconChevronRight, IconX, IconSearch, IconCheck, IconCopy } from '@tabler/icons-react';
|
|
7
|
-
|
|
8
|
-
// src/knowledge/iconTokens.ts
|
|
9
|
-
var KNOWLEDGE_ICON_TOKEN_BY_KIND = {
|
|
10
|
-
playbook: "knowledge.playbook",
|
|
11
|
-
strategy: "knowledge.strategy",
|
|
12
|
-
reference: "knowledge.reference"
|
|
13
|
-
};
|
|
14
|
-
function getKnowledgeIconToken(node) {
|
|
15
|
-
return node.icon ?? KNOWLEDGE_ICON_TOKEN_BY_KIND[node.kind];
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// ../core/src/knowledge/queries.ts
|
|
19
|
-
function buildKnowledgeSourceIdMap(graph) {
|
|
20
|
-
const map = /* @__PURE__ */ new Map();
|
|
21
|
-
for (const node of graph.nodes) {
|
|
22
|
-
if (node.kind === "knowledge" && node.sourceId) {
|
|
23
|
-
map.set(node.id, node.sourceId);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return map;
|
|
27
|
-
}
|
|
28
|
-
function byFeature(graph, featureId, knowledgeNodes) {
|
|
29
|
-
const targetGraphNodeId = `feature:${featureId}`;
|
|
30
|
-
const governingKnowledgeNodeIds = /* @__PURE__ */ new Set();
|
|
31
|
-
for (const edge of graph.edges) {
|
|
32
|
-
if (edge.kind === "governs" && edge.targetId === targetGraphNodeId && edge.sourceId.startsWith("knowledge:")) {
|
|
33
|
-
governingKnowledgeNodeIds.add(edge.sourceId);
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
const sourceIdMap = buildKnowledgeSourceIdMap(graph);
|
|
37
|
-
const matchingOmIds = /* @__PURE__ */ new Set();
|
|
38
|
-
for (const graphNodeId of governingKnowledgeNodeIds) {
|
|
39
|
-
const omId = sourceIdMap.get(graphNodeId);
|
|
40
|
-
if (omId) matchingOmIds.add(omId);
|
|
41
|
-
}
|
|
42
|
-
return knowledgeNodes.filter((n) => matchingOmIds.has(n.id));
|
|
43
|
-
}
|
|
44
|
-
function byKind(_graph, kind, knowledgeNodes) {
|
|
45
|
-
return knowledgeNodes.filter((n) => n.kind === kind);
|
|
46
|
-
}
|
|
47
|
-
var KIND_CHIP_TONE_STYLES = {
|
|
48
|
-
subtle: {
|
|
49
|
-
backgroundColor: "var(--color-surface-hover)",
|
|
50
|
-
borderColor: "var(--color-border)",
|
|
51
|
-
color: "var(--color-text-dimmed)"
|
|
52
|
-
},
|
|
53
|
-
primary: {
|
|
54
|
-
backgroundColor: "var(--surface-primary-subtle)",
|
|
55
|
-
borderColor: "color-mix(in srgb, var(--color-primary) 28%, var(--color-border))",
|
|
56
|
-
color: "var(--color-primary)"
|
|
57
|
-
},
|
|
58
|
-
muted: {
|
|
59
|
-
backgroundColor: "var(--color-surface)",
|
|
60
|
-
borderColor: "var(--color-border)",
|
|
61
|
-
color: "var(--color-text-subtle)"
|
|
62
|
-
}
|
|
63
|
-
};
|
|
64
|
-
function KindChip({ kind, tone = "subtle", style, ...spanProps }) {
|
|
65
|
-
return /* @__PURE__ */ jsx(
|
|
66
|
-
"span",
|
|
67
|
-
{
|
|
68
|
-
...spanProps,
|
|
69
|
-
"data-kind-chip": true,
|
|
70
|
-
"data-tone": tone,
|
|
71
|
-
style: {
|
|
72
|
-
display: "inline-flex",
|
|
73
|
-
alignItems: "center",
|
|
74
|
-
width: "fit-content",
|
|
75
|
-
maxWidth: "100%",
|
|
76
|
-
padding: "2px 8px",
|
|
77
|
-
border: "1px solid",
|
|
78
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
79
|
-
fontFamily: "var(--mantine-font-family)",
|
|
80
|
-
fontSize: "var(--mantine-font-size-xs)",
|
|
81
|
-
fontWeight: 600,
|
|
82
|
-
lineHeight: 1.2,
|
|
83
|
-
letterSpacing: 0,
|
|
84
|
-
whiteSpace: "nowrap",
|
|
85
|
-
transition: "background-color var(--duration-fast) var(--easing), border-color var(--duration-fast) var(--easing)",
|
|
86
|
-
...KIND_CHIP_TONE_STYLES[tone],
|
|
87
|
-
...style
|
|
88
|
-
},
|
|
89
|
-
children: kind
|
|
90
|
-
}
|
|
91
|
-
);
|
|
92
|
-
}
|
|
93
|
-
function NodeHeader({
|
|
94
|
-
title,
|
|
95
|
-
kind,
|
|
96
|
-
tone = "primary",
|
|
97
|
-
iconToken,
|
|
98
|
-
description,
|
|
99
|
-
updatedAt,
|
|
100
|
-
rightSection
|
|
101
|
-
}) {
|
|
102
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", children: [
|
|
103
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-start", wrap: "nowrap", children: [
|
|
104
|
-
iconToken && /* @__PURE__ */ jsx(
|
|
105
|
-
SemanticIcon,
|
|
106
|
-
{
|
|
107
|
-
token: iconToken,
|
|
108
|
-
fallbackKind: "knowledge",
|
|
109
|
-
size: 22,
|
|
110
|
-
style: { color: "var(--color-primary)", marginTop: 5 }
|
|
111
|
-
}
|
|
112
|
-
),
|
|
113
|
-
/* @__PURE__ */ jsx(Title, { order: 3, style: { flex: 1, minWidth: 0, color: "var(--color-text)" }, children: title }),
|
|
114
|
-
/* @__PURE__ */ jsx(KindChip, { kind, tone, style: { flexShrink: 0, marginTop: 4 } }),
|
|
115
|
-
rightSection
|
|
116
|
-
] }),
|
|
117
|
-
description && /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", style: { lineHeight: 1.5 }, children: description }),
|
|
118
|
-
updatedAt && /* @__PURE__ */ jsxs(Text, { size: "xs", c: "dimmed", style: { opacity: 0.7 }, children: [
|
|
119
|
-
"Updated ",
|
|
120
|
-
updatedAt
|
|
121
|
-
] })
|
|
122
|
-
] });
|
|
123
|
-
}
|
|
124
|
-
function EdgeChip({ id, onClick }) {
|
|
125
|
-
return /* @__PURE__ */ jsx(
|
|
126
|
-
"div",
|
|
127
|
-
{
|
|
128
|
-
role: onClick ? "button" : void 0,
|
|
129
|
-
tabIndex: onClick ? 0 : void 0,
|
|
130
|
-
onClick,
|
|
131
|
-
onKeyDown: onClick ? (e) => (e.key === "Enter" || e.key === " ") && onClick() : void 0,
|
|
132
|
-
style: {
|
|
133
|
-
display: "inline-flex",
|
|
134
|
-
alignItems: "center",
|
|
135
|
-
padding: "2px 8px",
|
|
136
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
137
|
-
backgroundColor: "var(--color-surface)",
|
|
138
|
-
border: "1px solid var(--color-border)",
|
|
139
|
-
fontSize: "var(--mantine-font-size-xs)",
|
|
140
|
-
color: onClick ? "var(--color-primary)" : "var(--color-text-subtle)",
|
|
141
|
-
cursor: onClick ? "pointer" : "default",
|
|
142
|
-
fontFamily: "var(--mantine-font-family-monospace)",
|
|
143
|
-
width: "fit-content",
|
|
144
|
-
background: "none"
|
|
145
|
-
},
|
|
146
|
-
children: id
|
|
147
|
-
}
|
|
148
|
-
);
|
|
149
|
-
}
|
|
150
|
-
function EdgeGroup({ label, ids, onNavigateToNode }) {
|
|
151
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
152
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", fw: 500, children: label }),
|
|
153
|
-
ids.map((id) => /* @__PURE__ */ jsx(EdgeChip, { id, onClick: onNavigateToNode ? () => onNavigateToNode(id) : void 0 }, id))
|
|
154
|
-
] });
|
|
155
|
-
}
|
|
156
|
-
function knowledgeNodeIdFromGraphNode(graphNode, graphNodeId) {
|
|
157
|
-
if (graphNode?.sourceId) return graphNode.sourceId;
|
|
158
|
-
return graphNodeId.startsWith("knowledge:") ? graphNodeId.slice("knowledge:".length) : graphNodeId;
|
|
159
|
-
}
|
|
160
|
-
function RelatedKnowledgeSection({
|
|
161
|
-
nodeId,
|
|
162
|
-
graph,
|
|
163
|
-
knowledgeNodes,
|
|
164
|
-
onNavigateToNode
|
|
165
|
-
}) {
|
|
166
|
-
const knowledgeById = new Map(knowledgeNodes.map((node) => [node.id, node]));
|
|
167
|
-
const cards = graph.edges.filter((edge) => edge.kind === "governs" && edge.targetId === nodeId).map((edge) => {
|
|
168
|
-
const graphNode = graph.nodes.find((node) => node.id === edge.sourceId && node.kind === "knowledge");
|
|
169
|
-
const knowledgeNode = knowledgeById.get(knowledgeNodeIdFromGraphNode(graphNode, edge.sourceId));
|
|
170
|
-
return knowledgeNode ? { graphNodeId: edge.sourceId, node: knowledgeNode } : null;
|
|
171
|
-
}).filter((card) => card !== null);
|
|
172
|
-
if (cards.length === 0) return null;
|
|
173
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", "data-related-knowledge-section": true, children: [
|
|
174
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, tt: "uppercase", c: "dimmed", style: { letterSpacing: "0.05em" }, children: "Governing Knowledge" }),
|
|
175
|
-
/* @__PURE__ */ jsx(Stack, { gap: "xs", children: cards.map(({ graphNodeId, node }) => /* @__PURE__ */ jsx(
|
|
176
|
-
Box,
|
|
177
|
-
{
|
|
178
|
-
component: onNavigateToNode ? "button" : "article",
|
|
179
|
-
type: onNavigateToNode ? "button" : void 0,
|
|
180
|
-
onClick: onNavigateToNode ? () => onNavigateToNode(graphNodeId) : void 0,
|
|
181
|
-
style: {
|
|
182
|
-
display: "block",
|
|
183
|
-
width: "100%",
|
|
184
|
-
padding: "var(--mantine-spacing-sm)",
|
|
185
|
-
border: "1px solid var(--color-border)",
|
|
186
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
187
|
-
background: "var(--color-surface)",
|
|
188
|
-
color: "var(--color-text)",
|
|
189
|
-
textAlign: "left",
|
|
190
|
-
cursor: onNavigateToNode ? "pointer" : "default",
|
|
191
|
-
font: "inherit"
|
|
192
|
-
},
|
|
193
|
-
children: /* @__PURE__ */ jsxs(Stack, { gap: 4, children: [
|
|
194
|
-
/* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-start", wrap: "nowrap", children: [
|
|
195
|
-
/* @__PURE__ */ jsx(
|
|
196
|
-
SemanticIcon,
|
|
197
|
-
{
|
|
198
|
-
token: getKnowledgeIconToken(node),
|
|
199
|
-
fallbackKind: node.kind,
|
|
200
|
-
size: 16,
|
|
201
|
-
style: { color: "var(--color-text-subtle)", marginTop: 1 }
|
|
202
|
-
}
|
|
203
|
-
),
|
|
204
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", fw: 600, style: { flex: 1, minWidth: 0, lineHeight: 1.35 }, children: node.title }),
|
|
205
|
-
/* @__PURE__ */ jsx(KindChip, { kind: node.kind, tone: "muted", style: { flexShrink: 0 } })
|
|
206
|
-
] }),
|
|
207
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", c: "dimmed", style: { lineHeight: 1.45 }, children: node.summary })
|
|
208
|
-
] })
|
|
209
|
-
},
|
|
210
|
-
node.id
|
|
211
|
-
)) })
|
|
212
|
-
] });
|
|
213
|
-
}
|
|
214
|
-
function relatedIds(graph, nodeId, edgeKind, direction, targetKind) {
|
|
215
|
-
return graph.edges.filter(
|
|
216
|
-
(edge) => direction === "outgoing" ? edge.kind === edgeKind && edge.sourceId === nodeId : edge.kind === edgeKind && edge.targetId === nodeId
|
|
217
|
-
).map((edge) => direction === "outgoing" ? edge.targetId : edge.sourceId).filter((id) => {
|
|
218
|
-
if (!targetKind) return true;
|
|
219
|
-
return graph.nodes.find((node) => node.id === id)?.kind === targetKind;
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
function EdgeRelationshipGroup({ nodeId, graph, knowledgeNodes, onNavigateToNode }) {
|
|
223
|
-
const subFeatures = relatedIds(graph, nodeId, "contains", "outgoing", "feature");
|
|
224
|
-
const resourcesUsed = relatedIds(graph, nodeId, "uses", "outgoing", "resource");
|
|
225
|
-
const entities = relatedIds(graph, nodeId, "operates-on", "outgoing", "entity");
|
|
226
|
-
const governedTargets = relatedIds(graph, nodeId, "governs", "outgoing");
|
|
227
|
-
const governingKnowledge = relatedIds(graph, nodeId, "governs", "incoming", "knowledge");
|
|
228
|
-
const hasRelationships = subFeatures.length > 0 || resourcesUsed.length > 0 || entities.length > 0 || governedTargets.length > 0 || governingKnowledge.length > 0;
|
|
229
|
-
if (!hasRelationships) return null;
|
|
230
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "xs", "data-edge-relationship-group": true, children: [
|
|
231
|
-
/* @__PURE__ */ jsx(Divider, {}),
|
|
232
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", fw: 600, tt: "uppercase", c: "dimmed", style: { letterSpacing: "0.05em" }, children: "Relationships" }),
|
|
233
|
-
subFeatures.length > 0 && /* @__PURE__ */ jsx(
|
|
234
|
-
EdgeGroup,
|
|
235
|
-
{
|
|
236
|
-
label: `Sub-features (${subFeatures.length})`,
|
|
237
|
-
ids: subFeatures,
|
|
238
|
-
onNavigateToNode
|
|
239
|
-
}
|
|
240
|
-
),
|
|
241
|
-
resourcesUsed.length > 0 && /* @__PURE__ */ jsx(
|
|
242
|
-
EdgeGroup,
|
|
243
|
-
{
|
|
244
|
-
label: `Resources used (${resourcesUsed.length})`,
|
|
245
|
-
ids: resourcesUsed,
|
|
246
|
-
onNavigateToNode
|
|
247
|
-
}
|
|
248
|
-
),
|
|
249
|
-
entities.length > 0 && /* @__PURE__ */ jsx(EdgeGroup, { label: `Entities (${entities.length})`, ids: entities, onNavigateToNode }),
|
|
250
|
-
governedTargets.length > 0 && /* @__PURE__ */ jsx(
|
|
251
|
-
EdgeGroup,
|
|
252
|
-
{
|
|
253
|
-
label: `Governs (${governedTargets.length})`,
|
|
254
|
-
ids: governedTargets,
|
|
255
|
-
onNavigateToNode
|
|
256
|
-
}
|
|
257
|
-
),
|
|
258
|
-
governingKnowledge.length > 0 && (knowledgeNodes ? /* @__PURE__ */ jsx(
|
|
259
|
-
RelatedKnowledgeSection,
|
|
260
|
-
{
|
|
261
|
-
nodeId,
|
|
262
|
-
graph,
|
|
263
|
-
knowledgeNodes,
|
|
264
|
-
onNavigateToNode
|
|
265
|
-
}
|
|
266
|
-
) : /* @__PURE__ */ jsx(
|
|
267
|
-
EdgeGroup,
|
|
268
|
-
{
|
|
269
|
-
label: `Governing knowledge (${governingKnowledge.length})`,
|
|
270
|
-
ids: governingKnowledge,
|
|
271
|
-
onNavigateToNode
|
|
272
|
-
}
|
|
273
|
-
))
|
|
274
|
-
] });
|
|
275
|
-
}
|
|
276
|
-
var PRESENTATION_METADATA_KEYS = /* @__PURE__ */ new Set(["label", "description"]);
|
|
277
|
-
function formatMetadataLabel(key) {
|
|
278
|
-
return key.replace(/([a-z0-9])([A-Z])/g, "$1 $2").replace(/^./, (char) => char.toUpperCase());
|
|
279
|
-
}
|
|
280
|
-
function NodeMetadataFooter({ node, title = "Technical metadata" }) {
|
|
281
|
-
const entries = Object.entries(node).filter(
|
|
282
|
-
([key, value]) => !PRESENTATION_METADATA_KEYS.has(key) && value !== void 0 && value !== null && typeof value !== "object"
|
|
283
|
-
);
|
|
284
|
-
if (entries.length === 0) return null;
|
|
285
|
-
return /* @__PURE__ */ jsxs(
|
|
286
|
-
Box,
|
|
287
|
-
{
|
|
288
|
-
component: "details",
|
|
289
|
-
style: {
|
|
290
|
-
borderTop: "1px solid var(--color-border)",
|
|
291
|
-
paddingTop: "var(--mantine-spacing-sm)"
|
|
292
|
-
},
|
|
293
|
-
children: [
|
|
294
|
-
/* @__PURE__ */ jsx(
|
|
295
|
-
Box,
|
|
296
|
-
{
|
|
297
|
-
component: "summary",
|
|
298
|
-
style: {
|
|
299
|
-
cursor: "pointer",
|
|
300
|
-
color: "var(--color-text-dimmed)",
|
|
301
|
-
fontSize: "var(--mantine-font-size-xs)",
|
|
302
|
-
fontWeight: 600,
|
|
303
|
-
letterSpacing: "0.05em",
|
|
304
|
-
textTransform: "uppercase"
|
|
305
|
-
},
|
|
306
|
-
children: title
|
|
307
|
-
}
|
|
308
|
-
),
|
|
309
|
-
/* @__PURE__ */ jsx(Stack, { gap: 4, style: { paddingTop: "var(--mantine-spacing-xs)" }, children: entries.map(([key, value]) => /* @__PURE__ */ jsx(KeyField, { label: formatMetadataLabel(key), value: String(value) }, key)) })
|
|
310
|
-
]
|
|
311
|
-
}
|
|
312
|
-
);
|
|
313
|
-
}
|
|
314
|
-
function NodeDescribeShell({ header, content, relationships, footer }) {
|
|
315
|
-
return /* @__PURE__ */ jsxs(Stack, { gap: "md", style: { padding: "var(--mantine-spacing-md)", width: "100%" }, children: [
|
|
316
|
-
header,
|
|
317
|
-
content && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
318
|
-
header && /* @__PURE__ */ jsx(Divider, {}),
|
|
319
|
-
content
|
|
320
|
-
] }),
|
|
321
|
-
relationships,
|
|
322
|
-
footer
|
|
323
|
-
] });
|
|
324
|
-
}
|
|
325
|
-
function KeyField({ label, value }) {
|
|
326
|
-
return /* @__PURE__ */ jsxs(Group, { gap: "xs", align: "baseline", children: [
|
|
327
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", fw: 500, c: "dimmed", style: { minWidth: 96 }, children: label }),
|
|
328
|
-
/* @__PURE__ */ jsx(Text, { size: "sm", style: { fontFamily: "var(--mantine-font-family-monospace)" }, children: value })
|
|
329
|
-
] });
|
|
330
|
-
}
|
|
331
|
-
var FEATURE_PREFIX = "feat:";
|
|
332
|
-
var LEAF_PREFIX = "leaf:";
|
|
333
|
-
var FOLDER_PREFIX = "folder:";
|
|
334
|
-
var GROUP_THRESHOLD = 8;
|
|
335
|
-
var SIDEBAR_TREE_ACCENT_COLOR = "var(--color-primary)";
|
|
336
|
-
var FOLDER_ORDER = [
|
|
337
|
-
"campaigns",
|
|
338
|
-
"pipeline",
|
|
339
|
-
"targeting",
|
|
340
|
-
"channels",
|
|
341
|
-
"proof",
|
|
342
|
-
"references",
|
|
343
|
-
"playbooks",
|
|
344
|
-
"strategies"
|
|
345
|
-
];
|
|
346
|
-
var FOLDER_LABELS = {
|
|
347
|
-
campaigns: "Campaigns",
|
|
348
|
-
pipeline: "Pipeline",
|
|
349
|
-
targeting: "Targeting and Strategy",
|
|
350
|
-
channels: "Channels",
|
|
351
|
-
proof: "Proof",
|
|
352
|
-
references: "References",
|
|
353
|
-
playbooks: "Playbooks",
|
|
354
|
-
strategies: "Strategies"
|
|
355
|
-
};
|
|
356
|
-
function toBareFeatureId(graphNodeId) {
|
|
357
|
-
return graphNodeId.startsWith("feature:") ? graphNodeId.slice("feature:".length) : graphNodeId;
|
|
358
|
-
}
|
|
359
|
-
function topLevelFeatureIds(allBareIds) {
|
|
360
|
-
return allBareIds.filter((id) => !id.includes("."));
|
|
361
|
-
}
|
|
362
|
-
function childFeatureIds(allBareIds, parentId) {
|
|
363
|
-
const prefix = `${parentId}.`;
|
|
364
|
-
return allBareIds.filter((id) => id.startsWith(prefix) && !id.slice(prefix.length).includes("."));
|
|
365
|
-
}
|
|
366
|
-
function featureHasKnowledgeDescendant(bareId, allBareIds, graph, knowledgeNodes) {
|
|
367
|
-
if (byFeature(graph, bareId, knowledgeNodes).length > 0) return true;
|
|
368
|
-
return childFeatureIds(allBareIds, bareId).some(
|
|
369
|
-
(childId) => featureHasKnowledgeDescendant(childId, allBareIds, graph, knowledgeNodes)
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
function buildFeatureMetaMap(graph) {
|
|
373
|
-
const map = {};
|
|
374
|
-
for (const node of graph.nodes) {
|
|
375
|
-
if (node.kind === "feature") {
|
|
376
|
-
map[toBareFeatureId(node.id)] = { label: node.label, icon: node.icon, node };
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
return map;
|
|
380
|
-
}
|
|
381
|
-
function getKnowledgeReadCommand(nodeId) {
|
|
382
|
-
return `/knowledge read ${nodeId}`;
|
|
383
|
-
}
|
|
384
|
-
function getKnowledgeReadCommands(nodeIds) {
|
|
385
|
-
return [...new Set(nodeIds)].map(getKnowledgeReadCommand).join("\n");
|
|
386
|
-
}
|
|
387
|
-
function getKnowledgeReadFeatureFolderCommand(bareFeatureId) {
|
|
388
|
-
return `/knowledge read-folder feature:${bareFeatureId}`;
|
|
389
|
-
}
|
|
390
|
-
function getKnowledgeFolderKey(node) {
|
|
391
|
-
const key = `${node.id} ${node.title}`.toLowerCase();
|
|
392
|
-
if (/\b(stage|booking|discovery|pipeline|communications?)\b/.test(key)) return "pipeline";
|
|
393
|
-
if (/\b(upwork scanning|youtube|obs|recording|reddit|social)\b/.test(key)) return "channels";
|
|
394
|
-
if (/\b(testimonials?|case stud|proof)\b/.test(key)) return "proof";
|
|
395
|
-
if (/\b(target|personalization|vertical|calibration|query|research|strategy)\b/.test(key)) return "targeting";
|
|
396
|
-
if (/\b(outreach|campaign|copy|handoff|bounce|reply|lead-gen playbook)\b/.test(key)) return "campaigns";
|
|
397
|
-
if (node.kind === "reference") return "references";
|
|
398
|
-
if (node.kind === "strategy") return "strategies";
|
|
399
|
-
return "playbooks";
|
|
400
|
-
}
|
|
401
|
-
function sortFolderKeys(left, right) {
|
|
402
|
-
const leftIndex = FOLDER_ORDER.indexOf(left);
|
|
403
|
-
const rightIndex = FOLDER_ORDER.indexOf(right);
|
|
404
|
-
if (leftIndex === -1 && rightIndex === -1) return left.localeCompare(right);
|
|
405
|
-
if (leftIndex === -1) return 1;
|
|
406
|
-
if (rightIndex === -1) return -1;
|
|
407
|
-
return leftIndex - rightIndex;
|
|
408
|
-
}
|
|
409
|
-
function createKnowledgeLeaf(bareId, node) {
|
|
410
|
-
return {
|
|
411
|
-
value: `${LEAF_PREFIX}${bareId}::${node.id}`,
|
|
412
|
-
label: node.title,
|
|
413
|
-
nodeType: "leaf",
|
|
414
|
-
knowledgeNodeId: node.id,
|
|
415
|
-
knowledgeNodeIds: [node.id]
|
|
416
|
-
};
|
|
417
|
-
}
|
|
418
|
-
function createKnowledgeLeaves(bareId, nodes) {
|
|
419
|
-
if (nodes.length < GROUP_THRESHOLD) {
|
|
420
|
-
return nodes.map((node) => createKnowledgeLeaf(bareId, node));
|
|
421
|
-
}
|
|
422
|
-
const grouped = /* @__PURE__ */ new Map();
|
|
423
|
-
for (const node of nodes) {
|
|
424
|
-
const folderKey = getKnowledgeFolderKey(node);
|
|
425
|
-
const folderNodes = grouped.get(folderKey) ?? [];
|
|
426
|
-
folderNodes.push(node);
|
|
427
|
-
grouped.set(folderKey, folderNodes);
|
|
428
|
-
}
|
|
429
|
-
return [...grouped.entries()].sort(([left], [right]) => sortFolderKeys(left, right)).map(([folderKey, folderNodes]) => ({
|
|
430
|
-
value: `${FOLDER_PREFIX}${bareId}::${folderKey}`,
|
|
431
|
-
label: FOLDER_LABELS[folderKey] ?? folderKey,
|
|
432
|
-
nodeType: "folder",
|
|
433
|
-
icon: "knowledge.playbook",
|
|
434
|
-
knowledgeNodeIds: folderNodes.map((node) => node.id),
|
|
435
|
-
children: folderNodes.map((node) => createKnowledgeLeaf(bareId, node))
|
|
436
|
-
}));
|
|
437
|
-
}
|
|
438
|
-
function collectKnowledgeNodeIds(node) {
|
|
439
|
-
if (node.knowledgeNodeIds) return node.knowledgeNodeIds;
|
|
440
|
-
return (node.children ?? []).flatMap(collectKnowledgeNodeIds);
|
|
441
|
-
}
|
|
442
|
-
function buildFeatureTreeNode(bareId, allBareIds, featureMetaMap, graph, knowledgeNodes) {
|
|
443
|
-
const governing = byFeature(graph, bareId, knowledgeNodes);
|
|
444
|
-
const childIds = childFeatureIds(allBareIds, bareId);
|
|
445
|
-
const childFeatureNodes = childIds.filter((childId) => featureHasKnowledgeDescendant(childId, allBareIds, graph, knowledgeNodes)).map((childId) => buildFeatureTreeNode(childId, allBareIds, featureMetaMap, graph, knowledgeNodes));
|
|
446
|
-
const knowledgeLeaves = createKnowledgeLeaves(bareId, governing);
|
|
447
|
-
const children = [...childFeatureNodes, ...knowledgeLeaves];
|
|
448
|
-
const meta = featureMetaMap[bareId];
|
|
449
|
-
return {
|
|
450
|
-
value: `${FEATURE_PREFIX}${bareId}`,
|
|
451
|
-
label: meta?.label ?? bareId,
|
|
452
|
-
nodeType: "feature",
|
|
453
|
-
icon: meta?.icon,
|
|
454
|
-
knowledgeNodeIds: children.flatMap(collectKnowledgeNodeIds),
|
|
455
|
-
children: children.length > 0 ? children : void 0
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function collectExpandableValues(nodes, acc = {}) {
|
|
459
|
-
for (const node of nodes) {
|
|
460
|
-
if (typeof node.value === "string" && (node.value.startsWith(FEATURE_PREFIX) || node.value.startsWith(FOLDER_PREFIX))) {
|
|
461
|
-
acc[node.value] = true;
|
|
462
|
-
}
|
|
463
|
-
if (node.children) {
|
|
464
|
-
collectExpandableValues(node.children, acc);
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
return acc;
|
|
468
|
-
}
|
|
469
|
-
function KnowledgeTree({
|
|
470
|
-
graph,
|
|
471
|
-
knowledgeNodes,
|
|
472
|
-
onSelectNode,
|
|
473
|
-
onSelectGraphNode,
|
|
474
|
-
selectedNodeId
|
|
475
|
-
}) {
|
|
476
|
-
const treeData = useMemo(() => {
|
|
477
|
-
const featureMetaMap = buildFeatureMetaMap(graph);
|
|
478
|
-
const allBareIds = Object.keys(featureMetaMap);
|
|
479
|
-
const topIds = topLevelFeatureIds(allBareIds);
|
|
480
|
-
return topIds.filter((bareId) => featureHasKnowledgeDescendant(bareId, allBareIds, graph, knowledgeNodes)).map((bareId) => buildFeatureTreeNode(bareId, allBareIds, featureMetaMap, graph, knowledgeNodes));
|
|
481
|
-
}, [graph, knowledgeNodes]);
|
|
482
|
-
const initialExpandedState = useMemo(() => collectExpandableValues(treeData), [treeData]);
|
|
483
|
-
const treeController = useTree({ initialExpandedState });
|
|
484
|
-
const leafNodeMap = useMemo(() => {
|
|
485
|
-
const map = /* @__PURE__ */ new Map();
|
|
486
|
-
for (const node of knowledgeNodes) {
|
|
487
|
-
for (const graphNode of graph.nodes) {
|
|
488
|
-
if (graphNode.kind === "feature") {
|
|
489
|
-
const bareId = toBareFeatureId(graphNode.id);
|
|
490
|
-
map.set(`${LEAF_PREFIX}${bareId}::${node.id}`, node);
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
return map;
|
|
495
|
-
}, [graph, knowledgeNodes]);
|
|
496
|
-
const featureKnowledgeCountMap = useMemo(() => {
|
|
497
|
-
const counts = /* @__PURE__ */ new Map();
|
|
498
|
-
for (const graphNode of graph.nodes) {
|
|
499
|
-
if (graphNode.kind === "feature") {
|
|
500
|
-
const bareId = toBareFeatureId(graphNode.id);
|
|
501
|
-
counts.set(bareId, byFeature(graph, bareId, knowledgeNodes).length);
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
return counts;
|
|
505
|
-
}, [graph, knowledgeNodes]);
|
|
506
|
-
const featureGraphNodeMap = useMemo(() => {
|
|
507
|
-
const map = /* @__PURE__ */ new Map();
|
|
508
|
-
for (const graphNode of graph.nodes) {
|
|
509
|
-
if (graphNode.kind === "feature") {
|
|
510
|
-
map.set(toBareFeatureId(graphNode.id), graphNode);
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
return map;
|
|
514
|
-
}, [graph]);
|
|
515
|
-
if (treeData.length === 0) {
|
|
516
|
-
return /* @__PURE__ */ jsx(Text, { size: "sm", c: "dimmed", style: { padding: "var(--mantine-spacing-md)" }, children: "No features in graph." });
|
|
517
|
-
}
|
|
518
|
-
return /* @__PURE__ */ jsx(
|
|
519
|
-
Tree,
|
|
520
|
-
{
|
|
521
|
-
data: treeData,
|
|
522
|
-
tree: treeController,
|
|
523
|
-
style: { padding: "var(--mantine-spacing-xs)" },
|
|
524
|
-
renderNode: ({ node, expanded, hasChildren, elementProps }) => {
|
|
525
|
-
const value = node.value;
|
|
526
|
-
const typedNode = node;
|
|
527
|
-
if (typeof value === "string" && value.startsWith(LEAF_PREFIX)) {
|
|
528
|
-
const knowledgeNode = leafNodeMap.get(value);
|
|
529
|
-
if (!knowledgeNode) return null;
|
|
530
|
-
return /* @__PURE__ */ jsx(
|
|
531
|
-
KnowledgeLeafRow,
|
|
532
|
-
{
|
|
533
|
-
elementProps,
|
|
534
|
-
knowledgeNode,
|
|
535
|
-
isActive: knowledgeNode.id === selectedNodeId,
|
|
536
|
-
onSelectNode
|
|
537
|
-
}
|
|
538
|
-
);
|
|
539
|
-
}
|
|
540
|
-
if (typeof value === "string" && value.startsWith(FOLDER_PREFIX)) {
|
|
541
|
-
return /* @__PURE__ */ jsx(
|
|
542
|
-
DirectoryRow,
|
|
543
|
-
{
|
|
544
|
-
elementProps,
|
|
545
|
-
expanded,
|
|
546
|
-
hasChildren,
|
|
547
|
-
label: String(node.label),
|
|
548
|
-
iconToken: typedNode.icon,
|
|
549
|
-
count: typedNode.knowledgeNodeIds?.length ?? 0,
|
|
550
|
-
command: getKnowledgeReadCommands(typedNode.knowledgeNodeIds ?? []),
|
|
551
|
-
isActive: false
|
|
552
|
-
}
|
|
553
|
-
);
|
|
554
|
-
}
|
|
555
|
-
const bareId = typeof value === "string" && value.startsWith(FEATURE_PREFIX) ? value.slice(FEATURE_PREFIX.length) : String(value);
|
|
556
|
-
const count = featureKnowledgeCountMap.get(bareId) ?? 0;
|
|
557
|
-
const graphNode = featureGraphNodeMap.get(bareId);
|
|
558
|
-
const isActive = graphNode !== void 0 && (selectedNodeId === graphNode.id || selectedNodeId === graphNode.sourceId || selectedNodeId === bareId);
|
|
559
|
-
return /* @__PURE__ */ jsx(
|
|
560
|
-
DirectoryRow,
|
|
561
|
-
{
|
|
562
|
-
elementProps,
|
|
563
|
-
expanded,
|
|
564
|
-
hasChildren,
|
|
565
|
-
label: String(node.label),
|
|
566
|
-
iconToken: typedNode.icon,
|
|
567
|
-
count,
|
|
568
|
-
command: getKnowledgeReadFeatureFolderCommand(bareId),
|
|
569
|
-
isActive,
|
|
570
|
-
uppercase: true,
|
|
571
|
-
onSelectGraphNode: graphNode ? () => onSelectGraphNode?.(graphNode) : void 0
|
|
572
|
-
}
|
|
573
|
-
);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
);
|
|
577
|
-
}
|
|
578
|
-
function CopyCommandControl({ command, label, visible }) {
|
|
579
|
-
const clipboard = useClipboard({ timeout: 1500 });
|
|
580
|
-
if (!command) return null;
|
|
581
|
-
return /* @__PURE__ */ jsx(
|
|
582
|
-
"span",
|
|
583
|
-
{
|
|
584
|
-
role: "button",
|
|
585
|
-
tabIndex: 0,
|
|
586
|
-
"aria-label": label,
|
|
587
|
-
onClick: (event) => {
|
|
588
|
-
event.preventDefault();
|
|
589
|
-
event.stopPropagation();
|
|
590
|
-
clipboard.copy(command);
|
|
591
|
-
},
|
|
592
|
-
onKeyDown: (event) => {
|
|
593
|
-
if (event.key !== "Enter" && event.key !== " ") return;
|
|
594
|
-
event.preventDefault();
|
|
595
|
-
event.stopPropagation();
|
|
596
|
-
clipboard.copy(command);
|
|
597
|
-
},
|
|
598
|
-
style: {
|
|
599
|
-
display: "inline-flex",
|
|
600
|
-
alignItems: "center",
|
|
601
|
-
justifyContent: "center",
|
|
602
|
-
width: 22,
|
|
603
|
-
height: 22,
|
|
604
|
-
flexShrink: 0,
|
|
605
|
-
opacity: visible ? 1 : 0,
|
|
606
|
-
pointerEvents: visible ? "auto" : "none",
|
|
607
|
-
color: clipboard.copied ? "var(--color-primary)" : "var(--color-text-subtle)",
|
|
608
|
-
transition: "opacity 120ms ease, color 120ms ease"
|
|
609
|
-
},
|
|
610
|
-
children: clipboard.copied ? /* @__PURE__ */ jsx(IconCheck, { size: 14 }) : /* @__PURE__ */ jsx(IconCopy, { size: 14 })
|
|
611
|
-
}
|
|
612
|
-
);
|
|
613
|
-
}
|
|
614
|
-
function KnowledgeLeafRow({ elementProps, knowledgeNode, isActive, onSelectNode }) {
|
|
615
|
-
const [hovered, setHovered] = useState(false);
|
|
616
|
-
return /* @__PURE__ */ jsx(
|
|
617
|
-
UnstyledButton,
|
|
618
|
-
{
|
|
619
|
-
...elementProps,
|
|
620
|
-
onMouseEnter: (event) => {
|
|
621
|
-
elementProps.onMouseEnter?.(event);
|
|
622
|
-
setHovered(true);
|
|
623
|
-
},
|
|
624
|
-
onMouseLeave: (event) => {
|
|
625
|
-
elementProps.onMouseLeave?.(event);
|
|
626
|
-
setHovered(false);
|
|
627
|
-
},
|
|
628
|
-
onClick: () => onSelectNode(knowledgeNode),
|
|
629
|
-
style: {
|
|
630
|
-
...elementProps.style ?? {},
|
|
631
|
-
padding: "5px 8px 5px 24px",
|
|
632
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
633
|
-
backgroundColor: isActive ? "color-mix(in srgb, var(--color-primary) 10%, transparent)" : hovered ? "var(--color-surface-hover)" : "transparent",
|
|
634
|
-
width: "100%",
|
|
635
|
-
textAlign: "left",
|
|
636
|
-
display: "block"
|
|
637
|
-
},
|
|
638
|
-
children: /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", children: [
|
|
639
|
-
/* @__PURE__ */ jsx(
|
|
640
|
-
SemanticIcon,
|
|
641
|
-
{
|
|
642
|
-
token: getKnowledgeIconToken(knowledgeNode),
|
|
643
|
-
fallbackKind: knowledgeNode.kind,
|
|
644
|
-
size: 15,
|
|
645
|
-
style: { color: SIDEBAR_TREE_ACCENT_COLOR }
|
|
646
|
-
}
|
|
647
|
-
),
|
|
648
|
-
/* @__PURE__ */ jsx(
|
|
649
|
-
Text,
|
|
650
|
-
{
|
|
651
|
-
size: "sm",
|
|
652
|
-
c: isActive ? "var(--color-primary)" : hovered ? "var(--color-text)" : void 0,
|
|
653
|
-
fw: isActive ? 600 : 400,
|
|
654
|
-
style: { flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" },
|
|
655
|
-
children: knowledgeNode.title
|
|
656
|
-
}
|
|
657
|
-
),
|
|
658
|
-
/* @__PURE__ */ jsx(
|
|
659
|
-
CopyCommandControl,
|
|
660
|
-
{
|
|
661
|
-
command: getKnowledgeReadCommand(knowledgeNode.id),
|
|
662
|
-
label: "Copy knowledge command",
|
|
663
|
-
visible: hovered
|
|
664
|
-
}
|
|
665
|
-
)
|
|
666
|
-
] })
|
|
667
|
-
}
|
|
668
|
-
);
|
|
669
|
-
}
|
|
670
|
-
function DirectoryRow({
|
|
671
|
-
elementProps,
|
|
672
|
-
expanded,
|
|
673
|
-
hasChildren,
|
|
674
|
-
label,
|
|
675
|
-
iconToken,
|
|
676
|
-
count,
|
|
677
|
-
command,
|
|
678
|
-
isActive,
|
|
679
|
-
uppercase = false,
|
|
680
|
-
onSelectGraphNode
|
|
681
|
-
}) {
|
|
682
|
-
const [hovered, setHovered] = useState(false);
|
|
683
|
-
return /* @__PURE__ */ jsxs(
|
|
684
|
-
Group,
|
|
685
|
-
{
|
|
686
|
-
...elementProps,
|
|
687
|
-
gap: "xs",
|
|
688
|
-
onMouseEnter: (event) => {
|
|
689
|
-
elementProps.onMouseEnter?.(event);
|
|
690
|
-
setHovered(true);
|
|
691
|
-
},
|
|
692
|
-
onMouseLeave: (event) => {
|
|
693
|
-
elementProps.onMouseLeave?.(event);
|
|
694
|
-
setHovered(false);
|
|
695
|
-
},
|
|
696
|
-
onClick: (event) => {
|
|
697
|
-
elementProps.onClick?.(event);
|
|
698
|
-
onSelectGraphNode?.();
|
|
699
|
-
},
|
|
700
|
-
style: {
|
|
701
|
-
...elementProps.style ?? {},
|
|
702
|
-
padding: "4px 8px",
|
|
703
|
-
borderRadius: "var(--mantine-radius-sm)",
|
|
704
|
-
backgroundColor: isActive ? "color-mix(in srgb, var(--color-primary) 10%, transparent)" : hovered ? "var(--color-surface-hover)" : "transparent",
|
|
705
|
-
cursor: onSelectGraphNode || hasChildren ? "pointer" : "default",
|
|
706
|
-
userSelect: "none"
|
|
707
|
-
},
|
|
708
|
-
children: [
|
|
709
|
-
hasChildren ? /* @__PURE__ */ jsx(
|
|
710
|
-
"span",
|
|
711
|
-
{
|
|
712
|
-
"aria-hidden": "true",
|
|
713
|
-
style: {
|
|
714
|
-
display: "inline-flex",
|
|
715
|
-
alignItems: "center",
|
|
716
|
-
justifyContent: "center",
|
|
717
|
-
width: 12,
|
|
718
|
-
height: 12,
|
|
719
|
-
color: SIDEBAR_TREE_ACCENT_COLOR,
|
|
720
|
-
flexShrink: 0
|
|
721
|
-
},
|
|
722
|
-
children: expanded ? /* @__PURE__ */ jsx(IconChevronDown, { size: 12, stroke: 2 }) : /* @__PURE__ */ jsx(IconChevronRight, { size: 12, stroke: 2 })
|
|
723
|
-
}
|
|
724
|
-
) : /* @__PURE__ */ jsx(Text, { size: "xs", style: { width: 12, flexShrink: 0 } }),
|
|
725
|
-
/* @__PURE__ */ jsx(
|
|
726
|
-
SemanticIcon,
|
|
727
|
-
{
|
|
728
|
-
token: iconToken,
|
|
729
|
-
fallbackKind: "feature",
|
|
730
|
-
size: 14,
|
|
731
|
-
style: { color: SIDEBAR_TREE_ACCENT_COLOR }
|
|
732
|
-
}
|
|
733
|
-
),
|
|
734
|
-
/* @__PURE__ */ jsx(
|
|
735
|
-
Text,
|
|
736
|
-
{
|
|
737
|
-
size: "xs",
|
|
738
|
-
fw: isActive ? 700 : 600,
|
|
739
|
-
tt: uppercase ? "uppercase" : void 0,
|
|
740
|
-
c: isActive ? "var(--color-primary)" : hovered ? "var(--color-text)" : void 0,
|
|
741
|
-
style: {
|
|
742
|
-
letterSpacing: uppercase ? "0.05em" : 0,
|
|
743
|
-
flex: 1,
|
|
744
|
-
minWidth: 0,
|
|
745
|
-
overflow: "hidden",
|
|
746
|
-
textOverflow: "ellipsis",
|
|
747
|
-
whiteSpace: "nowrap"
|
|
748
|
-
},
|
|
749
|
-
children: label
|
|
750
|
-
}
|
|
751
|
-
),
|
|
752
|
-
/* @__PURE__ */ jsx(TrailingCopySlot, { count, command, label: "Copy folder knowledge commands", showCopy: hovered })
|
|
753
|
-
]
|
|
754
|
-
}
|
|
755
|
-
);
|
|
756
|
-
}
|
|
757
|
-
function TrailingCopySlot({ count, command, label, showCopy }) {
|
|
758
|
-
return /* @__PURE__ */ jsx(
|
|
759
|
-
"span",
|
|
760
|
-
{
|
|
761
|
-
style: {
|
|
762
|
-
display: "inline-flex",
|
|
763
|
-
alignItems: "center",
|
|
764
|
-
justifyContent: "center",
|
|
765
|
-
width: 32,
|
|
766
|
-
height: 22,
|
|
767
|
-
flexShrink: 0
|
|
768
|
-
},
|
|
769
|
-
children: showCopy && command ? /* @__PURE__ */ jsx(CopyCommandControl, { command, label, visible: true }) : count > 0 ? /* @__PURE__ */ jsx(KindChip, { kind: String(count), tone: "primary" }) : null
|
|
770
|
-
}
|
|
771
|
-
);
|
|
772
|
-
}
|
|
1
|
+
import './chunk-I2KLQ2HA.js';
|
|
773
2
|
|
|
774
3
|
// src/knowledge/_generated/knowledge-search-index.json
|
|
775
4
|
var knowledge_search_index_default = [
|
|
@@ -788,8 +17,8 @@ var knowledge_search_index_default = [
|
|
|
788
17
|
{
|
|
789
18
|
id: "knowledge.org-model-reference",
|
|
790
19
|
title: "Organization Model Schema Reference",
|
|
791
|
-
summary: "Technical reference for the OrganizationModel Zod schema: all domains,
|
|
792
|
-
bodyText: "Schema\n\nThe OrganizationModel schema is defined in packages/core/src/organization-model/schema.ts. It is versioned at version: 1 and composed from domain sub-schemas.\n\
|
|
20
|
+
summary: "Technical reference for the OrganizationModel Zod schema: all top-level domains, the graph contract orientation, authored primitives versus projected graph nodes, and versioning rules.",
|
|
21
|
+
bodyText: "Schema\n\nThe OrganizationModel schema is defined in packages/core/src/organization-model/schema.ts. It is versioned at version: 1 and composed from domain sub-schemas. The OM is the authoritative contract for the organization: downstream surfaces including Command View, the Knowledge Browser, and the navigation shell are all derived projections from this schema.\n\nTop-Level Domains\n\nThese are the current top-level fields on OrganizationModel. There is no live features or capabilities top-level field -- those terms are legacy wording and should not appear as authored OM primitives.\n\n- version -- schema version, currently locked at 1\n- domainMetadata -- per-domain version and lastModified tracking\n- branding -- organization name, logo, color identity\n- navigation -- surfaces, groups, and defaultSurfaceId for shell routing\n- sales -- sales domain including pipeline entity references\n- prospecting -- lead generation stages, entity references, and pipeline config\n- projects -- project, milestone, and task entity references\n- identity -- organization identity fields\n- customers -- customer segment definitions\n- offerings -- product and service definitions linked to customer segments\n- roles -- role definitions with reportsToId hierarchy and agentId holder support\n- goals -- OKR-style objectives with period ranges and system links\n- systems -- the backbone: hierarchical bounded contexts with dotted IDs and parentSystemId\n- resources -- governance descriptors for workflows, agents, integrations, triggers, and scripts; resource identity lives here, not in a separate deployment manifest\n- actions -- the invokable semantic layer; actions map to resources, affect entities, bind to knowledge, and expose invocation types (slash command, MCP tool, API endpoint, script execution)\n- entities -- business objects owned by systems, with table metadata, state catalogs, and typed entity links\n- policies -- governance rules applied to systems, actions, resources, and roles\n- statuses -- runtime semantic status registry\n- knowledge -- first-class OM content backed by inline nodes and MDX source nodes\n\nGraph Contract\n\nThe OM is projected into a typed graph by buildOrganizationGraph() in packages/core/src/organization-model/graph/build.ts.\n\nNode kinds: organization, system, role, action, entity, event, policy, stage, resource, knowledge\n\nEdge kinds: contains, references, mapsto, uses, governs, links, affects, emits, originatesfrom, triggers, appliesto, effects\n\nThe resourceType overlay on resource nodes is a separate enum: workflow, agent, trigger, integration, external, humancheckpoint, script.\n\nAuthored Primitives vs Projected Graph Nodes\n\nAuthored primitives are what you write directly in the OM: systems, roles, resources, actions, entities, policies, knowledge. The graph builder reads these and emits a richer set of typed nodes and edges. Events, for example, are not authored directly -- they are projected from resource emits declarations. Stages are projected from prospecting stage catalogs.\n\nCommand View\n\nCommand View is a derived operational projection of the OM graph. It visualizes systems, resources, actions, entities, and relationships using the same node and edge taxonomy as buildOrganizationGraph(). It does not have a separate deployment manifest model -- the OM resources domain is the single source of resource identity."
|
|
793
22
|
},
|
|
794
23
|
{
|
|
795
24
|
id: "knowledge.seo-lead-gen-playbook",
|
|
@@ -1367,6 +596,131 @@ Graduated Exposure Protocol
|
|
|
1367
596
|
3. Face bubble intro (15-30s) + screen recording \u2014 ongoing
|
|
1368
597
|
|
|
1369
598
|
Each stage desensitizes one fear at a time. Never skip ahead. The goal is consistency, not perfection.`
|
|
599
|
+
},
|
|
600
|
+
{
|
|
601
|
+
id: "knowledge.what-is-elevasis",
|
|
602
|
+
title: "What is Elevasis",
|
|
603
|
+
summary: "Company overview, positioning, value proposition, and pricing for the AI orchestration platform",
|
|
604
|
+
bodyText: 'Executive Summary\n\nCompany: Bootstrapped AI orchestration platform for done-for-you business automation\n\nFounder: Solo (Alex, 34), bootstrapped\n\nMarket: Service-based SMBs that need practical AI automation without building internal AI teams\n\nGTM: Content-led marketing, proof assets, consultative sales, and land-and-expand pricing\n\nCompany Overview\n\nCurrent Status\n\nThe platform core is production-ready enough to support client-facing demonstrations and implementation work. Current business focus is client acquisition, proof-building, and turning the platform into a repeatable service delivery system.\n\nFounder Profile\n\nAlex, 34 \u2014 Solo founder with software engineering background (BSCS degree). Previous: Ecommerce platforms, Streaming technology. No formal AI background \u2014 practitioner, not academic.\n\nWhat We Are\n\nOperating layer for AI automation. We help SMBs operate with closed-loop feedback, decision capture, and continuous learning \u2014 at SMB pricing with done-for-you implementation (NOT self-service).\n\nTarget Market\n\nICP: Service-based SMBs (2-50 employees, USA, $200K-$5M revenue)\n\nCore Pain: Capacity crisis, pipeline problems, operational chaos, growth blockers \u2014 too busy serving clients to grow their own business.\n\nPricing (Land-and-Expand)\n\n- Tier 1 \u2014 Foundation (1-2 workflows): $500-2k/mo\n- Tier 2 \u2014 Scaled Operations (3-5 workflows): $2k-5k/mo\n- Tier 3 \u2014 AI-Powered Systems (6+ workflows): $5k-15k+/mo\n\nAdditional workflows cost less due to shared infrastructure.\n\nValue Proposition\n\n"We find one bottleneck in your business, build an AI solution to fix it, and you only pay if it actually saves you time or makes you money."'
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
id: "knowledge.platform-systems-overview",
|
|
608
|
+
title: "Platform Systems Overview",
|
|
609
|
+
summary: "Authoritative overview of the Elevasis AI Orchestration Platform systems -- what's built, what each system does, and how the pieces fit together",
|
|
610
|
+
bodyText: "Overview\n\nElevasis is a production AI orchestration platform that coordinates workflows, autonomous agents, and human approvals into a unified operating layer for SMBs. Everything described below is implemented and running in production.\n\nCore Architecture:\n\n| Layer | What It Does | Key Components |\n| --- | --- | --- |\n| Execution | Runs workflows and agents with schema validation | Workflow Engine, Agent Framework, Execution Runner |\n| Control | Human oversight, approvals, and decision capture | Command Queue (HITL), Dynamic Forms, Action System |\n| Intelligence | Autonomous reasoning, tool use, and memory management | ReAct Agents, Knowledge Map, Session Memory |\n| Observability | Real-time visibility into cost, performance, and health | Cost Tracking, Metrics, Activity Log, SSE Streaming |\n| Platform | Multi-tenancy, security, scheduling, integrations | Registry, RLS, Scheduler, Credential Vault, SDK |\n\nExecution Engine\n\nAI Workflows\n\nGraph-based workflow execution with schema-validated steps and conditional routing. Steps define explicit routing: linear, conditional (rule-based branching), or terminal. Context flows through the entire workflow.\n\nAutonomous Agents\n\nProduction-grade ReAct-style agents with tool use, memory management, and security hardening.\n\nLLM Provider Support: OpenAI (GPT-5), Google (Gemini 3 Flash), Anthropic (Claude Sonnet 4.5), OpenRouter (GLM-5)\n\nHuman-in-the-Loop (HITL)\n\nThe Command Queue surfaces pending approvals as structured tasks. Admin reviews, approves or edits, and the workflow continues. Every critical decision \u2014 sending emails to prospects, updating customer records, publishing content \u2014 requires human approval.\n\nKnowledge Map\n\nOrganizational knowledge loaded lazily into agent context. 80-95% token savings vs. always-loading full context. Nodes link to features, teams, and other nodes.\n\nIntegrations (13 active)\n\nAttio CRM, Cal.com, Instantly (cold email), Resend, Apify, Google Maps, Tomba, Mails.so, Supabase, Stripe, OpenAI, Google Gemini, Anthropic Claude.\n\nSDK\n\nTypeScript-based resource development with local testing, validation, and deployment pipeline. External consumers define workflows and agents in their own repos and deploy via elevasis-sdk deploy."
|
|
611
|
+
},
|
|
612
|
+
{
|
|
613
|
+
id: "knowledge.client-testimonials",
|
|
614
|
+
title: "Testimonials",
|
|
615
|
+
summary: "Customer testimonials and case study permissions from previous client work",
|
|
616
|
+
bodyText: `Status: 4 testimonials from 2 clients | Source: Upwork client work (2024)
|
|
617
|
+
|
|
618
|
+
Testimonials
|
|
619
|
+
|
|
620
|
+
Xero Automation \u2014 The Invoice Chase That Disappeared
|
|
621
|
+
|
|
622
|
+
Client: Word of Mouth Agency
|
|
623
|
+
|
|
624
|
+
Result: 10+ hours/week saved. Zero manual follow-up emails.
|
|
625
|
+
|
|
626
|
+
> "Working with Alex at Elevasis to automate our Xero follow-ups has been a game-changer. We're set to save over 5 hours a week, but more importantly, it has completely removed the stress of chasing invoices. The process was smooth and professional."
|
|
627
|
+
|
|
628
|
+
Permission: Case study approved with company name.
|
|
629
|
+
|
|
630
|
+
Influencer Discovery \u2014 From Spreadsheet Hell to Strategic Decisions
|
|
631
|
+
|
|
632
|
+
Client: Word of Mouth Agency (Perth, Australia)
|
|
633
|
+
|
|
634
|
+
Result: 10+ hours/week saved. Research scope: 50 to 200+ influencers. Team focus shifted from data to strategy.
|
|
635
|
+
|
|
636
|
+
> "Working with Alex at Elevasis to automate our Influencer Discovery has been a game-changer. We're set to save over 5 hours a week..."
|
|
637
|
+
|
|
638
|
+
Permission: Case study approved with company name.
|
|
639
|
+
|
|
640
|
+
EMRG Media \u2014 4 Automations for NYC's Premier Events Firm
|
|
641
|
+
|
|
642
|
+
Client: EMRG Media (NYC, est. 2001). Clients include Google, YouTube, Sony Music, Fiverr, Equinox.
|
|
643
|
+
|
|
644
|
+
What We Built:
|
|
645
|
+
1. Case Study Generator \u2014 7-10 hours \u2192 2 hours. Publication rate: 1/quarter \u2192 2/month.
|
|
646
|
+
2. EMRG Follow-Up Generator \u2014 Automated post-event follow-ups with signature management.
|
|
647
|
+
3. Event Sponsor Tracker \u2014 Automated sponsor pipeline tracking and communications.
|
|
648
|
+
4. Lead Scraper \u2014 Automated discovery of event planning prospects.
|
|
649
|
+
|
|
650
|
+
Fame Stats:
|
|
651
|
+
- 3,500+ attendees at The Event Planner Expo
|
|
652
|
+
- 25+ year track record
|
|
653
|
+
- Fortune 500 client roster
|
|
654
|
+
|
|
655
|
+
Permission: Case study approved with company name.`
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
id: "knowledge.understanding-elevasis",
|
|
659
|
+
title: "Understanding Elevasis Overview",
|
|
660
|
+
summary: "Company overview, product positioning, and platform systems context for Elevasis AI orchestration platform",
|
|
661
|
+
bodyText: "Lean documentation explaining what Elevasis is and what the platform can do. This section provides the foundational context for business conversations and technical evaluation.\n\nElevasis is a done-for-you AI automation service backed by a production-grade platform. The company targets service-based SMBs (2-50 employees, $200K-$5M revenue) who are too busy serving clients to grow their own business.\n\nKey Facts\n\n- Stage: Pre-revenue, platform 100% complete, client acquisition active\n- Offer: Done-for-you AI automation -- we build and maintain the workflows, no coding required\n- ICP: Service-based SMBs (2-50 employees, $200K-$5M revenue, USA)\n- Pricing: $500-$2k/mo (Foundation) \u2192 $2k-$5k/mo (Scaled) \u2192 $5k-$15k+/mo (AI-Powered)\n- Market: Early Adopters stage, $23.77B TAM growing to $87.7B by 2032\n- Competitive position: No dominant done-for-you SMB AI provider exists; blue ocean\n\nWhen to Use Each Document\n\n| Situation | Document |\n| --- | --- |\n| First conversation with any audience | What is Elevasis |\n| Technical evaluation or demo prep | Platform Systems Overview |\n\nDocumentation\n\n- What is Elevasis \u2014 AI orchestration platform overview, company stage, value proposition, and pricing tiers\n- Platform Systems Overview \u2014 Authoritative overview of what is built, what each system does, and how the pieces fit together"
|
|
662
|
+
},
|
|
663
|
+
{
|
|
664
|
+
id: "knowledge.marketing-overview",
|
|
665
|
+
title: "Marketing Overview",
|
|
666
|
+
summary: "Marketing documentation for Elevasis - strategy, website infrastructure, and content systems",
|
|
667
|
+
bodyText: "Welcome to the Elevasis marketing documentation. This section covers marketing strategy, website implementation, and content systems.\n\nDocumentation\n\nStrategy\n\n- Overview \u2014 Channel strategy, inbound content system, and proof-led demand generation\n\nWebsite\n\nThe marketing website's technical infrastructure, setup, and SEO docs now live under Architecture \u2192 Website."
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
id: "knowledge.ai-orchestration-principles",
|
|
671
|
+
title: "AI Orchestration Principles",
|
|
672
|
+
summary: "The 4 principles that separate production AI from toy automation",
|
|
673
|
+
bodyText: `Most AI automation fails. Not because the technology is bad, but because it's implemented without the principles that make AI systems work in production.
|
|
674
|
+
|
|
675
|
+
These four principles separate toy automation from AI systems that actually run businesses.
|
|
676
|
+
|
|
677
|
+
The 4 Principles
|
|
678
|
+
|
|
679
|
+
1. Integrated: Works With Your Existing Systems
|
|
680
|
+
|
|
681
|
+
Principle: AI must work with your existing tools, not replace them.
|
|
682
|
+
|
|
683
|
+
AI that doesn't connect to your CRM, your email, your calendar, your existing workflows is a toy. Real AI automation works WITH what you already have \u2014 not instead of it.
|
|
684
|
+
|
|
685
|
+
- Your 50 Zapier workflows keep running
|
|
686
|
+
- Your CRM stays your CRM
|
|
687
|
+
- AI adds intelligence on top, not a rip-and-replace
|
|
688
|
+
|
|
689
|
+
The anti-pattern: "Use our AI instead of everything else." Creates vendor lock-in and throws away your existing investment.
|
|
690
|
+
|
|
691
|
+
2. Improving: Gets Smarter Over Time
|
|
692
|
+
|
|
693
|
+
Principle: AI must learn from every decision and improve continuously.
|
|
694
|
+
|
|
695
|
+
Static automation is just fancy if-then logic. Real AI captures every approval, rejection, and edit \u2014 then uses that data to get smarter.
|
|
696
|
+
|
|
697
|
+
The anti-pattern: "Set it and forget it." Systems that don't learn stay dumb forever.
|
|
698
|
+
|
|
699
|
+
3. Observable: You See What AI Is Doing
|
|
700
|
+
|
|
701
|
+
Principle: You must be able to see what AI systems are doing in real-time.
|
|
702
|
+
|
|
703
|
+
Black-box automation is unacceptable for business operations. You need to see every action, every decision, every cost \u2014 as it happens.
|
|
704
|
+
|
|
705
|
+
- Real-time activity feeds showing what's executing
|
|
706
|
+
- Cost tracking per workflow, per execution
|
|
707
|
+
- Token usage visibility
|
|
708
|
+
- Execution logs for debugging
|
|
709
|
+
|
|
710
|
+
The anti-pattern: "It just works, trust us." Systems without observability create anxiety and prevent adoption.
|
|
711
|
+
|
|
712
|
+
4. Governed: Humans Control What Matters
|
|
713
|
+
|
|
714
|
+
Principle: Humans must approve important actions before execution.
|
|
715
|
+
|
|
716
|
+
AI should handle the grunt work. But critical decisions \u2014 sending emails to prospects, updating customer records, publishing content \u2014 require human approval.
|
|
717
|
+
|
|
718
|
+
- AI researches 50 prospects and drafts personalized emails
|
|
719
|
+
- Before anything sends, it lands in your approval queue
|
|
720
|
+
- You spend 10 minutes reviewing, approve or tweak
|
|
721
|
+
- Only then does it execute
|
|
722
|
+
|
|
723
|
+
The anti-pattern: "Fully autonomous AI." Systems without governance create risk and erode trust.`
|
|
1370
724
|
},
|
|
1371
725
|
{
|
|
1372
726
|
id: "knowledge.new-vertical-launch-playbook",
|
|
@@ -1528,6 +882,240 @@ Launch Checklist
|
|
|
1528
882
|
summary: "Stable scanning, scoring, freshness, and query-health workflow for the Upwork acquisition channel.",
|
|
1529
883
|
bodyText: 'Overview\n\nUse this playbook to run the Upwork acquisition scanning loop. The goal is to find fresh, low-competition jobs where Elevasis can deliver a real business system, score them consistently, and turn the best opportunities into proposals through the Upwork proposal playbook.\n\nThe scanning loop has six phases:\n\n1. Confirm browser and login readiness.\n2. Run saved search queries with the right filters.\n3. Extract fresh job cards.\n4. Deduplicate, score, and enrich strong candidates.\n5. Write the scan output.\n6. Review query health before the next scan.\n\nProposal copy and response handling are intentionally separate. Use knowledge.upwork-proposal-playbook for proposal strategy and knowledge.upwork-response-templates for client replies.\n\nPreconditions\n\nBefore scanning, verify the browser automation surface is available and Upwork is logged in.\n\n- Chrome tooling must be available through the active browser automation tools.\n- The Upwork search page must load at https://www.upwork.com/nx/search/jobs/.\n- The page must show the job search interface, not a login prompt.\n- If login is required, stop and log in manually before scanning again.\n\nDo not try to work around a missing browser session by inventing scan results. The scan depends on live Upwork search pages, current saved-search filters, and the visible result cards.\n\nScan Surface\n\nRun scans from the Upwork search page:\n\ntext\nhttps://www.upwork.com/nx/search/jobs/\n\nEach saved query is run individually. Apply the filters every time, because Upwork search state is session-dependent and not always persisted across queries.\n\n| Filter | Value | Reason |\n| --------- | ----------- | ---------------------------------------------------------------------- |\n| U.S. only | Checked | Higher-quality clients, stronger timezone fit, more verified payments. |\n| Sort by | Most Recent | Freshness is the strongest conversion lever. |\n| Proposals | \\<5, 5-10 | Low competition is required before spending connects. |\n\nUse saved queries as the primary scan source. The strategy targets operational system-build terms, not generic "AI automation" searches that attract high competition.\n\nFreshness Rule\n\nOnly collect posts from the last 12 hours. Results should be sorted by Most Recent, so stop extracting for a query once the visible posts are older than 12 hours. Do not do catch-up windows.\n\nFreshness priority:\n\n| Post age | Action |\n| ---------- | ---------------------------------------------------------------- |\n| 0-1 hour | Highest priority. Client is most likely active and shortlisting. |\n| 1-12 hours | Valid scan window. Still worth scoring if competition is low. |\n| 12+ hours | Skip. The client has usually already formed the shortlist. |\n\nRecord the scan timestamp as lastscanned in the daily scan doc frontmatter. This timestamp helps avoid reprocessing posts from prior scans, but the hard 12-hour cap still applies.\n\nRun Each Query\n\nFor each active saved query:\n\n1. Clear the search box.\n2. Enter the query string and submit.\n3. Apply U.S. only, Most Recent, and proposal-count filters.\n4. Wait for results to render.\n5. Extract visible job cards from the search page.\n6. Stop once a post exceeds the freshness cutoff.\n7. Tag each job with the source query.\n\nThe first page of most-recent, low-proposal results is the high-value slice. Do not paginate unless a specific investigation requires it.\n\nWhile scanning the API integration query, flag custom dashboard opportunities where a custom build is likely better than Power BI, Looker, Tableau, or a spreadsheet. Strong signs include 3+ data sources, live dashboard requirements, AI-generated summaries, anomaly detection, or workflow triggers.\n\nExtract Job Card Data\n\nThe search-card extraction should capture the fields needed for initial triage:\n\n- Title.\n- Upwork job URL path.\n- Posted age.\n- Proposal count.\n- Budget or hourly range.\n- Payment verification.\n- Client rating.\n- Total client spend.\n- Description snippet.\n- Source query.\n\nDeduplicate by Upwork job URL path after all queries finish. If a job appears under multiple queries, keep one row and note the overlap.\n\nScore and Enrich\n\nScoring has two stages.\n\nStage 1: Relevance\n\nScore relevance from 0 to 100 by asking whether the client needs a system, workflow, integration, automation, or operational build that Elevasis can deliver.\n\n| Range | Label | Criteria |\n| ------ | ------------ | ------------------------------------------------------------------------- |\n| 75-100 | Strong fit | Clear build intent, specific business workflow, named tools or outputs. |\n| 50-74 | Possible fit | Some build signals, but scope or buyer intent is ambiguous. |\n| 25-49 | Weak fit | Marginally related, mostly noise, low business-system signal. |\n| 0-24 | Irrelevant | Hiring, marketing, design, manual VA work, personal projects, or errands. |\n\nDisqualify posts that are clearly hiring a role, outsourcing manual work, asking for generic marketing/design help, or describing a personal/non-business project.\n\nStage 2: Application Priority\n\nApply tactical modifiers after relevance scoring. The final score determines whether to draft a proposal.\n\n| Signal | Positive indicators | Negative indicators |\n| -------------- | ---------------------------------------------------- | ------------------------------------------- |\n| Freshness | \\<1h or same-session post. | 8-12h old, or outside the scan window. |\n| Competition | \\<5 proposals, or 5-10 with strong fit. | 10+ proposals, especially 20+. |\n| Client quality | Payment verified, spend history, good rating, hires. | Unverified, no history, 0% hire rate. |\n| Budget | Fixed \\>$5K or hourly \\>$75/hr. | Fixed \\<$1K or hourly \\<$40/hr. |\n| Fit | Workflow, integration, dashboard, CRM, operations. | Copywriting, funnels, ads, hiring, support. |\n\nOnly enrich posts with a strong enough relevance score to matter, usually 65+. For those posts, open the job detail view and capture:\n\n- Hire rate.\n- Jobs posted.\n- Member since.\n- Company size.\n- Last viewed by client.\n- Interviewing count.\n\nUse this data to avoid over-ranking stale or low-quality clients that have a good-sounding job description but weak application ROI.\n\nWrite the Scan Output\n\nWrite scan docs under:\n\ntext\napps/docs/content/docs/operations/client-acquisition/upwork/scans/{YYYY-MM-DD}.mdx\n\nIf a scan doc already exists for the day, append the new scan as a later section instead of creating a second daily file.\n\nEach scan output should include:\n\n- Date and timestamp.\n- Freshness cutoff.\n- Queries scanned.\n- Total posts collected.\n- Deduplicated count.\n- Top opportunities only.\n- Comparison table.\n- Query health table.\n- Draft proposals section, when proposals are generated.\n\nKeep scan docs as decision tools, not archives. Include the top 10 ranked opportunities rather than every collected post.\n\nQuery Health\n\nAfter scoring, compute a query-health score so weak searches can be removed over time.\n\n| Metric | Measurement | Weight |\n| --------------- | ----------------------------------------------------------------- | ------ |\n| Fresh posts | Count within cutoff, normalized against 5 posts. | 25% |\n| Low competition | Percent with fewer than 10 proposals. | 25% |\n| Relevance | Percent actionable after deduplication and disqualification. | 30% |\n| Client quality | Percent with verified payment and useful spend or rating signals. | 20% |\n\nQueries that score 0 across 3+ consecutive scans are drop candidates. Compare the latest query-health table with the previous scan before changing the active query set.\n\nActive Query Principles\n\nStrong Upwork queries describe the system the buyer needs, not the freelancer category they think they are hiring.\n\nUse:\n\n- Specific operational system nouns such as booking system, intake form, scheduling system, ticketing system, tracking system, reservation system, billing system, and payment system.\n- Action verbs such as build, automate, custom, develop, setup, integration, and workflow.\n- Platform-specific searches where system-build intent overlaps with implementation work, such as GoHighLevel, Pipedrive, CRM, API integration, or inventory sync.\n\nAvoid:\n\n- Broad solution-first terms such as AI automation, AI agent, workflow automation, portal, dashboard, tool, or custom without a specific system noun.\n- Vertical keywords such as clinic, contractor, salon, or real estate unless paired with concrete system intent.\n- Pain-signal phrases that usually mean the client is hiring a human to perform manual work.\n- Revenue-proximity service terms such as cold email, appointment setting, outbound specialist, or funnel work.\n\nBefore testing a new query, read knowledge.upwork-query-strategy and check the Upwork query registry in the operations docs so old failure modes are not repeated. Use knowledge.upwork-calibration-strategy when promoting, dropping, or re-evaluating saved searches.\n\nProposal Handoff\n\nAfter the scan, draft proposals only for opportunities that remain strong after relevance, tactical modifiers, and client-quality checks.\n\nFor proposal drafting:\n\n1. Load knowledge.upwork-proposal-playbook.\n2. Read the top opportunities from the latest scan doc.\n3. Select the right proposal template by scope, budget, and relevance.\n4. Tailor the opening, proof point, plan, and two discovery questions.\n5. Append draft proposals to the scan doc under ## Draft Proposals.\n\nUse knowledge.upwork-response-templates after a client replies or sends an offer.\n\nScan Checklist\n\n- Browser tooling available.\n- Upwork logged in.\n- Search page loaded.\n- U.S. only filter applied.\n- Sort set to Most Recent.\n- Proposal-count filters applied.\n- All active saved queries scanned.\n- Posts older than 12 hours skipped.\n- Jobs deduplicated by URL path.\n- Relevance scores assigned.\n- Strong candidates enriched from detail view.\n- Final scores sorted descending.\n- Daily scan doc created or appended.\n- Query health table written.\n- Top proposal candidates handed to knowledge.upwork-proposal-playbook.'
|
|
1530
884
|
},
|
|
885
|
+
{
|
|
886
|
+
id: "knowledge.client-cli-overview",
|
|
887
|
+
title: "Client CLI Overview",
|
|
888
|
+
summary: "Reference for the seven client:* SDK CLI commands -- list, get, status, resolve, create, update, and delete -- with drill-down recipes for navigating client lineage, org-wide rollups, and write operations.",
|
|
889
|
+
bodyText: `Overview
|
|
890
|
+
|
|
891
|
+
The client: commands expose the clients hub through the SDK CLI. Use them to paginate clients, inspect individual client lineage, check org-wide status rollups, resolve a fuzzy name to a client ID, and mutate clients with create, update, and delete operations.
|
|
892
|
+
|
|
893
|
+
All examples below use the canonical monorepo invocation pattern. Tenant projects inside their own operations/ directory drop the -C packages/elevasis-operations prefix and run pnpm exec elevasis-sdk <cmd> directly.
|
|
894
|
+
|
|
895
|
+
client:list
|
|
896
|
+
|
|
897
|
+
Lists clients for the authenticated organization with optional filtering and pagination.
|
|
898
|
+
|
|
899
|
+
Purpose: Paginate all clients or narrow by status or search term to find the right client ID before drilling in with client:get.
|
|
900
|
+
|
|
901
|
+
Monorepo invocation:
|
|
902
|
+
|
|
903
|
+
bash
|
|
904
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:list
|
|
905
|
+
|
|
906
|
+
Tenant invocation (from inside operations/):
|
|
907
|
+
|
|
908
|
+
bash
|
|
909
|
+
pnpm exec elevasis-sdk client:list
|
|
910
|
+
|
|
911
|
+
Key flags:
|
|
912
|
+
|
|
913
|
+
- --status <value> -- filter by client status (e.g. active, inactive, prospect)
|
|
914
|
+
- --search <term> -- full-text search against client name
|
|
915
|
+
- --limit <n> -- maximum rows to return (default varies by server)
|
|
916
|
+
- --offset <n> -- pagination offset
|
|
917
|
+
- --pretty -- pretty-print JSON output
|
|
918
|
+
|
|
919
|
+
Drill-down recipe:
|
|
920
|
+
|
|
921
|
+
1. Run client:list --pretty to scan names and IDs.
|
|
922
|
+
2. Use --search to narrow when the list is long.
|
|
923
|
+
3. Copy the id from a row and pass it to client:get <id> for the full lineage payload (linked deal, primary company, primary contact, projects).
|
|
924
|
+
|
|
925
|
+
client:get
|
|
926
|
+
|
|
927
|
+
Fetches a single client record with its full lineage payload.
|
|
928
|
+
|
|
929
|
+
Purpose: Inspect one client in detail -- its associated deal, primary company, primary contact, and linked projects. Use this to confirm linkage after wiring a project to a client via project:update --client.
|
|
930
|
+
|
|
931
|
+
Monorepo invocation:
|
|
932
|
+
|
|
933
|
+
bash
|
|
934
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:get <clientId>
|
|
935
|
+
|
|
936
|
+
Tenant invocation:
|
|
937
|
+
|
|
938
|
+
bash
|
|
939
|
+
pnpm exec elevasis-sdk client:get <clientId>
|
|
940
|
+
|
|
941
|
+
Key flags:
|
|
942
|
+
|
|
943
|
+
- --pretty -- pretty-print JSON output
|
|
944
|
+
|
|
945
|
+
Drill-down recipe:
|
|
946
|
+
|
|
947
|
+
1. Obtain <clientId> from client:list or client:resolve.
|
|
948
|
+
2. Run client:get <clientId> --pretty.
|
|
949
|
+
3. Check projects array to confirm which projects are linked.
|
|
950
|
+
4. If projects is empty and a linkage was expected, run project:update <projectId> --client <clientId> to attach it.
|
|
951
|
+
5. Re-run client:get to verify the lineage updated.
|
|
952
|
+
|
|
953
|
+
client:status
|
|
954
|
+
|
|
955
|
+
Returns an org-wide rollup of client counts grouped by status.
|
|
956
|
+
|
|
957
|
+
Purpose: High-level health check -- how many clients are active, how many are in each pipeline stage -- without enumerating every record.
|
|
958
|
+
|
|
959
|
+
Monorepo invocation:
|
|
960
|
+
|
|
961
|
+
bash
|
|
962
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:status
|
|
963
|
+
|
|
964
|
+
Tenant invocation:
|
|
965
|
+
|
|
966
|
+
bash
|
|
967
|
+
pnpm exec elevasis-sdk client:status
|
|
968
|
+
|
|
969
|
+
Key flags:
|
|
970
|
+
|
|
971
|
+
- --pretty -- pretty-print JSON output
|
|
972
|
+
|
|
973
|
+
Drill-down recipe:
|
|
974
|
+
|
|
975
|
+
1. Run client:status --pretty to see counts per status bucket.
|
|
976
|
+
2. If a bucket looks unexpectedly large or small, run client:list --status <value> to enumerate the records in that bucket.
|
|
977
|
+
3. Use client:get <id> on specific records to investigate lineage gaps.
|
|
978
|
+
|
|
979
|
+
client:resolve
|
|
980
|
+
|
|
981
|
+
Fuzzy-resolves a client name to a client ID.
|
|
982
|
+
|
|
983
|
+
Purpose: Convert a partial or approximate name to the canonical UUID before passing --client to project commands. Mirrors project:resolve in shape.
|
|
984
|
+
|
|
985
|
+
Monorepo invocation:
|
|
986
|
+
|
|
987
|
+
bash
|
|
988
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:resolve "<query>"
|
|
989
|
+
|
|
990
|
+
Tenant invocation:
|
|
991
|
+
|
|
992
|
+
bash
|
|
993
|
+
pnpm exec elevasis-sdk client:resolve "<query>"
|
|
994
|
+
|
|
995
|
+
Key flags:
|
|
996
|
+
|
|
997
|
+
None beyond the positional <query> argument. Output is the resolved client ID (or a ranked list if multiple matches exist).
|
|
998
|
+
|
|
999
|
+
Drill-down recipe:
|
|
1000
|
+
|
|
1001
|
+
1. Run client:resolve "partial name" -- the command returns the best-match client ID.
|
|
1002
|
+
2. Pass the returned ID to project:create --client <id>, project:update --client <id>, or project:list --client <id>.
|
|
1003
|
+
3. If multiple matches are returned, refine the query or use client:list --search "<term>" to inspect candidates before committing.
|
|
1004
|
+
|
|
1005
|
+
client:create
|
|
1006
|
+
|
|
1007
|
+
Creates a new client record for the authenticated organization.
|
|
1008
|
+
|
|
1009
|
+
Purpose: Provision a client directly from the CLI -- useful for scripted onboarding or bulk imports where no source deal exists yet. The only required field is --name; all relationship IDs are optional and can be set later via client:update.
|
|
1010
|
+
|
|
1011
|
+
Monorepo invocation:
|
|
1012
|
+
|
|
1013
|
+
bash
|
|
1014
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:create --name "Acme Corp"
|
|
1015
|
+
|
|
1016
|
+
Tenant invocation (from inside operations/):
|
|
1017
|
+
|
|
1018
|
+
bash
|
|
1019
|
+
pnpm exec elevasis-sdk client:create --name "Acme Corp"
|
|
1020
|
+
|
|
1021
|
+
Key flags:
|
|
1022
|
+
|
|
1023
|
+
- --name <name> -- (required) client display name
|
|
1024
|
+
- --status <value> -- initial status: active | onboarding | paused | completed | churned
|
|
1025
|
+
- --source-deal-id <uuid> -- UUID of the originating deal
|
|
1026
|
+
- --primary-company-id <uuid> -- UUID of the primary company record
|
|
1027
|
+
- --primary-contact-id <uuid> -- UUID of the primary contact record
|
|
1028
|
+
- --metadata <json> -- arbitrary metadata as a JSON string (e.g. '{"tier":"enterprise"}')
|
|
1029
|
+
- --pretty -- render a human-readable summary instead of raw JSON
|
|
1030
|
+
|
|
1031
|
+
Drill-down recipe:
|
|
1032
|
+
|
|
1033
|
+
1. Run client:create --name "Acme Corp" --status onboarding --pretty to create and confirm the new record.
|
|
1034
|
+
2. Copy the ID from the pretty output (or id from raw JSON) for subsequent commands.
|
|
1035
|
+
3. Run client:get <id> --pretty to verify the full lineage payload was initialized correctly.
|
|
1036
|
+
4. If a source deal exists, pass --source-deal-id <uuid> at create time or backfill with client:update <id> --source-deal-id <uuid>.
|
|
1037
|
+
|
|
1038
|
+
client:update
|
|
1039
|
+
|
|
1040
|
+
Updates one or more fields on an existing client. Accepts a UUID or fuzzy name as the \\<id\\> argument.
|
|
1041
|
+
|
|
1042
|
+
Purpose: Rename a client, change its status, swap or clear relationship IDs, or patch arbitrary metadata -- without leaving the CLI. The command enforces a client-side "at-least-one-field" guard and rejects mutually exclusive flag pairs before making any API call.
|
|
1043
|
+
|
|
1044
|
+
Monorepo invocation:
|
|
1045
|
+
|
|
1046
|
+
bash
|
|
1047
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:update <id> --status active
|
|
1048
|
+
|
|
1049
|
+
Tenant invocation:
|
|
1050
|
+
|
|
1051
|
+
bash
|
|
1052
|
+
pnpm exec elevasis-sdk client:update <id> --status active
|
|
1053
|
+
|
|
1054
|
+
Key flags:
|
|
1055
|
+
|
|
1056
|
+
- --name <name> -- new client display name
|
|
1057
|
+
- --status <value> -- new status: active | onboarding | paused | completed | churned
|
|
1058
|
+
- --source-deal-id <uuid> -- set or replace the source deal link
|
|
1059
|
+
- --clear-source-deal -- remove the source deal link (sets sourceDealId to null; mutually exclusive with --source-deal-id)
|
|
1060
|
+
- --primary-company-id <uuid> -- set or replace the primary company
|
|
1061
|
+
- --clear-primary-company -- remove the primary company link (mutually exclusive with --primary-company-id)
|
|
1062
|
+
- --primary-contact-id <uuid> -- set or replace the primary contact
|
|
1063
|
+
- --clear-primary-contact -- remove the primary contact link (mutually exclusive with --primary-contact-id)
|
|
1064
|
+
- --metadata <json> -- replace metadata with the provided JSON string
|
|
1065
|
+
- --pretty -- render a human-readable summary instead of raw JSON
|
|
1066
|
+
|
|
1067
|
+
Drill-down recipe:
|
|
1068
|
+
|
|
1069
|
+
1. Obtain \\<id\\> from client:list, client:resolve, or the output of client:create.
|
|
1070
|
+
2. Pass only the fields to change -- the command patches rather than replaces the record.
|
|
1071
|
+
3. To unlink a relationship (e.g. remove the source deal), use --clear-source-deal rather than omitting the flag; omitting leaves the existing value unchanged.
|
|
1072
|
+
4. After update, run client:get <id> --pretty to confirm all fields reflect the expected state.
|
|
1073
|
+
5. If the command exits with CONFLICTINGFLAGS on stderr, you passed both a set flag and its --clear- counterpart -- remove one and retry.
|
|
1074
|
+
|
|
1075
|
+
client:delete
|
|
1076
|
+
|
|
1077
|
+
Deletes a client record. Accepts a UUID or fuzzy name as the \\<id\\> argument.
|
|
1078
|
+
|
|
1079
|
+
Purpose: Remove a client that was created in error or is no longer relevant. The API enforces a 409 Conflict when the client has linked rows (deals, projects, companies, contacts). The CLI surfaces the API error message verbatim so you know which links to resolve first.
|
|
1080
|
+
|
|
1081
|
+
Monorepo invocation:
|
|
1082
|
+
|
|
1083
|
+
bash
|
|
1084
|
+
pnpm -C packages/elevasis-operations exec elevasis-sdk client:delete <id>
|
|
1085
|
+
|
|
1086
|
+
Tenant invocation:
|
|
1087
|
+
|
|
1088
|
+
bash
|
|
1089
|
+
pnpm exec elevasis-sdk client:delete <id>
|
|
1090
|
+
|
|
1091
|
+
Key flags:
|
|
1092
|
+
|
|
1093
|
+
- --pretty -- render a human-readable confirmation instead of raw JSON
|
|
1094
|
+
- --api-url <url> -- override the API base URL (advanced; rarely needed)
|
|
1095
|
+
|
|
1096
|
+
Drill-down recipe:
|
|
1097
|
+
|
|
1098
|
+
1. Run client:get <id> --pretty to confirm which projects and deal links are attached before attempting deletion.
|
|
1099
|
+
2. Run client:delete <id> --pretty.
|
|
1100
|
+
3. If the API returns a 409, the error message lists the linked record counts (deals, projects, companies, contacts). Unlink or reassign those records first:
|
|
1101
|
+
- Projects: project:update <projectId> --clear-client (or reassign with --client <otherId>)
|
|
1102
|
+
- Deals: handled via the acquisition domain; no dedicated CLI command today
|
|
1103
|
+
4. Retry client:delete <id> --pretty once all links are resolved.
|
|
1104
|
+
5. Confirm deletion with client:list --search "<name>" -- the record should no longer appear.
|
|
1105
|
+
|
|
1106
|
+
Typical Workflow
|
|
1107
|
+
|
|
1108
|
+
A common session combining read and write commands:
|
|
1109
|
+
|
|
1110
|
+
1. client:status --pretty -- check org-wide distribution.
|
|
1111
|
+
2. client:resolve "Acme" -- get the Acme client ID.
|
|
1112
|
+
3. client:get <id> --pretty -- confirm lineage (deal, company, contact, projects).
|
|
1113
|
+
4. project:update <projectId> --client <id> -- link a project if missing.
|
|
1114
|
+
5. client:get <id> --pretty -- verify the linkage appears in the projects array.
|
|
1115
|
+
6. client:create --name "New Corp" --status onboarding --pretty -- provision a new client.
|
|
1116
|
+
7. client:update <id> --status active --pretty -- transition a client to active.
|
|
1117
|
+
8. client:delete <id> --pretty -- remove a client (API rejects with 409 if linked rows exist).`
|
|
1118
|
+
},
|
|
1531
1119
|
{
|
|
1532
1120
|
id: "knowledge.youtube-obs-recording-setup",
|
|
1533
1121
|
title: "YouTube OBS Recording Setup",
|
|
@@ -1786,216 +1374,106 @@ Pre-Recording Checklist
|
|
|
1786
1374
|
bodyText: "Overview\n\nElevasis finance operations run through Xero, with Stripe handling payment collection. Xero is the single source of truth for financial records: business checking bank feeds, Stripe payouts, contractor payments, expenses, invoices, receivables, and year-end exports.\n\nThe finance loop has three connected parts:\n\n1. Invoicing captures client revenue through monthly retainers and Stripe Checkout.\n2. Accounting records and reconciles bank transactions, Stripe payouts, contractor payments, and operating expenses.\n3. Taxes use accurate Xero records for quarterly estimates, deductions, 1099s, and annual filing.\n\nAccounting and Reconciliation\n\nReconcile bank transactions in Xero weekly. Match each bank feed entry to its real-world source before month end.\n\n- Stripe payouts should match against Stripe bank feed activity.\n- Contractor payments should match checking account transfers.\n- Software subscriptions such as Railway, Vercel, Supabase, WorkOS, and OpenAI should be categorized as operating expenses.\n- Unmatched or ambiguous transactions should be flagged for manual review before monthly close.\n\nMaintain the core chart of accounts around revenue, cost of sales, operating expenses, and owner draws. Revenue includes client retainers and one-time project fees. Cost of sales covers contractor labor directly tied to client work. Operating expenses cover SaaS tools, hosting, banking fees, and professional services. Owner draws track business distributions.\n\nConfigure Xero with the connected business checking account, the default tax rate for the business jurisdiction, and the correct financial year end in organization settings.\n\nInvoicing and Accounts Receivable\n\nClient billing runs on a monthly retainer model. Create invoices in Xero at the start of each billing period, send them by Xero email or Stripe Checkout link, record payment after Stripe confirms checkout completion, and reconcile the Stripe payout in Xero.\n\nUse Xero repeating invoices for monthly retainers:\n\n- Frequency: monthly.\n- Start date: billing cycle start date.\n- Approval: automatic approval where the billing terms are stable.\n\nConfigure invoice reminders around the due date: a courtesy reminder 3 days before due date, an overdue notice 1 day after due date, and an escalation notice 7 days after due date.\n\nFor invoices more than 14 days overdue, contact the client directly through the active communication channel, pause active work pending payment confirmation, and resolve the balance before the next billing cycle.\n\nTaxes\n\nTax work depends on accurate Xero records throughout the year. As a pass-through entity, Elevasis business income flows to the owner personal return, so quarterly estimates reduce underpayment risk and year-end exports should be clean enough for a CPA or tax preparer.\n\nQuarterly estimated payment targets:\n\n- Q1: April 15.\n- Q2: June 15.\n- Q3: September 15.\n- Q4: January 15 of the following year.\n\nEstimate payments as roughly 25-30% of net profit for the quarter and pay via IRS Direct Pay or EFTPS.\n\nTrack deductible expenses in Xero during the year: SaaS subscriptions, contractor payments, home office expenses where applicable, professional development, courses, and business banking fees. Keep digital receipts organized by year in the business records folder.\n\nIssue 1099-NEC forms to US-based contractors paid more than $600 in a calendar year. Collect a W-9 before first payment, track annual contractor totals in Xero, file 1099-NEC forms by January 31 of the following year, and file the 1096 summary with the IRS when required.\n\nAt year end, export the Xero profit and loss statement and balance sheet. Provide them to the CPA or tax preparer with issued 1099s, bank statements for the business year, mileage logs if applicable, and home office documentation if applicable."
|
|
1787
1375
|
},
|
|
1788
1376
|
{
|
|
1789
|
-
id: "knowledge.
|
|
1790
|
-
title: "
|
|
1791
|
-
summary: "
|
|
1792
|
-
bodyText: "Overview\n\nCommand View visualizes an organization's automation landscape: agents, workflows, triggers, integrations, external resources, and human checkpoints, plus the relationships between them.\n\nIt answers operational questions such as:\n\n- What does this agent talk to?\n- If a provider goes down, what breaks?\n- What is connected to the CRM?\n- Which resources are hidden, diagnostic-only, or currently relevant to this trace?\n\nAccess Command View through the Knowledge area at /knowledge/command-view.\n\nHow Command View Works\n\nCommand View is a visualization layer over the organization deployment manifest.\n\ntypescript\nconst yourOrgRegistry: DeploymentSpec = {\n agents: [],\n workflows: [],\n triggers: [],\n integrations: [],\n humanCheckpoints: [],\n relationships: {},\n externalResources: []\n}\n\nArchitecture:\n\n1. Manifest declares resources and relationships.\n2. SDK deploy and API registration validate declared references.\n3. Pre-serialization computes graph data once.\n4. Frontend visualizes cached graph data.\n\nCommand View does not create routing. It displays declarations and runtime topology data.\n\nNode Types\n\n| Type | Purpose | Badge info |\n| --- | --- | --- |\n| Triggers | Entry points such as webhook, schedule, manual, or event triggers | Trigger type |\n| Agents | Autonomous AI resources | Tool count, Knowledge Map, memory |\n| Workflows | Multi-step orchestrations | Step count |\n| Integrations | External service connections | Connection status |\n| External resources | Third-party platforms such as n8n, Make, or Zapier | Platform name |\n| Human checkpoints | Human decision points | Approval required |\n\nNode badges:\n\n- Agents show tool count, Knowledge Map, and memory.\n- Workflows show step count.\n- Integrations show connection status.\n- Triggers show webhook, schedule, manual, or event type.\n- External resources show platform.\n\nEdge Types\n\n| Type | Meaning |\n| --- | --- |\n| Triggers | A trigger initiates a resource |\n| Invokes | A resource calls another resource |\n| Uses | A resource uses an integration |\n| Approval | A resource requests human approval |\n\nDeclaring Relationships\n\nInternal Resources\n\nUse relationships to declare what agents and workflows invoke or use.\n\ntypescript\nconst relationships: ResourceRelationships = {\n 'executive-agent': {\n triggers: {\n workflows: ['report-workflow'],\n agents: ['research-agent']\n },\n uses: {\n integrations: ['integration-gmail', 'integration-attio']\n }\n }\n}\n\nTriggers\n\nTriggers declare their own metadata, while routing is declared through relationships.\n\ntypescript\nconst triggers: TriggerDefinition[] = [\n {\n resourceId: 'webhook-customer-created',\n type: 'trigger',\n triggerType: 'webhook',\n name: 'Customer Created Webhook',\n description: 'Triggers when a customer is created in Shopify',\n status: 'prod',\n webhookPath: '/webhooks/customer-created'\n }\n]\n\nTrigger routing is not configured on the trigger object itself.\n\nExternal Resources\n\nExternal resources can declare what they trigger and which integrations they use.\n\ntypescript\nconst externalResources: ExternalResourceDefinition[] = [\n {\n resourceId: 'external-n8n-sync',\n type: 'external',\n platform: 'n8n',\n name: 'N8N Product Sync',\n description: 'Syncs product data from Shopify to internal database',\n status: 'prod',\n triggers: { workflows: ['internal-sync-workflow'] },\n uses: { integrations: ['integration-shopify'] }\n }\n]\n\nThese declarations document the relationship. They do not programmatically connect the external platform.\n\nHuman Checkpoints\n\nHuman checkpoints declare which resources request approval and where approval can route.\n\ntypescript\nconst humanCheckpoints: HumanCheckpointDefinition[] = [\n {\n resourceId: 'approval-sales',\n name: 'Sales Approval Queue',\n description: 'Human approval for sales proposals',\n status: 'prod',\n requestedBy: { agents: ['sales-agent'] },\n routesTo: { workflows: ['send-proposal-workflow'] }\n }\n]\n\nActual human approval decisions are runtime choices, not automatic routing from the declaration alone.\n\nValidation\n\nCommand View shows relationships validated during deploy and API registration.\n\nValidation checks:\n\n- Referenced resource IDs exist.\n- Agent and workflow IDs are unique.\n- External resource references are valid.\n- Human checkpoint routes exist.\n- Model configs have valid providers, models, and API key references.\n\nThese checks cover declared relationships and registry consistency. They do not infer every runtime execution.trigger(...) target, especially when the target is computed dynamically.\n\nWhen validation errors occur, API startup fails with a descriptive error such as:\n\ntext\nRegistryValidationError: [YourOrg] Resource 'my-agent' uses non-existent integration: integration-attio\n\nFix validation errors by updating the manifest to reference the correct IDs or by adding the missing resource definition.\n\nUsing Command View\n\nVisualization Features\n\nCommand View uses Cytoscape graph modes:\n\n- Map: keeps organization structure visible and honors hidden-resource visibility.\n- Trace: resolves directed paths and reveals resources for the active trace.\n- Impact: pivots around the selected node and reveals related resources for impact review.\n\nFilter panel capabilities:\n\n- Search across nodes, relationships, and IDs.\n- Filter by node kind, resource type, environment, topology presence, integrations, and resource facets.\n- Toggle resource visibility and diagnostic/testing resource visibility.\n- Show visible and hidden resource counts in the same control surface.\n\nHidden resource behavior:\n\n- Resource nodes are hidden by default so the organization structure remains readable.\n- Structural nodes remain visible as the navigation skeleton.\n- Selecting a visible node can reveal directly connected hidden resources.\n- Diagnostic and testing resources are hidden by default and can be revealed from the filter panel.\n\nExpand Around behavior:\n\n- Select a node, open Details, and use Expand Around to preview nearby graph context before revealing it.\n- Semantic presets cover coverage, operational dependencies, organization context, and impact paths.\n- Feature, surface, entity, and capability nodes default to Coverage.\n- Resource nodes default to Operational Dependencies.\n- Preview counts show hidden resources before Apply reveals the graph patch.\n\nRead-only constraints:\n\n- No drag and drop.\n- No connection editing.\n- No graph mutation from the visualization layer.\n\nManifest Debugging\n\nUse Command View to verify:\n\n1. Resources appear as nodes.\n2. Relationships show as edges.\n3. Integration connections exist.\n4. Trigger invocations are declared correctly.\n5. Orphaned resources are intentional.\n\nCommon issues:\n\n- Typo in a resource ID reference.\n- Missing relationship declaration.\n- Incorrect integration credential name.\n- External resource missing a bidirectional declaration.\n\nSystem Understanding\n\nUseful exploration questions:\n\n- What triggers this workflow?\n- What integrations does this agent use?\n- What happens after this approval?\n- If this integration fails, what breaks?\n\nVisual patterns:\n\n- Hub nodes with many connections indicate critical resources.\n- Linear chains show sequential workflows.\n- Branching shows conditional logic.\n- Isolated nodes may indicate unused resources.\n\nTroubleshooting\n\nEmpty Graph\n\nLikely causes:\n\n- Organization has no resources defined.\n- Search, environment, node kind, topology, or resource facet filters hide all graph elements.\n- Command View resources are hidden and no structural nodes match current filters.\n- Validation errors prevented serialization.\n\nCheck:\n\n1. API logs for validation errors.\n2. Network tab for the /api/command-view response.\n3. Manifest resources arrays.\n4. Filter panel visibility and filter state.\n\nFix:\n\n- Add resources to the manifest.\n- Fix validation errors.\n- Reveal resources or reset graph filters.\n\nMissing Edges\n\nLikely causes:\n\n- Relationship is not declared in the manifest.\n- Resource ID reference has a typo.\n- Validation passed but the relationship declaration is incomplete.\n\nCheck:\n\n1. Manifest relationships field.\n2. Resource ID spelling and case.\n3. API response includes both nodes.\n\nFix the relationship declaration in the manifest.\n\nNode Not Appearing\n\nLikely causes:\n\n- Resource is not in the correct manifest array.\n- Resource is hidden by visibility controls.\n- Status, resource type, topology, or facet filters exclude the node.\n- Duplicate ID caused validation failure.\n\nCheck:\n\n1. Manifest includes the resource in the correct array.\n2. Filter panel visibility and filter state.\n3. Resource status field.\n4. API logs for duplicate ID errors.\n\nFix by adding the resource, revealing resources, or resetting filters.\n\nValidation Errors on Startup\n\nCommon error shape:\n\ntext\nRegistryValidationError: [OrgName] Resource 'resource-id' uses non-existent integration: integration-name\n\nCommon causes:\n\n- Typo in integration credential name.\n- Resource referenced before definition.\n- Missing import.\n\nFix:\n\n- Verify credential names match integration definitions.\n- Ensure all resources exist in manifest arrays.\n- Check module dependency statements in the manifest module.\n\nBest Practices\n\nKeep relationships current when resources change:\n\n1. Add new tools, then update uses.integrations.\n2. Add workflow invocation, then update triggers.workflows.\n3. Add agent delegation, then update triggers.agents.\n4. Add approval gate, then update the human checkpoint requestedBy.\n\nUse descriptive IDs:\n\n- integration-shopify-store-alpha instead of integration-1.\n- trigger-customer-created-webhook instead of trigger-webhook.\n- approval-sales-proposals instead of approval-1.\n\nValidate early and often:\n\nbash\npnpm dev:api\n\nAPI startup runs registry validation. Fix validation errors immediately because they prevent graph visualization.\n\nReview Command View regularly to onboard team members, plan new features, trace execution paths, and audit integration dependencies.\n\nRelationship Enforcement\n\nRelationships are declarations, not routing.\n\n| Layer | What it does | Enforcement |\n| --- | --- | --- |\n| DeploymentSpec.relationships | Declares relationships such as workflow A triggers workflow B | Validated at startup so IDs must exist |\n| buildEdges() in serialization.ts | Serializes declarations into CommandViewEdge[] | Pure transformation, no runtime check |\n| Runtime workflow and agent code | Calls resources through execution.trigger(...) or equivalent wiring | Independent of declarations |\n\nThe Resource Registry validates that declared targets reference real resource IDs during deploy and registration. However, there is no guarantee that declared graph relationships match runtime reality. Code can call a resource that is not declared in relationships, and that runtime call will not fail solely because the declaration is missing.\n\nEnforcement status:\n\n| Category | Validated at startup | Matches runtime | Status |\n| --- | --- | --- | --- |\n| Resource IDs unique | Yes | Yes, because lookup works | Enforced |\n| Input schema matches execution interface | Yes | Yes, because validation uses it | Enforced |\n| Relationship targets exist | Yes | No, invocations can be dynamic strings | Partially enforced |\n| Trigger routing | No | No, webhook handlers can hardcode routes | Decorative |\n| Tool availability per agent | No | No, tools are built dynamically by factories | Decorative |\n| Model config | Yes | No, runtime selects from environment variables | Decorative |\n| Integration usage | Partially, IDs checked | No, credentials resolve at runtime | Decorative |\n\nNon-Executable Node Types\n\nTriggers, integrations, external resources, and human checkpoints are non-executable node types. They exist for Command View.\n\n- TriggerDefinition does not set up actual routing. Webhook routing is independent.\n- IntegrationDefinition is a credentials reference with provider and credentialName; it does not connect to a provider by itself.\n- ExternalResourceDefinition documents a third-party automation but has no programmatic link to that platform.\n- HumanCheckpointDefinition documents approval queues and routes. Actual routing is determined by human choice and workflow code.\n\nRuntime Relationship Guard\n\nThere is no general runtime guard that discovers every nested execution target at call time. Deployed SDK workers can invoke nested resources through execution.trigger(...) and the API dispatcher, so dynamic targets can drift from DeploymentSpec.relationships even when deploy-time validation passes.\n\nDeploy-Time Validation\n\nResources are deployed through elevasis-sdk deploy. The SDK includes relationships in DeployMetadata, validates the resulting DeploymentSpec locally, and the API validates the merged deployment before registerOrganization().\n\nKey files:\n\n- packages/sdk/src/cli/commands/deploy.ts\n- apps/api/src/deployments/service.ts\n- packages/core/src/platform/registry/validation.ts\n\nAccepted Decorative Gaps\n\nThese gaps are inherent to the architecture and cannot be fully validated at deploy time:\n\n- Runtime invocation drift, especially with computed target IDs.\n- Tool declarations, because agents build tools dynamically by factory.\n- Model config, because runtime LLM calls use environment variables.\n- External resource triggers, because routing lives in external platforms.\n\nRelated References\n\n- /knowledge read knowledge.platform-composition-patterns\n- /knowledge read knowledge.platform-integration-patterns\n- /knowledge read knowledge.platform-capabilities"
|
|
1793
|
-
},
|
|
1794
|
-
{
|
|
1795
|
-
id: "knowledge.platform-composition-patterns",
|
|
1796
|
-
title: "Platform Composition Patterns",
|
|
1797
|
-
summary: "Reference for composing agents, workflows, integrations, tools, observability, scaling, error handling, and security into complete Elevasis automation systems.",
|
|
1798
|
-
bodyText: "Overview\n\nUse this reference to choose how Elevasis resources compose into complete systems. The core decision is whether reasoning, deterministic orchestration, or a hybrid of both should own the business flow.\n\nThree primary patterns exist:\n\n- Agent-centric: an agent is the primary orchestrator, and workflows or integrations are tools.\n- Workflow-centric: a workflow is the deterministic pipeline, and agents appear only as processing steps.\n- Hybrid: agents decide what should happen, and workflows execute reliable sequences.\n\nSystem Architecture Patterns\n\nAgent-Centric Systems\n\nPattern: Agent is the primary orchestrator; workflows and integrations are tools.\n\ntext\nTrigger to Agent\n |- Tool: Integration A\n |- Tool: Integration B\n |- Tool: Workflow 1 via resource invocation\n - Tool: Workflow 2 via resource invocation\n\nUse this pattern for ambiguous goals, dynamic decision-making, multi-turn conversations, and business logic that requires judgment.\n\nStrengths:\n\n- Flexible and adaptive.\n- Handles ambiguity well.\n- Can delegate to specialist resources.\n- Supports natural language interaction.\n\nWeaknesses:\n\n- Token cost per execution.\n- Non-deterministic LLM variance.\n- Requires strong observability for debugging.\n- Usually slower than deterministic workflows.\n\nWorkflow-Centric Systems\n\nPattern: Workflow is the pipeline; agents are processing steps.\n\ntext\nTrigger to Workflow\n |- Step 1: Integration A reads data\n |- Step 2: Agent processes or reasons\n |- Step 3: Integration B writes result\n - Step 4: Conditional routing\n\nUse this pattern for predictable sequences, data transformation pipelines, integration orchestration, and fixed business logic.\n\nStrengths:\n\n- Deterministic and reliable.\n- Fast execution.\n- Easy to debug step-by-step.\n- No token cost unless an agent step is included.\n\nWeaknesses:\n\n- Rigid, because steps are predefined.\n- Does not handle ambiguity well.\n- Requires code changes for logic updates.\n\nHybrid Systems\n\nPattern: Agents decide, workflows execute. This is the recommended default for complex automation.\n\ntext\nTrigger to Agent decides what to do\n |- Invokes: Workflow A\n |- Invokes: Workflow B\n - Uses: Integration C directly\n\nWorkflow A:\n |- Integration D reads\n |- Agent processes a specific subtask\n - Integration E writes\n\nUse this pattern when the system needs variable paths, reasoning, reliability, and a mix of predictable and unpredictable steps.\n\nStrengths:\n\n- Flexible where needed and reliable where possible.\n- Cost-efficient because workflows do not use tokens by default.\n- Good observability across agent reasoning and workflow steps.\n\nResource Composition Patterns\n\nAgent + Tools\n\nUse direct integration access for simple or frequent operations.\n\nTool categories:\n\n- Base tools: always available, high-frequency operations such as read queries or cached data access. Example: attiogetrecord.\n- Knowledge Map tools: lazy-loaded, low-frequency operations such as write operations or domain-specific capabilities. Examples: attiocreatecontact, dropboxuploadfile.\n- Resource invocation tools: delegate to workflows for deterministic sequences or call specialist agents.\n\nTool creation example:\n\ntypescript\nconst tool = createAttioCreateRecordTool('elevasis-attio')\n\nCredential names resolve to organization-scoped storage. RLS policies enforce organization isolation, credentials are encrypted at rest, and OAuth tokens can refresh automatically.\n\nAgent + Workflow\n\nUse an agent for reasoning and a workflow for deterministic execution.\n\ntext\nTrigger to Agent reasoning\n |- Direct tool: read integration\n |- Invoke tool: Workflow A\n - Invoke tool: Workflow B\n\nExecution flow:\n\n1. Agent analyzes the request.\n2. Agent invokes a workflow through a tool call.\n3. Workflow executes deterministic steps without token cost.\n4. Agent explains the result to the user.\n\nUse this pattern when the system needs both reasoning and reliability, has multiple execution paths, or should minimize token use by moving predictable work into workflows.\n\nWorkflow + Integration\n\nUse a workflow to orchestrate deterministic integration sequences.\n\ntext\nTrigger to Workflow\n |- Step 1: Integration A reads\n |- Step 2: Transform pure function\n |- Step 3: Integration B writes\n - Step 4: Notify\n\nUse this pattern for predictable step sequences, data transformation pipelines, integration orchestration, and cost-sensitive work that does not need LLM reasoning.\n\nPattern Selection Guide\n\n| Requirement | Pattern | Example |\n| --- | --- | --- |\n| Ambiguous goals | Agent-centric | Business orchestration with variable outcomes |\n| Predictable sequence | Workflow-centric | Shopify to CRM sync |\n| Hybrid needs | Agent + workflow | Support router where agent decides and workflow executes |\n| \\>10 tools | Knowledge Map | Agent with many domain capabilities |\n| High-frequency reads | Base tools | Attio read operations |\n| Low-frequency writes | Lazy-loaded tools | CRM updates or page creation |\n| External services | Integration tools | Attio, Gmail, Google Sheets |\n| Deterministic pipelines | Workflow + integration | Data transformation |\n\nComplexity guidelines:\n\n- Simple, 1-2 resources: agent with base tools, or workflow with 1-2 integrations.\n- Medium, 3-5 resources: agent plus Knowledge Map with 2-3 nodes, or workflow plus an agent step.\n- Complex, 6+ resources: agent plus Knowledge Map plus memory preload, or multi-integration workflows.\n\nCross-Cutting Concerns\n\nObservability\n\nTrack these surfaces:\n\n- Agent iterations, including reasoning and actions.\n- Workflow steps, including inputs and outputs.\n- Tool calls, including integration requests and responses.\n- Errors and retries.\n- Token usage and cost.\n\nPrimary tools:\n\n- Execution Logs for SSE-based debugging.\n- Activity Log for the real-time stream.\n- Execution Health for cost tracking and ROI review.\n- Command View for system architecture and dependency review.\n\nCost Tracking\n\nAgent cost comes from per-iteration token usage, model pricing, and tool execution cost. Workflow cost is usually zero token cost plus any external integration API fees.\n\nOptimization rules:\n\n- Use workflows for predictable tasks.\n- Lazy-load tools for 80-95% token savings.\n- Cache frequently accessed stable data.\n- Choose the appropriate model per task.\n\nScaling Strategies\n\nHorizontal scaling:\n\n- Keep agent execution stateless.\n- Store sessions in the database.\n- Let a load balancer distribute requests.\n\nVertical optimization:\n\n- Preload memory to reduce iteration count.\n- Lazy-load tools to reduce token usage.\n- Batch tool operations where possible.\n- Use parallel integration calls when the steps are independent.\n\nError Handling\n\nAgent error handling:\n\n- Tool errors are returned to the agent so it can reason about recovery.\n- Iteration limits prevent infinite loops.\n- Timeout protection constrains long-running tools and executions.\n- Graceful degradation explains what failed.\n\nWorkflow error handling:\n\n- Step retry logic uses exponential backoff, commonly 1, 4, and 9 minutes.\n- Conditional routing can send failures to an alternative step.\n- Manual intervention can route work to a human checkpoint.\n- Idempotent steps should be safe to retry.\n\nExample:\n\ntypescript\n{\n id: 'sync-to-crm',\n handler: async ({ input, context }) => {\n try {\n return await crmClient.createContact(input)\n } catch (error) {\n if (error.code === 'DUPLICATE') {\n return crmClient.updateContact(input)\n }\n\n throw error\n }\n },\n next: { type: 'linear', target: 'send-notification' }\n}\n\nSecurity\n\nCredential management:\n\n- Store credentials in the credentials table.\n- Encrypt values at rest.\n- Scope records by organization with RLS policies.\n- Refresh OAuth tokens automatically where supported.\n- Never store API keys in code.\n\nMulti-tenancy isolation layers:\n\n1. Database RLS on tenant-scoped tables.\n2. API middleware that requires organization context.\n3. Resource registry lookups scoped to the organization.\n4. Command View filtered by organization.\n\nBest Practices\n\n1. Start with a single agent or workflow, then add complexity only when needed.\n2. Build the working version before optimizing for caching or lazy loading.\n3. Put domain logic in agents and execution flow in workflows.\n4. Lazy-load large or low-frequency tool groups.\n5. Cache stable data, not fast-changing operational state.\n\nCommon mistakes:\n\n- Creating a Knowledge Map for fewer than five tools.\n- Using a workflow for one integration call when an agent tool would be simpler.\n- Loading more than ten tools as base tools.\n- Putting domain logic into workflows where it becomes hard to test and adapt.\n- Mixing unrelated concerns in one knowledge node.\n\nRelated References\n\n- /knowledge read knowledge.platform-integration-patterns\n- /knowledge read knowledge.platform-command-view"
|
|
1799
|
-
},
|
|
1800
|
-
{
|
|
1801
|
-
id: "knowledge.platform-integration-patterns",
|
|
1802
|
-
title: "Platform Integration Patterns",
|
|
1803
|
-
summary: "Reference for connecting Elevasis agents and workflows to external services through adapters, OAuth, API keys, tenant-isolated credentials, retries, and tests.",
|
|
1804
|
-
bodyText: "Overview\n\nIntegration patterns define how Elevasis agents and workflows connect to external services such as Gmail, Attio, Google Sheets, and custom APIs. The platform uses a standardized adapter pattern with OAuth 2.0 and API key authentication backed by tenant-isolated credentials.\n\nCore patterns:\n\n- Direct integration, where a resource uses integration tools directly.\n- Integration workflow, where a workflow coordinates multiple integrations.\n- Shared integration, where multiple resources use the same credential set.\n- Multi-account integration, where the same provider has separate credentials for different contexts.\n\nDirect Integration Pattern\n\nPattern: Resource uses an integration tool.\n\nUse direct integration tools when an agent or workflow performs frequent operations against a provider.\n\nCharacteristics:\n\n- Tools are always available to the resource.\n- There is no additional navigation overhead.\n- The pattern is optimized for frequent read-heavy operations.\n\nTool factory location:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/{provider}-tools.ts\n\nTool factories use createIntegrationTool() with a credentialName parameter. Example: createAttioCreateRecordTool(credentialName).\n\nIntegration Workflow Pattern\n\nPattern: Agent invokes a workflow, and the workflow uses integrations.\n\nUse an integration workflow for complex multi-step operations that need deterministic orchestration.\n\nExample lead capture flow:\n\n1. Webhook trigger receives the event.\n2. Workflow creates an Attio contact.\n3. Workflow sends a Gmail notification.\n4. Workflow returns confirmation.\n\nCharacteristics:\n\n- Deterministic execution order.\n- Step-level error handling and retry.\n- Multiple integrations coordinated in one place.\n- Built-in audit trail through workflow execution.\n\nShared Integration Pattern\n\nPattern: Multiple resources share the same integration.\n\nUse shared integrations for organization-wide providers that should use one credential set.\n\nExample:\n\ntext\npackages/elevasis-operations/src/index.ts\n\nCharacteristics:\n\n- One credential per integration.\n- Centralized credential management.\n- Shared across agents and workflows.\n\nMulti-Account Pattern\n\nPattern: Same provider, different credentials.\n\nUse this pattern for department-specific provider instances or separate dev/prod environments.\n\nExample:\n\ntypescript\nconst salesTool = createAttioCreateRecordTool('attio-sales')\nconst supportTool = createAttioCreateRecordTool('attio-support')\n\nDisambiguate multi-account tools with explicit tool names:\n\ntypescript\nconst tool = createAttioToolNamed(\n 'attiosalescreaterecord',\n 'attio-sales',\n 'createRecord'\n)\n\nAuthentication Patterns\n\nOAuth 2.0 vs API Key\n\n| Aspect | OAuth 2.0 | API key |\n| --- | --- | --- |\n| Auth flow | Browser redirect plus token exchange | Direct API key |\n| Setup complexity | High: client ID, secret, redirect URL | Low: single API key |\n| Token refresh | Automatic refresh token rotation | Manual key rotation |\n| Scope control | Granular permission scopes | Full access or none |\n| User context | Per-user authorization | Service-level access |\n| Revocation | User can revoke anytime | Manual key deletion |\n\nOAuth 2.0 Pattern\n\nProviders include Gmail and Google Sheets.\n\nCredential format:\n\ntypescript\ninterface OAuthToken {\n provider: string\n accessToken: string\n refreshToken: string\n expiresAt: string\n tokenType: 'Bearer'\n scope?: string\n}\n\nexpiresAt is an ISO 8601 timestamp. The platform handles token refresh when supported by the provider and credential implementation.\n\nAPI Key Pattern\n\nProviders include Attio and custom APIs.\n\nCredential format:\n\ntypescript\ninterface APIKeyCredentials {\n apiKey: string\n}\n\nProvider-specific validation belongs in the adapter. For Attio, see:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts\n\nCredential Management\n\nCredentials are stored in the credentials table.\n\nKey columns:\n\n- organizationid for tenant isolation.\n- name, such as elevasis-attio.\n- provider, such as gmail or attio.\n- encryptedvalue containing encrypted JSON.\n\nSecurity rules:\n\n- RLS policies enforce tenant isolation.\n- Values are encrypted at rest.\n- Credentials do not cross organization boundaries.\n\nBest practices:\n\n- Store credentials encrypted in the database.\n- Use tenant-isolated credential names.\n- Validate credentials before API calls.\n- Rotate API keys regularly.\n- Use least-privilege OAuth scopes.\n\nDo not:\n\n- Hardcode credentials in code.\n- Share credentials across organizations.\n- Log credential values.\n- Store provider API keys in environment variables for tenant integrations.\n- Reuse the same credential name for dev and prod.\n\nCredential resolution flow:\n\n1. Tool requests a credential by name.\n2. Platform queries credentials with the current organizationid.\n3. Platform decrypts the credential.\n4. Adapter validates the credential against the provider schema.\n5. Tool passes the credential to the provider adapter.\n6. Usage is logged without credential values.\n\nImplementation reference:\n\ntext\npackages/core/src/execution/engine/tools/integration/tool.ts\n\nError Handling Patterns\n\nAdapters should convert provider errors into platform tooling errors.\n\nCommon error categories:\n\n- credentialsinvalid for invalid or missing credentials.\n- apierror for provider API errors.\n- networkerror for network or timeout failures.\n- validationerror for invalid parameters.\n- methodnotfound for unknown adapter methods.\n\nRetry strategy:\n\n- Network errors: retry with exponential backoff, commonly 1, 2, and 4 seconds.\n- Auth errors: fail immediately because the credential needs attention.\n- Validation errors: fail immediately because the input needs correction.\n- Rate limits: retry after reset when the provider exposes a reset time.\n- Workflow-level retries: commonly 3 attempts with 1, 4, and 9 minute delays.\n\nAdapter Development\n\nAdapter class location:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/{provider}-adapter.ts\n\nAdapter classes implement BaseIntegrationAdapter and contain call() and validateCredentials() methods.\n\nTool factory location:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/{provider}-tools.ts\n\nTool factories export create{Provider}{Operation}Tool(credentialName) functions.\n\nTests belong under:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/tests/\n\nRegistration happens in:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/index.ts\n\nTesting Strategies\n\nUnit testing:\n\n- Mock external APIs.\n- Cover credential validation.\n- Cover API call construction.\n- Cover response parsing.\n- Cover error handling.\n\nIntegration testing:\n\n- Use real API calls only with test accounts or test workspaces.\n- Clean up test data after tests.\n- Run in CI only when secret credentials are available.\n- Keep provider behavior mocked in unit tests.\n\nAgent testing:\n\n- Test agents using integration tools at the resource boundary.\n- Tenant project agent tests normally live under external/{org}/src/agents/{agent}/tests/.\n\nRelated References\n\n- /knowledge read knowledge.platform-composition-patterns\n- /knowledge read knowledge.platform-command-view\n- /knowledge read knowledge.platform-capabilities"
|
|
1805
|
-
},
|
|
1806
|
-
{
|
|
1807
|
-
id: "knowledge.what-is-elevasis",
|
|
1808
|
-
title: "What is Elevasis",
|
|
1809
|
-
summary: "Company overview, positioning, value proposition, and pricing for the AI orchestration platform",
|
|
1810
|
-
bodyText: 'Executive Summary\n\nCompany: Bootstrapped AI orchestration platform for done-for-you business automation\n\nFounder: Solo (Alex, 34), bootstrapped\n\nMarket: Service-based SMBs that need practical AI automation without building internal AI teams\n\nGTM: Content-led marketing, proof assets, consultative sales, and land-and-expand pricing\n\nCompany Overview\n\nCurrent Status\n\nThe platform core is production-ready enough to support client-facing demonstrations and implementation work. Current business focus is client acquisition, proof-building, and turning the platform into a repeatable service delivery system.\n\nFounder Profile\n\nAlex, 34 \u2014 Solo founder with software engineering background (BSCS degree). Previous: Ecommerce platforms, Streaming technology. No formal AI background \u2014 practitioner, not academic.\n\nWhat We Are\n\nOperating layer for AI automation. We help SMBs operate with closed-loop feedback, decision capture, and continuous learning \u2014 at SMB pricing with done-for-you implementation (NOT self-service).\n\nTarget Market\n\nICP: Service-based SMBs (2-50 employees, USA, $200K-$5M revenue)\n\nCore Pain: Capacity crisis, pipeline problems, operational chaos, growth blockers \u2014 too busy serving clients to grow their own business.\n\nPricing (Land-and-Expand)\n\n- Tier 1 \u2014 Foundation (1-2 workflows): $500-2k/mo\n- Tier 2 \u2014 Scaled Operations (3-5 workflows): $2k-5k/mo\n- Tier 3 \u2014 AI-Powered Systems (6+ workflows): $5k-15k+/mo\n\nAdditional workflows cost less due to shared infrastructure.\n\nValue Proposition\n\n"We find one bottleneck in your business, build an AI solution to fix it, and you only pay if it actually saves you time or makes you money."'
|
|
1811
|
-
},
|
|
1812
|
-
{
|
|
1813
|
-
id: "knowledge.platform-capabilities",
|
|
1814
|
-
title: "Platform Capabilities",
|
|
1815
|
-
summary: "Authoritative overview of the Elevasis AI Orchestration Platform \u2014 what's built, what it does, and how the pieces fit together",
|
|
1816
|
-
bodyText: "Overview\n\nElevasis is a production AI orchestration platform that coordinates workflows, autonomous agents, and human approvals into a unified operating layer for SMBs. Everything described below is implemented and running in production.\n\nCore Architecture:\n\n| Layer | What It Does | Key Components |\n| --- | --- | --- |\n| Execution | Runs workflows and agents with schema validation | Workflow Engine, Agent Framework, Execution Runner |\n| Control | Human oversight, approvals, and decision capture | Command Queue (HITL), Dynamic Forms, Action System |\n| Intelligence | Autonomous reasoning, tool use, and memory management | ReAct Agents, Knowledge Map, Session Memory |\n| Observability | Real-time visibility into cost, performance, and health | Cost Tracking, Metrics, Activity Log, SSE Streaming |\n| Platform | Multi-tenancy, security, scheduling, integrations | Registry, RLS, Scheduler, Credential Vault, SDK |\n\nExecution Engine\n\nAI Workflows\n\nGraph-based workflow execution with schema-validated steps and conditional routing. Steps define explicit routing: linear, conditional (rule-based branching), or terminal. Context flows through the entire workflow.\n\nAutonomous Agents\n\nProduction-grade ReAct-style agents with tool use, memory management, and security hardening.\n\nLLM Provider Support: OpenAI (GPT-5), Google (Gemini 3 Flash), Anthropic (Claude Sonnet 4.5), OpenRouter (GLM-5)\n\nHuman-in-the-Loop (HITL)\n\nThe Command Queue surfaces pending approvals as structured tasks. Admin reviews, approves or edits, and the workflow continues. Every critical decision \u2014 sending emails to prospects, updating customer records, publishing content \u2014 requires human approval.\n\nKnowledge Map\n\nOrganizational knowledge loaded lazily into agent context. 80-95% token savings vs. always-loading full context. Nodes link to features, teams, and other nodes.\n\nIntegrations (13 active)\n\nAttio CRM, Cal.com, Instantly (cold email), Resend, Apify, Google Maps, Tomba, Mails.so, Supabase, Stripe, OpenAI, Google Gemini, Anthropic Claude.\n\nSDK\n\nTypeScript-based resource development with local testing, validation, and deployment pipeline. External consumers define workflows and agents in their own repos and deploy via elevasis-sdk deploy."
|
|
1817
|
-
},
|
|
1818
|
-
{
|
|
1819
|
-
id: "knowledge.client-testimonials",
|
|
1820
|
-
title: "Testimonials",
|
|
1821
|
-
summary: "Customer testimonials and case study permissions from previous client work",
|
|
1822
|
-
bodyText: `Status: 4 testimonials from 2 clients | Source: Upwork client work (2024)
|
|
1823
|
-
|
|
1824
|
-
Testimonials
|
|
1825
|
-
|
|
1826
|
-
Xero Automation \u2014 The Invoice Chase That Disappeared
|
|
1827
|
-
|
|
1828
|
-
Client: Word of Mouth Agency
|
|
1829
|
-
|
|
1830
|
-
Result: 10+ hours/week saved. Zero manual follow-up emails.
|
|
1831
|
-
|
|
1832
|
-
> "Working with Alex at Elevasis to automate our Xero follow-ups has been a game-changer. We're set to save over 5 hours a week, but more importantly, it has completely removed the stress of chasing invoices. The process was smooth and professional."
|
|
1833
|
-
|
|
1834
|
-
Permission: Case study approved with company name.
|
|
1835
|
-
|
|
1836
|
-
Influencer Discovery \u2014 From Spreadsheet Hell to Strategic Decisions
|
|
1837
|
-
|
|
1838
|
-
Client: Word of Mouth Agency (Perth, Australia)
|
|
1839
|
-
|
|
1840
|
-
Result: 10+ hours/week saved. Research scope: 50 to 200+ influencers. Team focus shifted from data to strategy.
|
|
1377
|
+
id: "knowledge.org-model-actions",
|
|
1378
|
+
title: "Organization Model Actions",
|
|
1379
|
+
summary: "Actions are the invokable semantic layer of the Organization Model -- they map to resources, affect entities, bind to knowledge, and expose four invocation types.",
|
|
1380
|
+
bodyText: `Overview
|
|
1841
1381
|
|
|
1842
|
-
|
|
1382
|
+
Actions are the invokable semantic layer of the Organization Model. They declare what an organization can do: what resources implement them, which entities they affect, and how they can be called.
|
|
1843
1383
|
|
|
1844
|
-
|
|
1384
|
+
Actions are authored in the OM.actions domain map and projected as action graph nodes by buildOrganizationGraph(). Each action emits a contains edge from the organization root and optional mapsto, affects, and triggers edges based on its declared fields.
|
|
1845
1385
|
|
|
1846
|
-
|
|
1386
|
+
Source schema: packages/core/src/organization-model/domains/actions.ts
|
|
1847
1387
|
|
|
1848
|
-
|
|
1388
|
+
What Actions Are
|
|
1849
1389
|
|
|
1850
|
-
|
|
1851
|
-
1. Case Study Generator \u2014 7-10 hours \u2192 2 hours. Publication rate: 1/quarter \u2192 2/month.
|
|
1852
|
-
2. EMRG Follow-Up Generator \u2014 Automated post-event follow-ups with signature management.
|
|
1853
|
-
3. Event Sponsor Tracker \u2014 Automated sponsor pipeline tracking and communications.
|
|
1854
|
-
4. Lead Scraper \u2014 Automated discovery of event planning prospects.
|
|
1390
|
+
An action is a named, typed, invokable operation. It is not executable code -- it is a semantic declaration that says "this operation exists, it can be called this way, it affects these business objects, and it is implemented by this resource."
|
|
1855
1391
|
|
|
1856
|
-
|
|
1857
|
-
- 3,500+ attendees at The Event Planner Expo
|
|
1858
|
-
- 25+ year track record
|
|
1859
|
-
- Fortune 500 client roster
|
|
1392
|
+
Actions answer questions like:
|
|
1860
1393
|
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
title: "Understanding Elevasis Overview",
|
|
1866
|
-
summary: "Company overview, product positioning, and platform capability context for Elevasis AI orchestration platform",
|
|
1867
|
-
bodyText: "Lean documentation explaining what Elevasis is and what the platform can do. This section provides the foundational context for business conversations and technical evaluation.\n\nElevasis is a done-for-you AI automation service backed by a production-grade platform. The company targets service-based SMBs (2-50 employees, $200K-$5M revenue) who are too busy serving clients to grow their own business.\n\nKey Facts\n\n- Stage: Pre-revenue, platform 100% complete, client acquisition active\n- Offer: Done-for-you AI automation -- we build and maintain the workflows, no coding required\n- ICP: Service-based SMBs (2-50 employees, $200K-$5M revenue, USA)\n- Pricing: $500-$2k/mo (Foundation) \u2192 $2k-$5k/mo (Scaled) \u2192 $5k-$15k+/mo (AI-Powered)\n- Market: Early Adopters stage, $23.77B TAM growing to $87.7B by 2032\n- Competitive position: No dominant done-for-you SMB AI provider exists; blue ocean\n\nWhen to Use Each Document\n\n| Situation | Document |\n| --- | --- |\n| First conversation with any audience | What is Elevasis |\n| Technical evaluation or demo prep | Platform Capabilities |\n\nDocumentation\n\n- What is Elevasis \u2014 AI orchestration platform overview, company stage, value proposition, and pricing tiers\n- Platform Capabilities \u2014 Authoritative overview of what is built, what it does, and how the pieces fit together"
|
|
1868
|
-
},
|
|
1869
|
-
{
|
|
1870
|
-
id: "knowledge.marketing-overview",
|
|
1871
|
-
title: "Marketing Overview",
|
|
1872
|
-
summary: "Marketing documentation for Elevasis - strategy, website infrastructure, and content systems",
|
|
1873
|
-
bodyText: "Welcome to the Elevasis marketing documentation. This section covers marketing strategy, website implementation, and content systems.\n\nDocumentation\n\nStrategy\n\n- Overview \u2014 Channel strategy, inbound content system, and proof-led demand generation\n\nWebsite\n\nThe marketing website's technical infrastructure, setup, and SEO docs now live under Architecture \u2192 Website."
|
|
1874
|
-
},
|
|
1875
|
-
{
|
|
1876
|
-
id: "knowledge.ai-orchestration-principles",
|
|
1877
|
-
title: "AI Orchestration Principles",
|
|
1878
|
-
summary: "The 4 principles that separate production AI from toy automation",
|
|
1879
|
-
bodyText: `Most AI automation fails. Not because the technology is bad, but because it's implemented without the principles that make AI systems work in production.
|
|
1394
|
+
- What operations does the sales.lead-gen system expose?
|
|
1395
|
+
- Which workflow implements the lead-gen.company.qualify operation?
|
|
1396
|
+
- What entities does this action modify?
|
|
1397
|
+
- How can this action be invoked from the CLI, an API, or an MCP client?
|
|
1880
1398
|
|
|
1881
|
-
|
|
1399
|
+
ActionInvocation Kinds
|
|
1882
1400
|
|
|
1883
|
-
|
|
1401
|
+
Each action can declare one or more invocations. An invocation describes how the action is called.
|
|
1884
1402
|
|
|
1885
|
-
|
|
1403
|
+
| Kind | Fields | Description |
|
|
1404
|
+
| ------------------ | ---------------------------------------------------------- | ----------------------------------------- |
|
|
1405
|
+
| slash-command | command (must start with /), optional toolFactory | Called from the CLI or agent tool surface |
|
|
1406
|
+
| mcp-tool | server, name | Exposed as an MCP tool on a named server |
|
|
1407
|
+
| api-endpoint | method (GET/POST/PATCH/DELETE), path, optional schemas | Called via HTTP API endpoint |
|
|
1408
|
+
| script-execution | resourceId | Executed by running a script resource |
|
|
1886
1409
|
|
|
1887
|
-
|
|
1410
|
+
Multiple invocations on one action mean the same semantic operation is reachable through multiple surfaces.
|
|
1888
1411
|
|
|
1889
|
-
|
|
1412
|
+
Scope
|
|
1890
1413
|
|
|
1891
|
-
|
|
1892
|
-
- Your CRM stays your CRM
|
|
1893
|
-
- AI adds intelligence on top, not a rip-and-replace
|
|
1414
|
+
Actions are either global or domain-scoped.
|
|
1894
1415
|
|
|
1895
|
-
|
|
1416
|
+
- Global (scope: 'global'): available across all systems.
|
|
1417
|
+
- Domain-scoped (scope: { domain: '<modelId>' }): associated with a specific OM domain such as sales.
|
|
1896
1418
|
|
|
1897
|
-
|
|
1419
|
+
Systems reference actions through ActionRef entries on system.actions[], with an intent of exposes (the system owns the action) or consumes (the system calls the action). This relationship emits a uses edge from the system to the action in the graph.
|
|
1898
1420
|
|
|
1899
|
-
|
|
1421
|
+
Affects and Knowledge Bindings
|
|
1900
1422
|
|
|
1901
|
-
|
|
1423
|
+
Actions can declare which entities they modify via the affects field (array of entity IDs). Each entry emits an affects edge from the action to the entity node.
|
|
1902
1424
|
|
|
1903
|
-
|
|
1425
|
+
Actions can also declare knowledge bindings (array of knowledge node IDs) that reference relevant reference material. These bindings are not graph edges; they associate documentation with the action for surfacing in the Knowledge Base.
|
|
1904
1426
|
|
|
1905
|
-
|
|
1427
|
+
Resource Mapping
|
|
1906
1428
|
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
Black-box automation is unacceptable for business operations. You need to see every action, every decision, every cost \u2014 as it happens.
|
|
1910
|
-
|
|
1911
|
-
- Real-time activity feeds showing what's executing
|
|
1912
|
-
- Cost tracking per workflow, per execution
|
|
1913
|
-
- Token usage visibility
|
|
1914
|
-
- Execution logs for debugging
|
|
1915
|
-
|
|
1916
|
-
The anti-pattern: "It just works, trust us." Systems without observability create anxiety and prevent adoption.
|
|
1429
|
+
The optional resourceId field links an action to its implementation resource. Setting resourceId emits a mapsto edge from the action node to the resource node. If the resource does not yet exist in OM.resources, the graph builder creates a stub resource node from the ID.
|
|
1917
1430
|
|
|
1918
|
-
|
|
1431
|
+
Lifecycle States
|
|
1919
1432
|
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1433
|
+
Actions follow a five-stage lifecycle: draft, beta, active, deprecated, archived. The default is active. Lifecycle state is authored on the action entry and is reflected in the graph node.`
|
|
1434
|
+
},
|
|
1435
|
+
{
|
|
1436
|
+
id: "knowledge.org-model-entities",
|
|
1437
|
+
title: "Organization Model Entities",
|
|
1438
|
+
summary: "Entities model business objects owned by systems -- with table metadata, state catalogs, and typed entity links.",
|
|
1439
|
+
bodyText: "Overview\n\nEntities are the business objects of the Organization Model. Each entity is a named, typed object owned by a system -- it corresponds to a database table, optionally participates in a state catalog, and can declare typed relationships to other entities.\n\nEntities are authored in the OM.entities domain map and projected as entity graph nodes by buildOrganizationGraph(). Each entity emits a contains edge from its owning system node and optional links edges to related entities.\n\nSource schema: packages/core/src/organization-model/domains/entities.ts\n\nWhat Entities Are\n\nAn entity declaration answers questions like:\n\n- What business objects does the sales.crm system own?\n- Which database table backs the crm.deal entity?\n- What states can a leadgen.company pass through?\n- How are crm.deal and crm.contact related?\n\nEntities are not executable. They are semantic declarations that describe the shape of business data: who owns it, where it lives, what states it can be in, and how it connects to other objects.\n\nownedBySystemId\n\nEvery entity must declare ownedBySystemId -- the ID of the system responsible for it. The graph builder emits a contains edge from system:<ownedBySystemId> to the entity node. An entity can only be owned by one system.\n\nTable and Row Schema\n\nThe optional table field names the backing database table (e.g. crmdeals). The optional rowSchema field references a schema identifier that describes the row shape. These fields are informational references; they are not validated against the database at OM parse time.\n\nstateCatalogId\n\nThe optional stateCatalogId field links the entity to a state catalog. The graph builder uses this to project event nodes for each state transition:\n\n- For general status catalogs, the builder walks OM.statuses entries whose semanticClass matches the catalog ID.\n- For crm.pipeline, the builder also walks pipeline stages from OM.sales.\n- For delivery.task, the builder walks task statuses from OM.projects.\n- For lead-gen.company and lead-gen.contact, the builder walks the lead-gen stage catalog.\n\nEach matching status or stage generates an event node with an originatesfrom edge pointing back to the entity.\n\nTyped Entity Links\n\nThe links field declares typed relationships to other entities. Each link has:\n\n- toEntity -- the target entity ID.\n- kind -- one of belongs-to, has-many, has-one, or many-to-many.\n- via -- optional join key or junction table name.\n- label -- optional display label for the relationship.\n\nEach link emits a links edge from the entity node to the target entity node in the graph.\n\nExample Entity Shape\n\nA crm.deal entity owned by sales.crm, backed by the crmdeals table, in the crm.pipeline state catalog, with a has-many link to crm.contact via the dealcontacts junction:\n\n- id: crm.deal\n- ownedBySystemId: sales.crm\n- table: crmdeals\n- stateCatalogId: crm.pipeline\n- links: [{ toEntity: 'crm.contact', kind: 'has-many', via: 'dealcontacts' }]"
|
|
1440
|
+
},
|
|
1441
|
+
{
|
|
1442
|
+
id: "knowledge.org-model-events",
|
|
1443
|
+
title: "Organization Model Events",
|
|
1444
|
+
summary: "Events are projected signals -- the OM has no authored event domain. They emit from resources and originate from entity state catalogs.",
|
|
1445
|
+
bodyText: "Overview\n\nEvents are projected graph nodes. The Organization Model has no OM.events domain map that authors edit directly. Instead, events are derived by buildOrganizationGraph() from two sources: EventEmissionDescriptor entries on workflow and agent resources, and state catalog entries on entities.\n\nEvery event node carries a unique ID formed as \\<ownerId\\>:\\<eventKey\\>, a human-readable label, and an edge that connects it back to the node that produced it.\n\nProjection logic: packages/core/src/organization-model/graph/build.ts\n\nAuthored vs. Projected\n\nThe distinction matters because it determines where you change event data.\n\nEvents are never edited in a standalone events file. To change an event you must change its source:\n\n- To change a resource-emitted event, update the emits array on the workflow or agent entry in OM.resources.\n- To change an entity state-transition event, update the entity's stateCatalogId or the underlying status/stage entry in the relevant catalog domain.\n\nThe graph builder projects these into event graph nodes automatically on the next call to buildOrganizationGraph().\n\nEventEmissionDescriptor on Resources\n\nWorkflow and agent resource entries can declare an emits array. Each element is an EventEmissionDescriptor:\n\n| Field | Type | Description |\n| --------------- | --------------- | ------------------------------------------------------------- |\n| eventKey | ModelIdSchema | Short key scoped to the owner resource (e.g. enrolled) |\n| label | string | Human-readable label for the event |\n| payloadSchema | ModelIdSchema | Optional reference to a schema that describes the payload |\n| lifecycle | lifecycle enum | Optional: draft, beta, active, deprecated, archived |\n\nThe graph builder constructs the full EventDescriptor by combining ownerId (the resource ID), ownerKind: 'resource', and the emission descriptor fields. The resulting event ID is \\<resourceId\\>:\\<eventKey\\>.\n\nAn emits edge is then projected from the resource node to the event node.\n\nOnly workflow and agent resource kinds support emits. integration and script resources do not.\n\noriginatesfrom Edges from Entity State Catalogs\n\nWhen an entity declares a stateCatalogId, the graph builder projects one event node per state transition available to that entity. These events use ownerKind: 'entity' and the resulting event ID is \\<entityId\\>:\\<eventKey\\>.\n\nA reversed originatesfrom edge is projected from the event node pointing back to the entity node. This edge direction is the opposite of emits: it signals that the event represents a state change that originates from the entity, not that the entity actively fires the event.\n\nThe builder resolves state catalogs as follows:\n\n- General status catalogs: walks OM.statuses entries whose semanticClass matches the entity's stateCatalogId.\n- crm.pipeline: also walks pipeline stages from OM.sales that reference the entity.\n- delivery.task: also walks task statuses from OM.projects.\n- lead-gen.company and lead-gen.contact: walks the LEADGENSTAGECATALOG constant, filtering by entity type.\n\ntriggers Edges to Policies\n\nWhen a policy declares trigger.kind: 'event', the graph builder looks up the projected event node by event ID and emits a triggers edge from the event node to the policy node. This edge is only emitted if the event node was already projected by the time the policy loop runs.\n\nPolicy triggers are the primary way events connect to downstream behavior. No triggers edge is emitted for events that no policy references.\n\nEventDescriptor Full Shape\n\nEventDescriptor is the resolved type used internally by the graph builder. It extends EventEmissionDescriptor with identity fields:\n\n| Field | Type | Description |\n| --------------- | -------------------------- | ----------------------------------------------------- |\n| id | EventIdSchema | Composite: \\<ownerId\\>:\\<eventKey\\> |\n| ownerId | resource ID or model ID | ID of the resource or entity that is the event source |\n| ownerKind | 'resource' or 'entity' | Discriminates between the two projection paths |\n| eventKey | ModelIdSchema | Short key, unique within the owner |\n| label | string | Human-readable label |\n| payloadSchema | ModelIdSchema (opt) | Schema reference for payload shape |\n| lifecycle | lifecycle enum (opt) | Lifecycle state from the emission descriptor |\n\nEventDescriptor is not stored in the graph node itself -- it is used transiently during graph construction to build the node and emit edges. The graph node stores id, kind: 'event', label, and sourceId.\n\nGraph Summary\n\n| Edge kind | Direction | When emitted |\n| ----------------- | --------------------------- | -------------------------------------------- |\n| emits | resource node -> event node | Resource emits[] array is non-empty |\n| originatesfrom | event node -> entity node | Entity has a stateCatalogId |\n| triggers | event node -> policy node | Policy trigger.kind === 'event' matches ID |"
|
|
1446
|
+
},
|
|
1447
|
+
{
|
|
1448
|
+
id: "knowledge.org-model-graph-contract",
|
|
1449
|
+
title: "Organization Model Graph Contract",
|
|
1450
|
+
summary: "The canonical graph node and edge contract -- 10 node kinds, 12 edge kinds, and the resource-type overlay derived from the Organization Model.",
|
|
1451
|
+
bodyText: "Overview\n\nThe Organization Model graph contract defines the typed node and edge taxonomy emitted by buildOrganizationGraph(). Every surface that reads or visualizes the OM -- including Command View -- works against this contract.\n\nThe graph has two categories of nodes: authored nodes derived directly from OM domain maps, and projected nodes derived by the graph builder from authored content. Events and stages are projected; all other node kinds are authored.\n\nSource schema: packages/core/src/organization-model/graph/schema.ts\nProjection logic: packages/core/src/organization-model/graph/build.ts\n\nNode Kinds\n\nTen node kinds are valid in the graph.\n\n| Kind | Authored / Projected | Source |\n| -------------- | -------------------- | ------------------------------------------------------- |\n| organization | Projected | Root node; always present; id is organization-model |\n| system | Authored | OM.systems domain map |\n| role | Authored | OM.roles domain map |\n| action | Authored | OM.actions domain map |\n| entity | Authored | OM.entities domain map |\n| event | Projected | Derived from resource emits and entity state catalogs |\n| policy | Authored | OM.policies domain map |\n| stage | Projected | Derived from OM.prospecting stage catalogs |\n| resource | Authored | OM.resources domain map |\n| knowledge | Authored | OM.knowledge.nodes array |\n\nEdge Kinds\n\nTwelve edge kinds are valid in the graph.\n\n| Kind | Direction | Meaning |\n| ----------------- | ------------------------------------- | ------------------------------------------------------------- |\n| contains | parent -> child | Containment: organization to system, system to resource, etc. |\n| references | source -> target | Cross-reference: role reports-to, agent invokes action |\n| mapsto | action -> resource | Action is implemented by a resource |\n| uses | system -> action, stage -> action | System or stage uses an action |\n| governs | knowledge -> target, role -> system | Knowledge node or role governs a target |\n| links | entity -> entity | Typed entity relationship (belongs-to, has-many, etc.) |\n| affects | action -> entity | Action modifies or reads an entity |\n| emits | resource -> event | Resource produces an observable event |\n| originatesfrom | event -> entity | Event originates from an entity state transition |\n| triggers | event -> policy, action -> policy | Event or action invocation triggers a policy evaluation |\n| appliesto | policy -> system/action/resource/role | Policy governs the target |\n| effects | policy -> action/role | Policy effect invokes an action or notifies a role |\n\nResource Type Overlay\n\nResource nodes carry an optional resourceType field that is a separate enum from kind. It is set by the graph builder from the OM resource kind field or from Command View data.\n\n| Value | Description |\n| ------------------ | --------------------------------- |\n| workflow | Deterministic automation pipeline |\n| agent | LLM-driven reasoning resource |\n| trigger | Event-driven entry point |\n| integration | External service connector |\n| external | Third-party system reference |\n| humancheckpoint | Human-in-the-loop review gate |\n| script | One-shot executable script |\n\nAuthored vs. Projected Fields\n\nAuthored nodes\n\nSystem, role, action, entity, policy, resource, and knowledge nodes are authored in the OM domain maps. Their id, label, description, and domain-specific fields are set from OM source data.\n\nProjected nodes\n\n- Organization node: always present; id is always organization-model.\n- Event nodes: derived from two sources. Resource events come from resource.emits declarations on workflow and agent resources. Entity events come from the entity's stateCatalogId -- the builder walks the matching statuses and pipeline/project stage catalogs to generate one event node per transition.\n- Stage nodes: derived from OM.prospecting.companyStages and OM.prospecting.contactStages.\n\nProjected nodes are never authored directly. To add an event, declare emits on a resource or assign stateCatalogId to an entity.\n\nGraph Node Shape\n\nEach node has: id (graph-unique string), kind (one of the 10 kinds), label (display name), optional sourceId (OM domain ID), optional description, optional icon, optional enabled flag, and optional resourceType (resource nodes only).\n\nEach edge has: id (graph-unique string), kind (one of the 12 kinds), sourceId, targetId, optional label, and optional relationshipType (triggers, uses, or approval)."
|
|
1452
|
+
},
|
|
1453
|
+
{
|
|
1454
|
+
id: "knowledge.org-model-policies",
|
|
1455
|
+
title: "Organization Model Policies",
|
|
1456
|
+
summary: "Policies govern systems, actions, resources, and roles -- with discriminated triggers, predicates, and effects.",
|
|
1457
|
+
bodyText: "Overview\n\nPolicies are cross-cutting governance rules in the Organization Model. They declare what should happen when a trigger fires, under what conditions, and with what effect. Policies are authored in the OM.policies domain map and projected as policy graph nodes by buildOrganizationGraph().\n\nEvery policy emits a contains edge from the organization root and appliesto edges to each system, action, resource, or role it governs. When the trigger is event-based or action-based, an additional triggers edge connects the source node to the policy.\n\nSource schema: packages/core/src/organization-model/domains/policies.ts\n\nPolicyTrigger\n\nThe trigger is a discriminated union on kind that defines what activates the policy.\n\n| Kind | Required fields | Description |\n| ------------------- | --------------- | --------------------------------------------------------- |\n| event | eventId | Fires when the referenced event is projected in the graph |\n| action-invocation | actionId | Fires when the referenced action is invoked |\n| schedule | cron | Fires on a cron schedule (max 120 chars) |\n| manual | none | Fires only when explicitly triggered by a user or process |\n\nFor event triggers, the graph builder looks up the projected event node by eventId and emits a triggers edge from that event node to the policy node. For action-invocation triggers, a triggers edge is emitted from the action node to the policy node. Schedule and manual triggers produce no additional graph edges.\n\nPolicyPredicate\n\nThe predicate is a discriminated union on kind that defines the condition under which the policy effect is applied.\n\n| Kind | Required fields | Description |\n| ------------ | ------------------------------- | ---------------------------------------------------------- |\n| always | none | Condition is unconditionally true; effect always fires |\n| expression | expression (string, max 2000) | Arbitrary expression evaluated at runtime |\n| threshold | metric, operator, value | Numeric threshold check: lt, lte, eq, gte, or gt |\n\nThe default predicate if not specified is { kind: 'always' }.\n\nPolicyEffect\n\nThe actions field holds an ordered array of effects (PolicyEffect[]). At least one effect is required. Effects are a discriminated union on kind.\n\n| Kind | Required fields | Description |\n| ------------------ | ------------------- | ----------------------------------------------------------- |\n| require-approval | roleId (optional) | Blocks execution until a human approves; routes to the role |\n| invoke-action | actionId | Invokes the referenced action as a consequence |\n| notify-role | roleId | Sends a notification to the specified role |\n| block | none | Stops execution entirely; no approval path |\n\nFor invoke-action effects, the graph builder emits an effects edge from the policy node to the referenced action node. For notify-role and require-approval effects that include a roleId, an effects edge is emitted from the policy node to the role node.\n\nappliesTo\n\nThe appliesTo field declares which OM entities the policy governs. It contains four ID arrays, all defaulting to empty:\n\n| Field | Target kind | Graph edge emitted |\n| ------------- | ----------- | ----------------------------------------- |\n| systemIds | system | appliesto from policy to system node |\n| actionIds | action | appliesto from policy to action node |\n| resourceIds | resource | appliesto from policy to resource node |\n| roleIds | role | appliesto from policy to role node |\n\nA policy can apply to any combination of these targets simultaneously. A policy with all four arrays empty is valid but produces no appliesto edges.\n\nLifecycle\n\nPolicies follow the same five-stage lifecycle as systems and actions: draft, beta, active, deprecated, archived. The default is active. Lifecycle is authored on the policy entry and is reflected in the graph node.\n\nThe order field controls domain-map iteration order. The convention is multiples of 10 (10, 20, 30, ...) to allow easy insertion between existing entries.\n\nGraph Summary\n\n| Edge kind | Direction | When emitted |\n| ------------ | ------------------------------------------ | ---------------------------------------------------------------------------------- |\n| contains | organization root -> policy node | Always; every policy is contained by the organization root |\n| appliesto | policy node -> system/action/resource/role | For each non-empty appliesTo.Ids entry |\n| triggers | event or action node -> policy node | When trigger.kind is event or action-invocation |\n| effects | policy node -> action or role node | For invoke-action, notify-role, and require-approval effects with a roleId |"
|
|
1458
|
+
},
|
|
1459
|
+
{
|
|
1460
|
+
id: "knowledge.platform-command-view",
|
|
1461
|
+
title: "Platform Command View",
|
|
1462
|
+
summary: "Reference for Command View, the visual Organization Model graph surface that projects systems, resources, actions, entities, events, and policies into an explorable operational view.",
|
|
1463
|
+
bodyText: "Overview\n\nCommand View is the visual surface for the Organization Model graph. It projects the OM -- systems, resources, actions, entities, events, and policies -- into an explorable graph using the same node and edge taxonomy emitted by buildOrganizationGraph().\n\nIt answers operational questions such as:\n\n- What systems own which resources?\n- What does this agent map to in the OM?\n- Which actions affect which entities?\n- If a resource goes offline, what policies or actions reference it?\n- What events originate from this entity or resource?\n\nAccess Command View through the Knowledge area at /knowledge/command-view.\n\nHow Command View Works\n\nCommand View is a projection of the Organization Model graph, not a separate deployment manifest.\n\nThe OM resources domain is the single source of resource identity. The actions domain is the invokable semantic layer. The systems domain is the backbone. Command View merges the graph emitted by buildOrganizationGraph() with optional live Command View data (deployed resource metadata) to render a unified operational picture.\n\nGraph projection pipeline:\n\n1. Organization Model is authored in packages/elevasis-core/src/organization-model/.\n2. buildOrganizationGraph() projects the OM into typed nodes and edges.\n3. Command View data (deployed resource metadata) is optionally merged as an overlay.\n4. The merged graph is serialized and cached.\n5. The frontend visualizes the cached graph.\n\nCommand View does not own resource identity. It reads what the OM declares.\n\nNode Kinds\n\nCommand View renders the same node kinds as the OM graph:\n\n| Kind | Source | Description |\n| -------------- | -------------- | -------------------------------------------------------------------------- |\n| organization | Root | The organization-model root node |\n| system | OM.systems | Bounded contexts with dotted IDs and parentSystemId hierarchy |\n| role | OM.roles | Named roles with hierarchy and holder assignments |\n| action | OM.actions | Invokable operations with invocation type metadata |\n| entity | OM.entities | Business objects owned by systems |\n| event | Projected | Derived from resource emits declarations and entity state catalogs |\n| policy | OM.policies | Governance rules applied to systems, actions, resources, and roles |\n| stage | Projected | Derived from prospecting stage catalogs |\n| resource | OM.resources | Workflows, agents, integrations, triggers, scripts, and external resources |\n| knowledge | OM.knowledge | Knowledge nodes governed by systems |\n\nThe resourceType overlay on resource nodes is a separate enum: workflow, agent, trigger, integration, external, humancheckpoint, script.\n\nEdge Kinds\n\n| Kind | Meaning |\n| ----------------- | ------------------------------------------------------------------------------ |\n| contains | Parent-to-child containment (organization to system, system to resource, etc.) |\n| references | Cross-reference between nodes (role reports-to, agent invokes action) |\n| mapsto | Action mapped to a resource implementation |\n| uses | System or stage uses an action |\n| governs | Knowledge node or role governs a system or target |\n| links | Entity links to another entity |\n| affects | Action affects an entity |\n| emits | Resource or entity emits an event |\n| originatesfrom | Event originates from an entity |\n| triggers | Event or action triggers a policy |\n| appliesto | Policy applies to a system, action, resource, or role |\n| effects | Policy effect invokes an action or notifies a role |\n\nVisualization Modes\n\nCommand View uses Cytoscape graph modes:\n\n- Map: preserves organization structure and honors hidden-resource visibility.\n- Trace: resolves directed paths and reveals resources for the active trace.\n- Impact: pivots around the selected node and reveals related resources for impact review.\n\nFilter panel:\n\n- Search across nodes, relationships, and IDs.\n- Filter by node kind, resource type, environment, topology presence, integrations, and resource facets.\n- Toggle resource visibility and diagnostic/testing resource visibility.\n- Show visible and hidden resource counts in the same control surface.\n\nHidden resource behavior:\n\n- Resource nodes are hidden by default so the organization structure remains readable.\n- Structural nodes remain visible as the navigation skeleton.\n- Selecting a visible node can reveal directly connected hidden resources.\n- Diagnostic and testing resources are hidden by default and can be revealed from the filter panel.\n\nExpand Around behavior:\n\n- Select a node, open Details, and use Expand Around to preview nearby graph context before revealing it.\n- Semantic presets cover coverage, operational dependencies, organization context, and impact paths.\n- System and entity nodes default to Coverage.\n- Resource nodes default to Operational Dependencies.\n- Preview counts show hidden resources before Apply reveals the graph patch.\n\nRead-only constraints:\n\n- No drag and drop.\n- No connection editing.\n- No graph mutation from the visualization layer.\n\nOrganization Model as the Source of Truth\n\nResource identity lives in OM.resources, not in a separate manifest. To add a resource to Command View:\n\n1. Add the resource to OM.resources in the organization model.\n2. Link it to a system via systemPath.\n3. Optionally link actions to the resource via action.resourceId.\n4. Deploy the organization model. Command View reflects the new resource automatically.\n\nThere is no separate DeploymentSpec.relationships declaration required for OM-declared resources. The graph builder derives containment and relationship edges directly from the OM contract.\n\nLive resource metadata (deployed name, description, runtime status) can be overlaid from Command View data when available, but the OM contract is primary.\n\nUsing Command View\n\nSystem Understanding\n\nUseful exploration questions:\n\n- What systems own which resources and entities?\n- What actions does this system expose, and what do they map to?\n- Which knowledge nodes govern this system?\n- What policies apply to this system?\n- What events originate from this entity?\n- What roles are responsible for this system?\n\nVisual patterns:\n\n- Hub nodes with many edges indicate central resources or pivotal systems.\n- Linear chains show sequential workflow pipelines.\n- Branching shows conditional routing or multiple action paths.\n- Isolated nodes may indicate unused or draft resources.\n\nManifest Debugging\n\nUse Command View to verify:\n\n1. Resources appear as nodes under the correct system.\n2. Actions map to the expected resources.\n3. Entities link to the correct systems and each other.\n4. Policies apply to the correct systems, actions, or resources.\n5. Events appear for resources with emits declarations.\n6. Orphaned resources are intentional.\n\nCommon issues:\n\n- Resource declared in OM but assigned to the wrong systemPath.\n- Action resourceId points to a resource ID that does not exist.\n- Entity ownedBySystemId references an unknown system.\n- Policy appliesTo references a system, action, or resource that was removed.\n\nTroubleshooting\n\nEmpty Graph\n\nLikely causes:\n\n- Organization has no systems or resources defined.\n- Search, node kind, or resource type filters hide all graph elements.\n- Command View resources are hidden and no structural nodes match current filters.\n\nCheck:\n\n1. OM systems and resources domains have entries.\n2. Filter panel visibility and filter state.\n3. API response for the /api/command-view endpoint.\n\nFix:\n\n- Add systems and resources to the organization model.\n- Reveal resources or reset graph filters.\n\nMissing Nodes\n\nLikely causes:\n\n- Resource, action, or entity is defined in OM but omitted from the relevant domain map.\n- Node is hidden by visibility controls.\n- Status, resource type, or topology filters exclude the node.\n\nCheck:\n\n1. OM domain map includes the ID as a key and as id in the entry.\n2. Filter panel visibility and filter state.\n3. Node lifecycle or enabled field.\n\nFix by adding the entry to the correct domain map or revealing hidden nodes.\n\nMissing Edges\n\nLikely causes:\n\n- Action resourceId does not match any OM resource ID.\n- Entity ownedBySystemId references an unknown system.\n- Policy appliesTo references IDs that do not exist.\n- Knowledge node link uses a nodeId that has no matching OM node.\n\nCheck:\n\n1. Cross-reference IDs across OM domains.\n2. Run pnpm --filter @repo/core check-types to surface Zod validation errors.\n\nFix the OM entry to reference the correct ID.\n\nBest Practices\n\nKeep the OM up to date as systems and resources evolve:\n\n1. Add new resources to OM.resources with the correct systemPath.\n2. Map actions to resources via action.resourceId when the action calls a specific resource.\n3. Update entity.ownedBySystemId when entities move between systems.\n4. Update policy.appliesTo when systems, actions, or resources are renamed or removed.\n5. Declare resource.emits for workflows and agents that produce observable events.\n\nUse the OM graph as the authoritative reference for Command View structure. The graph builder derives the visualization from the OM contract -- do not expect Command View to show connections that are not declared in the OM.\n\nRelated References\n\n- /knowledge read knowledge.org-model-reference\n- /knowledge read knowledge.platform-composition-patterns\n- /knowledge read knowledge.platform-integration-patterns"
|
|
1464
|
+
},
|
|
1465
|
+
{
|
|
1466
|
+
id: "knowledge.platform-composition-patterns",
|
|
1467
|
+
title: "Platform Composition Patterns",
|
|
1468
|
+
summary: "Reference for composing agents, workflows, integrations, tools, observability, scaling, error handling, and security into complete Elevasis automation systems.",
|
|
1469
|
+
bodyText: "Overview\n\nUse this reference to choose how Elevasis resources compose into complete systems. The core decision is whether reasoning, deterministic orchestration, or a hybrid of both should own the business flow.\n\nThree primary patterns exist:\n\n- Agent-centric: an agent is the primary orchestrator, and workflows or integrations are tools.\n- Workflow-centric: a workflow is the deterministic pipeline, and agents appear only as processing steps.\n- Hybrid: agents decide what should happen, and workflows execute reliable sequences.\n\nSystem Architecture Patterns\n\nAgent-Centric Systems\n\nPattern: Agent is the primary orchestrator; workflows and integrations are tools.\n\ntext\nTrigger to Agent\n |- Tool: Integration A\n |- Tool: Integration B\n |- Tool: Workflow 1 via resource invocation\n - Tool: Workflow 2 via resource invocation\n\nUse this pattern for ambiguous goals, dynamic decision-making, multi-turn conversations, and business logic that requires judgment.\n\nStrengths:\n\n- Flexible and adaptive.\n- Handles ambiguity well.\n- Can delegate to specialist resources.\n- Supports natural language interaction.\n\nWeaknesses:\n\n- Token cost per execution.\n- Non-deterministic LLM variance.\n- Requires strong observability for debugging.\n- Usually slower than deterministic workflows.\n\nWorkflow-Centric Systems\n\nPattern: Workflow is the pipeline; agents are processing steps.\n\ntext\nTrigger to Workflow\n |- Step 1: Integration A reads data\n |- Step 2: Agent processes or reasons\n |- Step 3: Integration B writes result\n - Step 4: Conditional routing\n\nUse this pattern for predictable sequences, data transformation pipelines, integration orchestration, and fixed business logic.\n\nStrengths:\n\n- Deterministic and reliable.\n- Fast execution.\n- Easy to debug step-by-step.\n- No token cost unless an agent step is included.\n\nWeaknesses:\n\n- Rigid, because steps are predefined.\n- Does not handle ambiguity well.\n- Requires code changes for logic updates.\n\nHybrid Systems\n\nPattern: Agents decide, workflows execute. This is the recommended default for complex automation.\n\ntext\nTrigger to Agent decides what to do\n |- Invokes: Workflow A\n |- Invokes: Workflow B\n - Uses: Integration C directly\n\nWorkflow A:\n |- Integration D reads\n |- Agent processes a specific subtask\n - Integration E writes\n\nUse this pattern when the system needs variable paths, reasoning, reliability, and a mix of predictable and unpredictable steps.\n\nStrengths:\n\n- Flexible where needed and reliable where possible.\n- Cost-efficient because workflows do not use tokens by default.\n- Good observability across agent reasoning and workflow steps.\n\nResource Composition Patterns\n\nAgent + Tools\n\nUse direct integration access for simple or frequent operations.\n\nTool categories:\n\n- Base tools: always available, high-frequency operations such as read queries or cached data access. Example: attiogetrecord.\n- Knowledge Map tools: lazy-loaded, low-frequency operations such as write operations or domain-specific capabilities. Examples: attiocreatecontact, dropboxuploadfile.\n- Resource invocation tools: delegate to workflows for deterministic sequences or call specialist agents.\n\nTool creation example:\n\ntypescript\nconst tool = createAttioCreateRecordTool('elevasis-attio')\n\nCredential names resolve to organization-scoped storage. RLS policies enforce organization isolation, credentials are encrypted at rest, and OAuth tokens can refresh automatically.\n\nAgent + Workflow\n\nUse an agent for reasoning and a workflow for deterministic execution.\n\ntext\nTrigger to Agent reasoning\n |- Direct tool: read integration\n |- Invoke tool: Workflow A\n - Invoke tool: Workflow B\n\nExecution flow:\n\n1. Agent analyzes the request.\n2. Agent invokes a workflow through a tool call.\n3. Workflow executes deterministic steps without token cost.\n4. Agent explains the result to the user.\n\nUse this pattern when the system needs both reasoning and reliability, has multiple execution paths, or should minimize token use by moving predictable work into workflows.\n\nWorkflow + Integration\n\nUse a workflow to orchestrate deterministic integration sequences.\n\ntext\nTrigger to Workflow\n |- Step 1: Integration A reads\n |- Step 2: Transform pure function\n |- Step 3: Integration B writes\n - Step 4: Notify\n\nUse this pattern for predictable step sequences, data transformation pipelines, integration orchestration, and cost-sensitive work that does not need LLM reasoning.\n\nPattern Selection Guide\n\n| Requirement | Pattern | Example |\n| --- | --- | --- |\n| Ambiguous goals | Agent-centric | Business orchestration with variable outcomes |\n| Predictable sequence | Workflow-centric | Shopify to CRM sync |\n| Hybrid needs | Agent + workflow | Support router where agent decides and workflow executes |\n| \\>10 tools | Knowledge Map | Agent with many domain capabilities |\n| High-frequency reads | Base tools | Attio read operations |\n| Low-frequency writes | Lazy-loaded tools | CRM updates or page creation |\n| External services | Integration tools | Attio, Gmail, Google Sheets |\n| Deterministic pipelines | Workflow + integration | Data transformation |\n\nComplexity guidelines:\n\n- Simple, 1-2 resources: agent with base tools, or workflow with 1-2 integrations.\n- Medium, 3-5 resources: agent plus Knowledge Map with 2-3 nodes, or workflow plus an agent step.\n- Complex, 6+ resources: agent plus Knowledge Map plus memory preload, or multi-integration workflows.\n\nCross-Cutting Concerns\n\nObservability\n\nTrack these surfaces:\n\n- Agent iterations, including reasoning and actions.\n- Workflow steps, including inputs and outputs.\n- Tool calls, including integration requests and responses.\n- Errors and retries.\n- Token usage and cost.\n\nPrimary tools:\n\n- Execution Logs for SSE-based debugging.\n- Activity Log for the real-time stream.\n- Execution Health for cost tracking and ROI review.\n- Command View for system architecture and dependency review.\n\nCost Tracking\n\nAgent cost comes from per-iteration token usage, model pricing, and tool execution cost. Workflow cost is usually zero token cost plus any external integration API fees.\n\nOptimization rules:\n\n- Use workflows for predictable tasks.\n- Lazy-load tools for 80-95% token savings.\n- Cache frequently accessed stable data.\n- Choose the appropriate model per task.\n\nScaling Strategies\n\nHorizontal scaling:\n\n- Keep agent execution stateless.\n- Store sessions in the database.\n- Let a load balancer distribute requests.\n\nVertical optimization:\n\n- Preload memory to reduce iteration count.\n- Lazy-load tools to reduce token usage.\n- Batch tool operations where possible.\n- Use parallel integration calls when the steps are independent.\n\nError Handling\n\nAgent error handling:\n\n- Tool errors are returned to the agent so it can reason about recovery.\n- Iteration limits prevent infinite loops.\n- Timeout protection constrains long-running tools and executions.\n- Graceful degradation explains what failed.\n\nWorkflow error handling:\n\n- Step retry logic uses exponential backoff, commonly 1, 4, and 9 minutes.\n- Conditional routing can send failures to an alternative step.\n- Manual intervention can route work to a human checkpoint.\n- Idempotent steps should be safe to retry.\n\nExample:\n\ntypescript\n{\n id: 'sync-to-crm',\n handler: async ({ input, context }) => {\n try {\n return await crmClient.createContact(input)\n } catch (error) {\n if (error.code === 'DUPLICATE') {\n return crmClient.updateContact(input)\n }\n\n throw error\n }\n },\n next: { type: 'linear', target: 'send-notification' }\n}\n\nSecurity\n\nCredential management:\n\n- Store credentials in the credentials table.\n- Encrypt values at rest.\n- Scope records by organization with RLS policies.\n- Refresh OAuth tokens automatically where supported.\n- Never store API keys in code.\n\nMulti-tenancy isolation layers:\n\n1. Database RLS on tenant-scoped tables.\n2. API middleware that requires organization context.\n3. Resource registry lookups scoped to the organization.\n4. Command View filtered by organization.\n\nBest Practices\n\n1. Start with a single agent or workflow, then add complexity only when needed.\n2. Build the working version before optimizing for caching or lazy loading.\n3. Put domain logic in agents and execution flow in workflows.\n4. Lazy-load large or low-frequency tool groups.\n5. Cache stable data, not fast-changing operational state.\n\nCommon mistakes:\n\n- Creating a Knowledge Map for fewer than five tools.\n- Using a workflow for one integration call when an agent tool would be simpler.\n- Loading more than ten tools as base tools.\n- Putting domain logic into workflows where it becomes hard to test and adapt.\n- Mixing unrelated concerns in one knowledge node.\n\nRelated References\n\n- /knowledge read knowledge.platform-integration-patterns\n- /knowledge read knowledge.platform-command-view"
|
|
1470
|
+
},
|
|
1471
|
+
{
|
|
1472
|
+
id: "knowledge.platform-integration-patterns",
|
|
1473
|
+
title: "Platform Integration Patterns",
|
|
1474
|
+
summary: "Reference for connecting Elevasis agents and workflows to external services through adapters, OAuth, API keys, tenant-isolated credentials, retries, and tests.",
|
|
1475
|
+
bodyText: "Overview\n\nIntegration patterns define how Elevasis agents and workflows connect to external services such as Gmail, Attio, Google Sheets, and custom APIs. The platform uses a standardized adapter pattern with OAuth 2.0 and API key authentication backed by tenant-isolated credentials.\n\nCore patterns:\n\n- Direct integration, where a resource uses integration tools directly.\n- Integration workflow, where a workflow coordinates multiple integrations.\n- Shared integration, where multiple resources use the same credential set.\n- Multi-account integration, where the same provider has separate credentials for different contexts.\n\nDirect Integration Pattern\n\nPattern: Resource uses an integration tool.\n\nUse direct integration tools when an agent or workflow performs frequent operations against a provider.\n\nCharacteristics:\n\n- Tools are always available to the resource.\n- There is no additional navigation overhead.\n- The pattern is optimized for frequent read-heavy operations.\n\nTool factory location:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/{provider}-tools.ts\n\nTool factories use createIntegrationTool() with a credentialName parameter. Example: createAttioCreateRecordTool(credentialName).\n\nIntegration Workflow Pattern\n\nPattern: Agent invokes a workflow, and the workflow uses integrations.\n\nUse an integration workflow for complex multi-step operations that need deterministic orchestration.\n\nExample lead capture flow:\n\n1. Webhook trigger receives the event.\n2. Workflow creates an Attio contact.\n3. Workflow sends a Gmail notification.\n4. Workflow returns confirmation.\n\nCharacteristics:\n\n- Deterministic execution order.\n- Step-level error handling and retry.\n- Multiple integrations coordinated in one place.\n- Built-in audit trail through workflow execution.\n\nShared Integration Pattern\n\nPattern: Multiple resources share the same integration.\n\nUse shared integrations for organization-wide providers that should use one credential set.\n\nExample:\n\ntext\npackages/elevasis-operations/src/index.ts\n\nCharacteristics:\n\n- One credential per integration.\n- Centralized credential management.\n- Shared across agents and workflows.\n\nMulti-Account Pattern\n\nPattern: Same provider, different credentials.\n\nUse this pattern for department-specific provider instances or separate dev/prod environments.\n\nExample:\n\ntypescript\nconst salesTool = createAttioCreateRecordTool('attio-sales')\nconst supportTool = createAttioCreateRecordTool('attio-support')\n\nDisambiguate multi-account tools with explicit tool names:\n\ntypescript\nconst tool = createAttioToolNamed(\n 'attiosalescreaterecord',\n 'attio-sales',\n 'createRecord'\n)\n\nAuthentication Patterns\n\nOAuth 2.0 vs API Key\n\n| Aspect | OAuth 2.0 | API key |\n| --- | --- | --- |\n| Auth flow | Browser redirect plus token exchange | Direct API key |\n| Setup complexity | High: client ID, secret, redirect URL | Low: single API key |\n| Token refresh | Automatic refresh token rotation | Manual key rotation |\n| Scope control | Granular permission scopes | Full access or none |\n| User context | Per-user authorization | Service-level access |\n| Revocation | User can revoke anytime | Manual key deletion |\n\nOAuth 2.0 Pattern\n\nProviders include Gmail and Google Sheets.\n\nCredential format:\n\ntypescript\ninterface OAuthToken {\n provider: string\n accessToken: string\n refreshToken: string\n expiresAt: string\n tokenType: 'Bearer'\n scope?: string\n}\n\nexpiresAt is an ISO 8601 timestamp. The platform handles token refresh when supported by the provider and credential implementation.\n\nAPI Key Pattern\n\nProviders include Attio and custom APIs.\n\nCredential format:\n\ntypescript\ninterface APIKeyCredentials {\n apiKey: string\n}\n\nProvider-specific validation belongs in the adapter. For Attio, see:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/attio/attio-adapter.ts\n\nCredential Management\n\nCredentials are stored in the credentials table.\n\nKey columns:\n\n- organizationid for tenant isolation.\n- name, such as elevasis-attio.\n- provider, such as gmail or attio.\n- encryptedvalue containing encrypted JSON.\n\nSecurity rules:\n\n- RLS policies enforce tenant isolation.\n- Values are encrypted at rest.\n- Credentials do not cross organization boundaries.\n\nBest practices:\n\n- Store credentials encrypted in the database.\n- Use tenant-isolated credential names.\n- Validate credentials before API calls.\n- Rotate API keys regularly.\n- Use least-privilege OAuth scopes.\n\nDo not:\n\n- Hardcode credentials in code.\n- Share credentials across organizations.\n- Log credential values.\n- Store provider API keys in environment variables for tenant integrations.\n- Reuse the same credential name for dev and prod.\n\nCredential resolution flow:\n\n1. Tool requests a credential by name.\n2. Platform queries credentials with the current organizationid.\n3. Platform decrypts the credential.\n4. Adapter validates the credential against the provider schema.\n5. Tool passes the credential to the provider adapter.\n6. Usage is logged without credential values.\n\nImplementation reference:\n\ntext\npackages/core/src/execution/engine/tools/integration/tool.ts\n\nError Handling Patterns\n\nAdapters should convert provider errors into platform tooling errors.\n\nCommon error categories:\n\n- credentialsinvalid for invalid or missing credentials.\n- apierror for provider API errors.\n- networkerror for network or timeout failures.\n- validationerror for invalid parameters.\n- methodnotfound for unknown adapter methods.\n\nRetry strategy:\n\n- Network errors: retry with exponential backoff, commonly 1, 2, and 4 seconds.\n- Auth errors: fail immediately because the credential needs attention.\n- Validation errors: fail immediately because the input needs correction.\n- Rate limits: retry after reset when the provider exposes a reset time.\n- Workflow-level retries: commonly 3 attempts with 1, 4, and 9 minute delays.\n\nAdapter Development\n\nAdapter class location:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/{provider}-adapter.ts\n\nAdapter classes implement BaseIntegrationAdapter and contain call() and validateCredentials() methods.\n\nTool factory location:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/{provider}-tools.ts\n\nTool factories export create{Provider}{Operation}Tool(credentialName) functions.\n\nTests belong under:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/adapters/{provider}/tests/\n\nRegistration happens in:\n\ntext\npackages/core/src/execution/engine/tools/integration/server/index.ts\n\nTesting Strategies\n\nUnit testing:\n\n- Mock external APIs.\n- Cover credential validation.\n- Cover API call construction.\n- Cover response parsing.\n- Cover error handling.\n\nIntegration testing:\n\n- Use real API calls only with test accounts or test workspaces.\n- Clean up test data after tests.\n- Run in CI only when secret credentials are available.\n- Keep provider behavior mocked in unit tests.\n\nAgent testing:\n\n- Test agents using integration tools at the resource boundary.\n- Tenant project agent tests normally live under external/{org}/src/agents/{agent}/tests/.\n\nRelated References\n\n- /knowledge read knowledge.platform-composition-patterns\n- /knowledge read knowledge.platform-command-view\n- /knowledge read knowledge.platform-systems-overview"
|
|
1930
1476
|
}
|
|
1931
1477
|
];
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
search(query) {
|
|
1935
|
-
if (!query.trim()) return [];
|
|
1936
|
-
const q = query.trim().toLowerCase();
|
|
1937
|
-
const scored = [];
|
|
1938
|
-
for (const entry of entries) {
|
|
1939
|
-
let score = 0;
|
|
1940
|
-
if (entry.title.toLowerCase().includes(q)) score += 3;
|
|
1941
|
-
if (entry.summary.toLowerCase().includes(q)) score += 2;
|
|
1942
|
-
if (entry.bodyText.toLowerCase().includes(q)) score += 1;
|
|
1943
|
-
if (score > 0) scored.push({ id: entry.id, score });
|
|
1944
|
-
}
|
|
1945
|
-
return scored.sort((a, b) => b.score - a.score).map((s) => s.id);
|
|
1946
|
-
}
|
|
1947
|
-
};
|
|
1948
|
-
}
|
|
1949
|
-
function KnowledgeSearchBar({
|
|
1950
|
-
knowledgeNodes,
|
|
1951
|
-
onResults,
|
|
1952
|
-
placeholder = "Search knowledge\u2026"
|
|
1953
|
-
}) {
|
|
1954
|
-
const [query, setQuery] = useState("");
|
|
1955
|
-
const indexRef = useRef(null);
|
|
1956
|
-
useEffect(() => {
|
|
1957
|
-
indexRef.current = buildSearchIndex(knowledge_search_index_default);
|
|
1958
|
-
}, []);
|
|
1959
|
-
const nodeMapRef = useRef(/* @__PURE__ */ new Map());
|
|
1960
|
-
useEffect(() => {
|
|
1961
|
-
const map = /* @__PURE__ */ new Map();
|
|
1962
|
-
for (const node of knowledgeNodes) map.set(node.id, node);
|
|
1963
|
-
nodeMapRef.current = map;
|
|
1964
|
-
}, [knowledgeNodes]);
|
|
1965
|
-
const handleChange = (value) => {
|
|
1966
|
-
setQuery(value);
|
|
1967
|
-
if (!value.trim()) {
|
|
1968
|
-
onResults(null);
|
|
1969
|
-
return;
|
|
1970
|
-
}
|
|
1971
|
-
const ids = indexRef.current?.search(value) ?? [];
|
|
1972
|
-
const hits = ids.flatMap((id) => {
|
|
1973
|
-
const node = nodeMapRef.current.get(id);
|
|
1974
|
-
return node ? [node] : [];
|
|
1975
|
-
});
|
|
1976
|
-
onResults(hits);
|
|
1977
|
-
};
|
|
1978
|
-
const handleClear = () => {
|
|
1979
|
-
setQuery("");
|
|
1980
|
-
onResults(null);
|
|
1981
|
-
};
|
|
1982
|
-
return /* @__PURE__ */ jsx(
|
|
1983
|
-
TextInput,
|
|
1984
|
-
{
|
|
1985
|
-
value: query,
|
|
1986
|
-
onChange: (e) => handleChange(e.currentTarget.value),
|
|
1987
|
-
placeholder,
|
|
1988
|
-
leftSection: /* @__PURE__ */ jsx(IconSearch, { size: 16 }),
|
|
1989
|
-
rightSection: query ? /* @__PURE__ */ jsx(IconX, { size: 14, style: { cursor: "pointer", color: "var(--color-text-subtle)" }, onClick: handleClear }) : null,
|
|
1990
|
-
styles: {
|
|
1991
|
-
input: {
|
|
1992
|
-
backgroundColor: "var(--color-surface)",
|
|
1993
|
-
borderColor: "var(--color-border)",
|
|
1994
|
-
color: "var(--color-text)"
|
|
1995
|
-
}
|
|
1996
|
-
}
|
|
1997
|
-
}
|
|
1998
|
-
);
|
|
1999
|
-
}
|
|
2000
|
-
|
|
2001
|
-
export { EdgeChip, EdgeGroup, EdgeRelationshipGroup, KNOWLEDGE_ICON_TOKEN_BY_KIND, KeyField, KindChip, KnowledgeSearchBar, KnowledgeTree, NodeDescribeShell, NodeHeader, NodeMetadataFooter, RelatedKnowledgeSection, byKind, getKnowledgeIconToken };
|
|
1478
|
+
|
|
1479
|
+
export { knowledge_search_index_default as default };
|