@hanzo/rooms 0.1.73 → 0.1.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/assets/personas/ada.md +187 -0
  2. package/assets/personas/adi_shamir.md +155 -0
  3. package/assets/personas/al_khwarizmi.md +168 -0
  4. package/assets/personas/al_kindi.md +172 -0
  5. package/assets/personas/alan_kay.md +154 -0
  6. package/assets/personas/analyst.md +138 -0
  7. package/assets/personas/andrew_yao.md +155 -0
  8. package/assets/personas/auguste_kerckhoffs.md +168 -0
  9. package/assets/personas/barbara_liskov.md +154 -0
  10. package/assets/personas/butler_lampson.md +156 -0
  11. package/assets/personas/charles_babbage.md +175 -0
  12. package/assets/personas/creative.md +141 -0
  13. package/assets/personas/curry.md +169 -0
  14. package/assets/personas/cynthia_dwork.md +158 -0
  15. package/assets/personas/david_chaum.md +157 -0
  16. package/assets/personas/david_parnas.md +153 -0
  17. package/assets/personas/dennis_ritchie.md +153 -0
  18. package/assets/personas/dijkstra.md +187 -0
  19. package/assets/personas/donald_knuth.md +156 -0
  20. package/assets/personas/douglas_engelbart.md +175 -0
  21. package/assets/personas/edgar_codd.md +151 -0
  22. package/assets/personas/engineer.md +140 -0
  23. package/assets/personas/feynman.md +178 -0
  24. package/assets/personas/finney.md +187 -0
  25. package/assets/personas/fred_brooks.md +152 -0
  26. package/assets/personas/geoffrey_hinton.md +194 -0
  27. package/assets/personas/herbert_simon.md +190 -0
  28. package/assets/personas/hickey.md +169 -0
  29. package/assets/personas/hopper.md +191 -0
  30. package/assets/personas/hubert_dreyfus.md +184 -0
  31. package/assets/personas/humberto_maturana.md +156 -0
  32. package/assets/personas/index.json +1 -0
  33. package/assets/personas/jcr_licklider.md +178 -0
  34. package/assets/personas/jobs.md +177 -0
  35. package/assets/personas/joe_armstrong.md +155 -0
  36. package/assets/personas/john_backus.md +179 -0
  37. package/assets/personas/john_mccarthy.md +177 -0
  38. package/assets/personas/jon_postel.md +149 -0
  39. package/assets/personas/joseph_weizenbaum.md +185 -0
  40. package/assets/personas/judea_pearl.md +192 -0
  41. package/assets/personas/karen_sparck_jones.md +154 -0
  42. package/assets/personas/konrad_zuse.md +175 -0
  43. package/assets/personas/kunihiko_fukushima.md +152 -0
  44. package/assets/personas/leslie_lamport.md +156 -0
  45. package/assets/personas/markov.md +171 -0
  46. package/assets/personas/mentor.md +141 -0
  47. package/assets/personas/nancy_lynch.md +154 -0
  48. package/assets/personas/niklaus_wirth.md +154 -0
  49. package/assets/personas/norbert_wiener.md +179 -0
  50. package/assets/personas/north-star.md +138 -0
  51. package/assets/personas/peter_naur.md +151 -0
  52. package/assets/personas/pike.md +165 -0
  53. package/assets/personas/radia_perlman.md +156 -0
  54. package/assets/personas/raj_reddy.md +154 -0
  55. package/assets/personas/ralph_merkle.md +152 -0
  56. package/assets/personas/richard_sutton.md +156 -0
  57. package/assets/personas/robin_milner.md +149 -0
  58. package/assets/personas/shafi_goldwasser.md +156 -0
  59. package/assets/personas/shannon.md +184 -0
  60. package/assets/personas/stafford_beer.md +157 -0
  61. package/assets/personas/taher_elgamal.md +180 -0
  62. package/assets/personas/tesla.md +181 -0
  63. package/assets/personas/tony_hoare.md +153 -0
  64. package/assets/personas/torvalds.md +188 -0
  65. package/assets/personas/tpope.md +163 -0
  66. package/assets/personas/turing.md +188 -0
  67. package/assets/personas/whitfield_diffie.md +159 -0
  68. package/assets/personas/zeekay.md +151 -0
  69. package/dist/App.js +26 -22
  70. package/dist/Chat.js +63 -46
  71. package/dist/Column.d.ts +3 -3
  72. package/dist/Column.js +5 -27
  73. package/dist/Me.d.ts +8 -3
  74. package/dist/Me.js +79 -108
  75. package/dist/Palette.d.ts +18 -0
  76. package/dist/Palette.js +131 -0
  77. package/dist/Rail.js +3 -15
  78. package/dist/Recents.d.ts +1 -2
  79. package/dist/Recents.js +9 -9
  80. package/dist/Shell.d.ts +7 -0
  81. package/dist/Shell.js +65 -46
  82. package/dist/Top.d.ts +17 -0
  83. package/dist/Top.js +58 -0
  84. package/dist/agents.d.ts +64 -0
  85. package/dist/agents.js +62 -0
  86. package/dist/crew.d.ts +26 -5
  87. package/dist/crew.js +208 -83
  88. package/dist/failure.d.ts +1 -1
  89. package/dist/failure.js +7 -6
  90. package/dist/help.d.ts +43 -0
  91. package/dist/help.js +58 -0
  92. package/dist/lib/web.d.ts +6 -0
  93. package/dist/lib/web.js +39 -7
  94. package/dist/recent.d.ts +2 -2
  95. package/dist/recent.js +3 -5
  96. package/dist/seek.d.ts +37 -0
  97. package/dist/seek.js +55 -0
  98. package/dist/team.js +3 -11
  99. package/dist/thread.js +7 -2
  100. package/dist/view.d.ts +44 -0
  101. package/dist/view.js +98 -0
  102. package/package.json +5 -5
  103. package/dist/Account.d.ts +0 -12
  104. package/dist/Account.js +0 -65
  105. package/dist/Find.d.ts +0 -7
  106. package/dist/Find.js +0 -82
  107. package/dist/Profile.d.ts +0 -4
  108. package/dist/Profile.js +0 -67
  109. package/dist/Standing.d.ts +0 -4
  110. package/dist/Standing.js +0 -16
package/dist/Shell.js CHANGED
@@ -13,13 +13,13 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
13
13
  // holds arrangement and words and owns no pixels of its own. That is the whole
14
14
  // architecture: a stranger with `@hanzo/ui`, `@hanzo/ai` and `@hanzo/iam` can
15
15
  // assemble this room, because nothing in it is private to hanzo.ai.
16
- import { createContext, lazy, Suspense, useContext, useEffect, useMemo, useRef, useState, } from "react";
16
+ import { createContext, lazy, Suspense, useCallback, useContext, useEffect, useMemo, useRef, useState, } from "react";
17
17
  import { createPortal } from "react-dom";
18
- import { MessageSquare, Code2, Inbox, Bot, ListChecks, HardDrive, Settings2, UserPlus, Building2, Search, Hash, AtSign, Lock, Workflow, Plus, ArrowLeft, ArrowRight, Send, Minus, Blocks, Star, FolderOpen, Activity, BookOpen, SquareTerminal, Share2, Sparkles, PanelLeft, PanelRight, Users, Globe, Cpu, Boxes, Wallet, Video, Calendar, LayoutGrid, ArrowUpLeft, } from "lucide-react";
18
+ import { MessageSquare, Code2, Inbox, Bot, ListChecks, HardDrive, Settings2, UserPlus, PanelLeftClose, Search, Hash, AtSign, Lock, Workflow, Plus, ArrowLeft, ArrowRight, Send, Minus, Blocks, Star, FolderOpen, Activity, BookOpen, SquareTerminal, Share2, PanelLeft, PanelRight, Users, Globe, Cpu, Boxes, Wallet, Video, Calendar, LayoutGrid, ArrowUpLeft, } from "lucide-react";
19
19
  import { AiProvider, useAgents, useInbox, useModels, usePeople, } from "@hanzo/ai/react";
20
20
  import { PINNED } from "./recent.js";
21
21
  import { ConversationMenu, ThreadRow, useThreadList } from "./thread.js";
22
- import { Box, XStack, YStack, Text } from "@hanzo/ui";
22
+ import { Box, Shortcut, XStack, YStack, Text } from "@hanzo/ui";
23
23
  // THE PAPER LADDER. `sheet(n)` is a fill AND the light on it, published as a
24
24
  // pair by @hanzo/design, so a surface asks for a HEIGHT instead of composing a
25
25
  // shadow — which is the only way three panels on one screen end up lit from one
@@ -27,14 +27,17 @@ import { Box, XStack, YStack, Text } from "@hanzo/ui";
27
27
  import { scrim, sheet } from "@hanzo/ui/glass";
28
28
  import { Sidebar, SidebarIconButton, SidebarNewChat, SidebarScroll, SidebarSection, SidebarItem, SidebarFolder, } from "@hanzo/ui/chat";
29
29
  import { OrgProjectSwitcher, useIam, useOrganizations, } from "@hanzo/iam/react";
30
- import { Grip } from "@hanzo/build";
30
+ import { SideProvider, useSplit } from "@hanzo/build";
31
+ import { Grip } from "@hanzo/ui";
31
32
  import { HanzoMark, } from "@hanzogui/shell";
32
33
  import { useHydrated } from "./lib/hydrated.js";
33
34
  import { useHost } from "./lib/hostname.js";
34
35
  import { useAi } from "./lib/ai.js";
35
36
  import { useTiers, useHidden } from "./lib/tiers.js";
36
37
  import { useAccount } from "./lib/account.js";
37
- import { hasSession, superAdmin } from "./lib/session.js";
38
+ import { bearer, hasSession, org as scoped, pick as choose } from "./lib/session.js";
39
+ import { api } from "./lib/api.js";
40
+ import { useSignOut } from "./lib/signout.js";
38
41
  import { enter, LOGIN } from "./lib/destination.js";
39
42
  import { address, sharedWithMe } from "./lib/share.js";
40
43
  import { list } from "./lib/list.js";
@@ -60,6 +63,7 @@ const FIELD = {
60
63
  * custom properties. */
61
64
  const GUTTER_START = "calc(-1 * var(--pane-gap, 0px))";
62
65
  const GUTTER = "max(8px, var(--pane-gap, 8px))";
66
+ const TOUCH_START = "calc(-22px - var(--pane-gap, 0px) / 2)";
63
67
  const HOME_INDICATOR = "env(safe-area-inset-bottom)";
64
68
  import { invite } from "./lib/home.js";
65
69
  import { reach } from "./lib/reach.js";
@@ -71,10 +75,9 @@ import { useTeamRooms, teamKey, roomHref } from "./rooms.js";
71
75
  import { apex, app, dev, ENTRY, room as moves, WORKSPACE } from "./lib/host.js";
72
76
  import { netKey } from "./conversations.js";
73
77
  import { FIRST, SettingsList, } from "./Settings.js";
74
- import { Profile } from "./Profile.js";
75
- import { SupportPicker } from "./Support.js";
76
- import { Find } from "./Find.js";
77
- import { Account } from "./Account.js";
78
+ import { Me } from "./Me.js";
79
+ import { Palette } from "./Palette.js";
80
+ import { summons } from "./seek.js";
78
81
  import { called } from "./team.js";
79
82
  import { checkoutUrl } from './lib/pay.js';
80
83
  /** Tabs: the terminals, at their own host. */
@@ -831,14 +834,11 @@ function Bots({ show, owner, filter = "", onPick, navigate, }) {
831
834
  }
832
835
  /** A phone's tab bar: the rail along the foot, above the home indicator. */
833
836
  const TABS_HEIGHT = "calc(56px + env(safe-area-inset-bottom))";
834
- /** A column's width, px: the floor a drag holds, the ceiling, and the default a double-click returns to. */
837
+ /** The sidebar's width, px: the floor a drag holds, the ceiling, and the default a double-click returns to. */
835
838
  const FLOOR = 200;
836
839
  const CEIL = 480;
837
840
  /** Per-reader, per-browser. A width that resets is a width you set every load. */
838
841
  const SPAN = "hanzo.sidebar.span";
839
- const BESIDE = "hanzo.aside.span";
840
- /** The column beside a room: its default width, px. */
841
- const BESIDE_WIDTH = 380;
842
842
  /** The rooms' sidebar: its default width, px. */
843
843
  const SIDEBAR_WIDTH = 244;
844
844
  /**
@@ -875,6 +875,13 @@ export function Beside({ children }) {
875
875
  * the app at `/` lay a room out with this, so a room's column is one mechanism.
876
876
  * `swap` stands in the pane in the room's place (a chosen Settings row).
877
877
  *
878
+ * The column is @hanzo/build's (`useSplit`), the one Dev's runs stand their
879
+ * panel in: its edge, the width the person drags (kept in their preferences
880
+ * where a `PrefsProvider` is above, as in the app), the pane hidden while the
881
+ * column is pulled over it until Show chat, and full screen from the panel's
882
+ * strip. The pane is hidden, never unmounted, so the conversation keeps its
883
+ * state.
884
+ *
878
885
  * `framed` is the app at `/`: both are panes floating on the frame's ground
879
886
  * (app/_web.tsx), filled with `--pane-fill` and cut by `pane()`, and the edge
880
887
  * that sizes the column sits in the gutter between them. In a room they lie on
@@ -884,7 +891,7 @@ export function Beside({ children }) {
884
891
  */
885
892
  export function Paned({ children, swap, framed = false, bare = false, }) {
886
893
  const { aside, showAside } = useOpen();
887
- const beside = useSpan(BESIDE, BESIDE_WIDTH, FLOOR, CEIL);
894
+ const split = useSplit(Boolean(aside), "chat", () => showAside(null));
888
895
  // State and not a ref: a ref set during commit does not re-render, so the
889
896
  // portal would have nowhere to go on the pass that created its target.
890
897
  const [slot, setSlot] = useState(null);
@@ -896,17 +903,19 @@ export function Paned({ children, swap, framed = false, bare = false, }) {
896
903
  ? bare
897
904
  ? null
898
905
  : { ...pane(), overflow: "hidden" }
899
- : { ...sheet(0), borderLeftWidth: 1, borderColor: "var(--edge, var(--white-06))" }), flex: 1, minW: 0, minH: 0, children: swap ?? (_jsx(Framed.Provider, { value: framed, children: _jsx(Slot.Provider, { value: slot, children: children }) })) }), aside ? (_jsxs(YStack, { render: "aside", "data-slot": "side", gridArea: "side", ...pane(framed ? undefined : sheet(1).backgroundColor),
906
+ : { ...sheet(0), borderLeftWidth: 1, borderColor: "var(--edge, var(--white-06))" }), ref: split.main,
907
+ // inline-style: the pane hidden while the column has its room, written by a drag too (useSplit).
908
+ style: split.mainStyle, flex: 1, minW: 0, minH: 0, children: swap ?? (_jsx(Framed.Provider, { value: framed, children: _jsx(SideProvider, { value: split.side, children: _jsx(Slot.Provider, { value: slot, children: children }) }) })) }), aside ? (_jsxs(YStack, { render: "aside", "data-slot": "side", gridArea: "side", ...pane(framed ? undefined : sheet(1).backgroundColor),
900
909
  // In the frame the gutter stands between the pane and this column.
901
- ml: framed ? "var(--pane-gap)" : undefined, ref: beside.ref,
902
- // inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
903
- style: beside.style, position: "relative", width: "var(--span)",
904
- // Half the WINDOW. Half of an `auto` grid track is half of this
905
- // column's own width, so the rooms' shell drew it at half its span
906
- // beside an empty strip of the same size.
907
- maxW: "50vw", shrink: 0, minH: 0, display: "none", $lg: { display: "flex" }, children: [_jsx(Grip, { side: "right", span: beside.span, floor: FLOOR, ceil: CEIL, reset: BESIDE_WIDTH, onSpan: beside.move, onKeep: beside.keep, onShut: () => showAside(null), label: "Resize side panel",
910
+ ml: framed ? "var(--pane-gap)" : undefined, ref: split.aside,
911
+ // inline-style: the width a drag rewrites on the element itself, and full screen (useSplit).
912
+ style: split.asideStyle, position: "relative", shrink: 0, minH: 0, display: "none", $lg: { display: "flex" }, children: [split.grip ? (_jsx(Grip, { ...split.grip, label: "Resize side panel", bar: "inline",
913
+ // The handle halfway down the edge, where a hand looks for it.
914
+ justify: "center",
908
915
  // In the gutter the frame leaves between the panes; a room has none.
909
- l: GUTTER_START, width: GUTTER }), _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", rounded: "var(--pane-round)", ref: (el) => setSlot(el instanceof HTMLElement ? el : null) })] })) : null] }));
916
+ l: GUTTER_START, width: GUTTER,
917
+ // A finger's band, centred on the gutter.
918
+ $touchable: { l: TOUCH_START, width: 44 } })) : null, _jsx(YStack, { flex: 1, minH: 0, overflowY: "auto", overflowX: "hidden", rounded: "var(--pane-round)", ref: (el) => setSlot(el instanceof HTMLElement ? el : null) })] })) : null] }));
910
919
  }
911
920
  /**
912
921
  * THE RAIL: where you can go, as marks with their names under them. It is the
@@ -992,6 +1001,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
992
1001
  // hook it no longer runs.
993
1002
  const hydrated = useHydrated();
994
1003
  const [finding, setFinding] = useState(false);
1004
+ const found = useCallback(() => setFinding(false), []);
995
1005
  /**
996
1006
  * WHICH SETTINGS ROW IS OPEN, and null for none. ONE value, not two.
997
1007
  *
@@ -1001,12 +1011,6 @@ function Frame({ mode, children, navigate, back, forward, }) {
1001
1011
  * act — which is why there is no second `panel` state beside this one.
1002
1012
  */
1003
1013
  const { settings: settingsPane, showSettings } = useOpen();
1004
- // The profile is a COLUMN, not a modal, so it is not one of the panels above:
1005
- // those lift off the window and dim it, this one stands in the row beside the
1006
- // room. It is the slot the thread view will share.
1007
- const [profiling, setProfiling] = useState(false);
1008
- // Every organization, for a SuperAdmin to step into (components/workspace/Support.tsx).
1009
- const [supporting, setSupporting] = useState(false);
1010
1014
  /**
1011
1015
  * The sidebar is open by default on desktop and stays open.
1012
1016
  *
@@ -1073,7 +1077,7 @@ function Frame({ mode, children, navigate, back, forward, }) {
1073
1077
  const onPick = typeof window !== "undefined" && window.innerWidth < 1024 ? dismiss : undefined;
1074
1078
  useEffect(() => {
1075
1079
  const shortcut = (event) => {
1076
- if ((event.metaKey || event.ctrlKey) && event.key.toLowerCase() === "k") {
1080
+ if (summons(event)) {
1077
1081
  event.preventDefault();
1078
1082
  setFinding(true);
1079
1083
  }
@@ -1164,9 +1168,10 @@ function Frame({ mode, children, navigate, back, forward, }) {
1164
1168
  /**
1165
1169
  * WHERE THE PALETTE CAN GO WITHOUT ASKING ANYONE.
1166
1170
  *
1167
- * The rooms this identity may open, and the surfaces above. The lists that
1168
- * have to be read — conversations, projects — are `Find`'s, which is why they
1169
- * are not here: this half is known before anyone types.
1171
+ * The rooms this identity may open, and the surfaces above, as the palette's
1172
+ * Actions; this Settings and Customize stand in for the palette's own. The
1173
+ * lists that have to be read — conversations, projects, agents, skills — are
1174
+ * the palette's, which is why they are not here.
1170
1175
  */
1171
1176
  const onApex = apex(useHost());
1172
1177
  const places = useMemo(() => [CHAT, ...PLACES].filter((place) => shows(place, tiers, hidden, onApex)), [tiers, hidden, onApex]);
@@ -1181,19 +1186,17 @@ function Frame({ mode, children, navigate, back, forward, }) {
1181
1186
  // Home's is the brand — one row reading "Hanzo" among Chat, Dev and
1182
1187
  // Drive names the company rather than the room.
1183
1188
  id: place.id,
1184
- title: place.label,
1185
- category: "Apps",
1186
- icon: _jsx(place.icon, { size: 15, "aria-hidden": true }),
1187
- href: place.route,
1189
+ label: place.label,
1190
+ icon: _jsx(place.icon, { size: 16, "aria-hidden": true }),
1191
+ run: () => navigate?.(place.route),
1188
1192
  })),
1189
1193
  ...tools.map((tool) => ({
1190
1194
  id: tool.id,
1191
- title: tool.label,
1192
- category: "Workspace",
1193
- icon: _jsx(tool.icon, { size: 15, "aria-hidden": true }),
1194
- action: tool.go,
1195
+ label: tool.label,
1196
+ icon: _jsx(tool.icon, { size: 16, "aria-hidden": true }),
1197
+ run: tool.go,
1195
1198
  })),
1196
- ], [tiers, hidden, onApex, tools]);
1199
+ ], [tiers, hidden, onApex, tools, navigate]);
1197
1200
  // The org IS the workspace. Naming it twice is how the bar and the rail come
1198
1201
  // to disagree about which one you are standing in.
1199
1202
  const workspace = currentOrg?.displayName || currentOrg?.name || "Hanzo";
@@ -1208,6 +1211,20 @@ function Frame({ mode, children, navigate, back, forward, }) {
1208
1211
  const settled = hydrated && !isLoading;
1209
1212
  const { user: account } = useAccount();
1210
1213
  const signedIn = isAuthenticated || Boolean(user) || Boolean(account) || hasSession();
1214
+ const logout = useSignOut();
1215
+ const face = account?.avatar ?? user?.avatar;
1216
+ // The account menu's reading of this host: who, in which organization, and how they leave.
1217
+ const who = {
1218
+ api: api(),
1219
+ token: bearer,
1220
+ org: scoped(),
1221
+ person: {
1222
+ name: account?.name || user?.displayName || user?.name || user?.email || "",
1223
+ email: user?.email || "",
1224
+ avatar: typeof face === "string" && face.startsWith("https://") ? face : "",
1225
+ },
1226
+ signOut: () => void logout(),
1227
+ };
1211
1228
  const list = (_jsxs(Sidebar, { ref: side.ref,
1212
1229
  // A phone's sheet is never wider than 280, whatever the laptop kept.
1213
1230
  // inline-style: `--span`, the width a drag rewrites on the element itself (span.ts); no prop sets a variable.
@@ -1226,16 +1243,18 @@ function Frame({ mode, children, navigate, back, forward, }) {
1226
1243
  // FILTER, not Search. Two controls that both said Search did two
1227
1244
  // different things: this one narrows the list beside you, and the
1228
1245
  // bar above opens the palette that finds anything anywhere.
1229
- placeholder: "Filter", "aria-label": "Filter sidebar" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2" })] }) }), settingsPane ? (_jsx(SidebarScroll, { children: _jsx(SettingsList, { active: settingsPane, filter: filter, onPick: pick, onBack: () => showSettings(null) }) })) : (_jsx(_Fragment, { children: _jsx(SidebarScroll, { children: _jsxs(_Fragment, { children: [settled ? (_jsx(Index, { mode: mode, owner: owner, filter: filter, onPick: onPick, navigate: navigate })) : null, products.length ? (_jsx(SidebarFolder, { name: "Cloud & Platform", open: filter ? true : undefined, children: products.map((prod) => (_jsx(SidebarItem, { icon: _jsx(prod.icon, { size: 13, "aria-hidden": true }), onPress: () => window.open(prod.href, "_blank", "noopener"), children: prod.label }, prod.id))) })) : null] }) }) })), settled && onApex ? _jsx(Leave, { href: signedIn ? ENTRY : WORKSPACE, children: "Workspace" }) : null, settled ? (signedIn ? (_jsxs(_Fragment, { children: [superAdmin() ? (_jsx(SidebarItem, { icon: _jsx(Building2, { size: 15, "aria-hidden": true }), onPress: () => setSupporting(true), children: "All organizations" })) : null, _jsx(SidebarItem, { icon: _jsx(UserPlus, { size: 15, "aria-hidden": true }), onPress: () => window.open(invite(), "_blank", "noopener"), children: "Invite Teammates" }), _jsx(SidebarItem, { icon: _jsx(Share2, { size: 15, "aria-hidden": true }), onPress: () => {
1246
+ placeholder: "Filter", "aria-label": "Filter sidebar" }), flex: 1, minW: 0, bg: "transparent", borderWidth: 0, outlineStyle: "none", color: "inherit", fontSize: "$2" })] }) }), settingsPane ? (_jsx(SidebarScroll, { children: _jsx(SettingsList, { active: settingsPane, filter: filter, onPick: pick, onBack: () => showSettings(null) }) })) : (_jsx(_Fragment, { children: _jsx(SidebarScroll, { children: _jsxs(_Fragment, { children: [settled ? (_jsx(Index, { mode: mode, owner: owner, filter: filter, onPick: onPick, navigate: navigate })) : null, products.length ? (_jsx(SidebarFolder, { name: "Cloud & Platform", open: filter ? true : undefined, children: products.map((prod) => (_jsx(SidebarItem, { icon: _jsx(prod.icon, { size: 13, "aria-hidden": true }), onPress: () => window.open(prod.href, "_blank", "noopener"), children: prod.label }, prod.id))) })) : null] }) }) })), settled && onApex ? _jsx(Leave, { href: signedIn ? ENTRY : WORKSPACE, children: "Workspace" }) : null, settled ? (signedIn ? (_jsxs(_Fragment, { children: [_jsx(SidebarItem, { icon: _jsx(UserPlus, { size: 15, "aria-hidden": true }), onPress: () => window.open(invite(), "_blank", "noopener"), children: "Invite Teammates" }), _jsx(SidebarItem, { icon: _jsx(Share2, { size: 15, "aria-hidden": true }), onPress: () => {
1230
1247
  navigate?.(site("/referral"));
1231
1248
  dismiss();
1232
- }, children: "Share Hanzo" }), _jsx(SidebarItem, { icon: _jsx(Sparkles, { size: 15, "aria-hidden": true }), onPress: () => window.open(BILLING, "_blank", "noopener"), children: "Upgrade" }), _jsx(Account, { onToggleCollapse: toggle, onProfile: () => setProfiling(true), onAppearance: () => showSettings("general"), onUsage: () => showSettings("usage"), onKeys: () => showSettings("keys") })] })) : null) : null] }));
1249
+ }, children: "Share Hanzo" }), _jsxs(XStack, { items: "center", gap: "$1", px: "$1.5", py: "$1", borderTopWidth: 1, borderColor: "$borderColor", children: [_jsx(YStack, { flex: 1, minW: 0, children: _jsx(Me, { host: who, onOrg: (o) => (choose(o), window.location.reload()) }) }), _jsx(Box, { render: "button", onClick: toggle, "aria-label": "Collapse sidebar", p: "$2", rounded: "$2", hoverStyle: { bg: "$hover" }, cursor: "pointer", children: _jsx(PanelLeftClose, { size: 16, "aria-hidden": true }) })] })] })) : null) : null] }));
1233
1250
  return (_jsxs(YStack, { "data-slot": "workspace", "data-hydrated": settled ? "true" : undefined, "data-sidebar": asked ? "open" : "shut", display: "grid", gridTemplateColumns: `${asked ? 0 : 64}px auto minmax(0, 1fr) auto`, gridTemplateRows: "44px minmax(0, 1fr)", gridTemplateAreas: '"rail bar bar bar" "rail list pane side"', "$max-md": {
1234
1251
  gridTemplateColumns: "minmax(0, 1fr)",
1235
1252
  gridTemplateRows: `44px minmax(0, 1fr) ${TABS_HEIGHT}`,
1236
1253
  gridTemplateAreas: '"bar" "pane" "rail"',
1237
- }, position: "relative", width: "100%", height: "100dvh", overflow: "hidden", bg: "$background", backgroundImage: "var(--pane-ground)", children: [_jsx(Rail, { mode: mode, places: places, open: asked, team: onApex ? (signedIn ? ENTRY : WORKSPACE) : undefined, navigate: navigate, onSettings: () => showSettings(FIRST), onMore: () => setFinding(true) }), _jsxs(XStack, { render: "header", "data-slot": "bar", gridArea: "bar", ...sheet(0), height: 44, shrink: 0, items: "center", px: "$3", gap: "$3", borderBottomWidth: 1, borderColor: "var(--edge, var(--white-06))", children: [_jsxs(XStack, { items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: shown ? "Close sidebar" : "Open sidebar", onPress: toggle, onMouseEnter: () => setPeeking(true), children: _jsx(PanelLeft, { size: 16, "aria-hidden": true }) }), back || forward ? (_jsxs(XStack, { display: "none", $md: { display: "flex" }, items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: "Back", onPress: () => back?.(), children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: "Forward", onPress: () => forward?.(), children: _jsx(ArrowRight, { size: 16, "aria-hidden": true }) })] })) : null] }), _jsx(XStack, { flex: 1, justify: "center", children: _jsx(YStack, { width: 440, maxW: "100%", position: "relative", children: _jsxs(Box, { render: "button", onClick: () => setFinding(true), display: "flex", flexDirection: "row", "data-slot": "find", items: "center", "$max-md": { justify: "center" }, gap: "$2", width: "100%", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 13, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsxs(Text, { fontSize: "$1", color: "$soft", "$max-md": { display: "none" }, children: ["Search ", workspace] }), _jsx(XStack, { ml: "auto", items: "center", justify: "center", height: 18, px: "$1", rounded: "$1", bg: "$edge", "$max-md": { display: "none" }, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "$soft", children: "\u2318K" }) })] }) }) }), _jsxs(XStack, { shrink: 0, minW: 0, justify: "flex-end", items: "center", gap: "$2", children: [settled && !signedIn ? (_jsx(Box, { ...TAP, render: "button", onClick: () => enter(LOGIN), display: "flex", items: "center", justify: "center", cursor: "pointer", children: _jsx(XStack, { items: "center", justify: "center", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "$ink", children: "Sign in" }) }) })) : null, _jsxs(XStack, { display: "none", $lg: { display: "flex" }, items: "center", gap: "$1", children: [_jsx(SidebarIconButton, { ...TAP, label: aside === "roster" ? "Hide directory & roster" : "Show directory & roster", onPress: () => showAside(aside === "roster" ? null : "roster"), children: _jsx(Users, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: aside === "tasks" ? "Close side panel" : "Open side panel", onPress: () => showAside(aside === "tasks" ? null : "tasks"), children: _jsx(PanelRight, { size: 16, "aria-hidden": true }) })] })] })] }), shown ? (_jsx(Box, { render: "button", "aria-label": "Close sidebar", onClick: dismiss, ...scrim, "data-slot": "scrim", position: "absolute", t: 44, r: 0, b: TABS_HEIGHT, l: 0, z: "var(--z-scrim)", $md: { display: "none" } })) : null, shown ? (_jsx(XStack, { "data-slot": "list", gridArea: "list", position: asked ? "relative" : "absolute", ...(asked ? {} : { top: 44, bottom: 0, left: 64 }), "$max-md": { position: "fixed", t: 44, r: 0, b: TABS_HEIGHT, l: 0, width: "100%" }, z: "var(--z-drawer)", bg: "$background", onMouseEnter: () => setPeeking(true), onMouseLeave: () => {
1254
+ }, position: "relative", width: "100%", height: "100dvh", overflow: "hidden", bg: "$background", backgroundImage: "var(--pane-ground)", children: [_jsx(Rail, { mode: mode, places: places, open: asked, team: onApex ? (signedIn ? ENTRY : WORKSPACE) : undefined, navigate: navigate, onSettings: () => showSettings(FIRST), onMore: () => setFinding(true) }), _jsxs(XStack, { render: "header", "data-slot": "bar", gridArea: "bar", ...sheet(0), height: 44, shrink: 0, items: "center", px: "$3", gap: "$3", borderBottomWidth: 1, borderColor: "var(--edge, var(--white-06))", children: [_jsxs(XStack, { items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: shown ? "Close sidebar" : "Open sidebar", onPress: toggle, onMouseEnter: () => setPeeking(true), children: _jsx(PanelLeft, { size: 16, "aria-hidden": true }) }), back || forward ? (_jsxs(XStack, { display: "none", $md: { display: "flex" }, items: "center", gap: "$1", shrink: 0, children: [_jsx(SidebarIconButton, { ...TAP, label: "Back", onPress: () => back?.(), children: _jsx(ArrowLeft, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: "Forward", onPress: () => forward?.(), children: _jsx(ArrowRight, { size: 16, "aria-hidden": true }) })] })) : null] }), _jsx(XStack, { flex: 1, justify: "center", children: _jsx(YStack, { width: 440, maxW: "100%", position: "relative", children: _jsxs(Box, { render: "button", onClick: () => setFinding(true), display: "flex", flexDirection: "row", "data-slot": "find", items: "center", "$max-md": { justify: "center" }, gap: "$2", width: "100%", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: [_jsx(Search, { size: 13, "aria-hidden": true, color: "var(--soft, var(--muted-foreground))" }), _jsxs(Text, { fontSize: "$1", color: "$soft", "$max-md": { display: "none" }, children: ["Search ", workspace] }), _jsx(Shortcut, { keys: ["Mod", "K"], ml: "auto", "$max-md": { display: "none" } })] }) }) }), _jsxs(XStack, { shrink: 0, minW: 0, justify: "flex-end", items: "center", gap: "$2", children: [settled && !signedIn ? (_jsx(Box, { ...TAP, render: "button", onClick: () => enter(LOGIN), display: "flex", items: "center", justify: "center", cursor: "pointer", children: _jsx(XStack, { items: "center", justify: "center", height: CONTROL, px: "$2.5", rounded: "$2", borderWidth: 1, borderColor: "$borderColor", hoverStyle: { bg: '$hover' }, children: _jsx(Text, { fontSize: "$1", fontWeight: "600", color: "$ink", children: "Sign in" }) }) })) : null, _jsxs(XStack, { display: "none", $lg: { display: "flex" }, items: "center", gap: "$1", children: [_jsx(SidebarIconButton, { ...TAP, label: aside === "roster" ? "Hide directory & roster" : "Show directory & roster", onPress: () => showAside(aside === "roster" ? null : "roster"), children: _jsx(Users, { size: 16, "aria-hidden": true }) }), _jsx(SidebarIconButton, { ...TAP, label: aside === "tasks" ? "Close side panel" : "Open side panel", onPress: () => showAside(aside === "tasks" ? null : "tasks"), children: _jsx(PanelRight, { size: 16, "aria-hidden": true }) })] })] })] }), shown ? (_jsx(Box, { render: "button", "aria-label": "Close sidebar", onClick: dismiss, ...scrim, "data-slot": "scrim", position: "absolute", t: 44, r: 0, b: TABS_HEIGHT, l: 0, z: "var(--z-scrim)", $md: { display: "none" } })) : null, shown ? (_jsx(XStack, { "data-slot": "list", gridArea: "list", position: asked ? "relative" : "absolute", ...(asked ? {} : { top: 44, bottom: 0, left: 64 }), "$max-md": { position: "fixed", t: 44, r: 0, b: TABS_HEIGHT, l: 0, width: "100%" }, z: "var(--z-drawer)", bg: "$background", onMouseEnter: () => setPeeking(true), onMouseLeave: () => {
1238
1255
  if (!asked)
1239
1256
  setPeeking(false);
1240
- }, children: list })) : null, _jsx(Paned, { swap: settingsPane ? _jsx(Suspense, { fallback: null, children: _jsx(SettingsPane, { id: settingsPane }) }) : undefined, children: children }), _jsx(Profile, { open: profiling, onClose: () => setProfiling(false) }), supporting ? _jsx(SupportPicker, { onClose: () => setSupporting(false) }) : null, finding ? (_jsx(Find, { doors: doors, onClose: () => setFinding(false), navigate: navigate })) : null] }));
1257
+ }, children: list })) : null, _jsx(Paned, { swap: settingsPane ? _jsx(Suspense, { fallback: null, children: _jsx(SettingsPane, { id: settingsPane }) }) : undefined, children: children }), finding ? (_jsx(Palette, { actions: doors, onClose: found,
1258
+ // A chat opens in this workspace's Chat, the palette having picked it; the rest is the app's.
1259
+ go: (to) => navigate?.(to.startsWith(CHAT.route) ? CHAT.route : site(to)) })) : null] }));
1241
1260
  }
package/dist/Top.d.ts ADDED
@@ -0,0 +1,17 @@
1
+ import { type Host } from '@hanzo/build';
2
+ import type { RailLink } from '@hanzo/ui/chat';
3
+ import { type Place } from './lib/host.js';
4
+ export declare function Top({ host, mode, places, go, onMenu, onChat, onDev, }: {
5
+ host: Host;
6
+ mode: Place['mode'];
7
+ /** The mode's places, where they are not Dev's (Studio's `nav`). */
8
+ places?: {
9
+ id: string;
10
+ links: RailLink[];
11
+ }[];
12
+ /** Moves the app, the drawer away first. */
13
+ go: (address: string) => void;
14
+ onMenu: () => void;
15
+ onChat: () => void;
16
+ onDev: () => void;
17
+ }): import("react").JSX.Element;
package/dist/Top.js ADDED
@@ -0,0 +1,58 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ // A phone's bar, which has neither rail nor sidebar to move the app: the
4
+ // drawer's button at the left, Chat | Dev in the middle, and New at the right
5
+ // (the owner's iPhone reference; LLM.md "The app is `App`").
6
+ //
7
+ // (≡) ( Chat | Dev ) (✎)
8
+ //
9
+ // The three are glass: the pane's material (ground.ts) cut round, its edge one
10
+ // step stronger so a circle on the bare floor reads as one. Measured on the
11
+ // reference at 440×956: the buttons are 44px circles 20px in from each edge,
12
+ // the pill 158×48 with a 40px segment inside a 4px rim, its labels at 15px
13
+ // medium, all centred on one line 48px tall under the device's top inset.
14
+ //
15
+ // Chat | Dev moves the app the way the rail does (`onChat`, `onDev`: where each
16
+ // was last). In Studio neither is lit. New is Chat's New chat, and elsewhere the
17
+ // mode's own New row (@hanzo/build's and @hanzo/studio's `nav`, `NEW`), so the
18
+ // bar names it as the sidebar does.
19
+ import { SquarePen } from 'lucide-react';
20
+ import { Text, XStack } from '@hanzo/gui';
21
+ import { nav, NEW } from '@hanzo/build';
22
+ import { ToggleGroup, ToggleGroupItem } from '@hanzo/ui';
23
+ import { pane } from './ground.js';
24
+ import { empty } from './open.js';
25
+ import { talk } from './lib/host.js';
26
+ /** A button, px: a thumb's 44. */
27
+ const ROUND = 44;
28
+ /** The bar's line under the device's top inset, px: the pill's height. */
29
+ const LINE = 48;
30
+ /** A segment, px: the pill's 150px inside its rim, less the group's 4px gap, halved. */
31
+ const SEGMENT = 73;
32
+ /** The glass the three are cut from. */
33
+ const GLASS = { ...pane(), borderColor: 'var(--border-strong)', borderRadius: 9999 };
34
+ /** A round glass button named for what it does. */
35
+ function Round({ label, onPress, children, ...rest }) {
36
+ return (_jsx(XStack, { render: "button", "aria-label": label, onPress: onPress, ...GLASS, width: ROUND, height: ROUND, items: "center", justify: "center", shrink: 0, cursor: "pointer", pressStyle: { bg: '$raised' }, focusVisibleStyle: { outlineWidth: 2, outlineStyle: 'solid', outlineColor: '$outlineColor' }, ...rest, children: children }));
37
+ }
38
+ /** The drawer's mark: two strokes, the lower one shorter, 19×11 as the reference draws it. */
39
+ function Lines() {
40
+ return (_jsx("svg", { width: 19, height: 11, viewBox: "0 0 19 11", fill: "none", stroke: "currentColor", strokeWidth: 2, strokeLinecap: "round", "aria-hidden": true, children: _jsx("path", { d: "M1 1h17M1 10h10.7" }) }));
41
+ }
42
+ /** One side of the pill. */
43
+ function Side({ value, children }) {
44
+ return (_jsx(ToggleGroupItem, { value: value, size: "lg", width: SEGMENT, rounded: 9999, bg: "transparent", borderColor: "transparent", hoverStyle: { bg: '$hover' }, activeStyle: { bg: '$raised', borderColor: 'transparent' }, children: _jsx(Text, { fontSize: "$4", fontWeight: "500", color: "$ink", numberOfLines: 1, children: children }) }));
45
+ }
46
+ export function Top({ host, mode, places, go, onMenu, onChat, onDev, }) {
47
+ const made = mode === 'chat' ? null : (places ?? nav(host, (p) => host.go(p))).flatMap((g) => g.links).find((l) => l.id === NEW);
48
+ const fresh = mode === 'chat'
49
+ ? {
50
+ label: 'New chat',
51
+ onPress: () => {
52
+ empty();
53
+ go(talk());
54
+ },
55
+ }
56
+ : { label: made?.label ?? 'New', onPress: made?.onPress };
57
+ return (_jsxs(XStack, { "data-slot": "app-bar", height: `calc(${LINE}px + var(--safe-top))`, pt: "var(--safe-top)", px: "$4.5", items: "center", justify: "space-between", shrink: 0, $md: { display: 'none' }, children: [_jsx(Round, { label: "Open sidebar", "data-slot": "bar-menu", onPress: onMenu, children: _jsx(Lines, {}) }), _jsx(XStack, { "data-slot": "bar-mode", ...GLASS, height: LINE, p: "$1", items: "center", children: _jsxs(ToggleGroup, { type: "single", "aria-label": "App", value: mode === 'studio' ? '' : mode, disableDeactivation: true, onValueChange: (v) => (v === 'chat' ? onChat() : v === 'dev' ? onDev() : undefined), children: [_jsx(Side, { value: "chat", children: "Chat" }), _jsx(Side, { value: "dev", children: "Dev" })] }) }), _jsx(Round, { label: fresh.label, "data-slot": "bar-new", onPress: fresh.onPress, children: _jsx(SquarePen, { size: 20, "aria-hidden": true }) })] }));
58
+ }
@@ -0,0 +1,64 @@
1
+ export type { Link, Shelf, ShelfRow as Row } from '@hanzo/build';
2
+ /**
3
+ * The model that draws an agent's face: Hanzo's own image SKU (hanzo-inc/zen
4
+ * catalog.yaml `zen-image`), named on every call, because the endpoint takes
5
+ * none by default.
6
+ */
7
+ export declare const PAINTER = "zen-image";
8
+ /** The words every drawn face is asked for after its description. */
9
+ export declare const MEMOJI = "memoji style 3D emoji portrait, head and shoulders, centered, plain background";
10
+ /** A core member, drawn for an org that keeps no row of its own for them. */
11
+ export interface House {
12
+ id: string;
13
+ name: string;
14
+ avatar: string;
15
+ }
16
+ /** The default cast: the core team @hanzo/personas publishes, in the crew's order. */
17
+ export declare const CORE: readonly House[];
18
+ /**
19
+ * The org's agents with the default cast filling in any member the org keeps no
20
+ * row for, the cast first in its own order, then the rest as listed. With no
21
+ * list yet (or none at all) it is the default cast.
22
+ */
23
+ export declare function ordered<T extends {
24
+ name: string;
25
+ }>(agents: readonly T[] | null | undefined): (T | House)[];
26
+ /**
27
+ * One figure of @hanzo/personas' roster, as assets/personas/index.json lists it
28
+ * (scripts/team.mjs). `called`, `about` and `based` are its PERSONA.md's
29
+ * `name`, `description` and `based_on`, present only where one is written.
30
+ */
31
+ export interface Figure {
32
+ slug: string;
33
+ name: string;
34
+ category: string;
35
+ called?: string;
36
+ about?: string;
37
+ based?: string;
38
+ }
39
+ /** Where a host serves the roster: the list, and each written figure's PERSONA.md. */
40
+ export declare const FIGURES = "/personas";
41
+ /** The name a person reads for a figure: who it is based on, else its own name. */
42
+ export declare const title: (f: Figure) => string;
43
+ /** A figure's one line: its PERSONA.md's description, else what it was. */
44
+ export declare const line: (f: Figure) => string;
45
+ /**
46
+ * A name the agents API takes (cloud apps/agents `nameRE`): letters, digits, dots,
47
+ * dashes and underscores, from a letter or digit, up to 64. Accents are dropped
48
+ * and any other run becomes a dash: "Spärck Jones" is Sparck-Jones.
49
+ */
50
+ export declare const handle: (name: string) => string;
51
+ /** What the New agent sheet is filled with from a figure. */
52
+ export interface Draft {
53
+ name: string;
54
+ /** The instructions. */
55
+ does: string;
56
+ /** The face to draw. */
57
+ look: string;
58
+ }
59
+ /**
60
+ * A figure as an agent: its PERSONA.md's name and its prompt word for word
61
+ * (the text under the front matter); a figure with none written is told who it
62
+ * is in one paragraph. The face is described by who it is and its one line.
63
+ */
64
+ export declare function draft(f: Figure, md: string | null): Draft;
package/dist/agents.js ADDED
@@ -0,0 +1,62 @@
1
+ // The agents a conversation can hold, and the figures a new one can start from.
2
+ // Pure: the composer's Agents shelf (crew.tsx `useAgentShelf`), the crew strip
3
+ // and the New agent sheet read these, and the tests run them in Node.
4
+ import { TEAM } from './team.js';
5
+ /**
6
+ * The model that draws an agent's face: Hanzo's own image SKU (hanzo-inc/zen
7
+ * catalog.yaml `zen-image`), named on every call, because the endpoint takes
8
+ * none by default.
9
+ */
10
+ export const PAINTER = 'zen-image';
11
+ /** The words every drawn face is asked for after its description. */
12
+ export const MEMOJI = 'memoji style 3D emoji portrait, head and shoulders, centered, plain background';
13
+ /** The default cast: the core team @hanzo/personas publishes, in the crew's order. */
14
+ export const CORE = TEAM.map((one) => ({ id: `house-${one.id}`, name: one.name, avatar: `/agents/${one.id}.png` }));
15
+ const RANK = new Map(TEAM.map((one, i) => [one.id, i]));
16
+ /**
17
+ * The org's agents with the default cast filling in any member the org keeps no
18
+ * row for, the cast first in its own order, then the rest as listed. With no
19
+ * list yet (or none at all) it is the default cast.
20
+ */
21
+ export function ordered(agents) {
22
+ const list = Array.isArray(agents) ? [...agents] : [];
23
+ const names = new Set(list.map((one) => one.name?.toLowerCase()));
24
+ for (const one of CORE)
25
+ if (!names.has(one.name.toLowerCase()))
26
+ list.push(one);
27
+ const rank = (one) => RANK.get(one.name?.toLowerCase() ?? '') ?? Infinity;
28
+ return list.sort((a, b) => rank(a) - rank(b));
29
+ }
30
+ /** Where a host serves the roster: the list, and each written figure's PERSONA.md. */
31
+ export const FIGURES = '/personas';
32
+ /** The name a person reads for a figure: who it is based on, else its own name. */
33
+ export const title = (f) => f.based?.replace(/\s*\(.*\)\s*$/, '') ?? f.called ?? f.name;
34
+ /** A figure's one line: its PERSONA.md's description, else what it was. */
35
+ export const line = (f) => f.about ?? f.category.replace(/_/g, ' ').replace(/^./, (c) => c.toUpperCase());
36
+ /**
37
+ * A name the agents API takes (cloud apps/agents `nameRE`): letters, digits, dots,
38
+ * dashes and underscores, from a letter or digit, up to 64. Accents are dropped
39
+ * and any other run becomes a dash: "Spärck Jones" is Sparck-Jones.
40
+ */
41
+ export const handle = (name) => name
42
+ .normalize('NFD')
43
+ .replace(/[̀-ͯ]/g, '')
44
+ .replace(/[^A-Za-z0-9._-]+/g, '-')
45
+ .replace(/^[^A-Za-z0-9]+/, '')
46
+ .slice(0, 64)
47
+ .replace(/-+$/, '');
48
+ /**
49
+ * A figure as an agent: its PERSONA.md's name and its prompt word for word
50
+ * (the text under the front matter); a figure with none written is told who it
51
+ * is in one paragraph. The face is described by who it is and its one line.
52
+ */
53
+ export function draft(f, md) {
54
+ const who = title(f);
55
+ const prompt = md?.replace(/^---\n[\s\S]*?\n---\n/, '').trim();
56
+ return {
57
+ name: handle(f.called ?? who),
58
+ does: prompt ||
59
+ `You are ${who}. Answer as ${who} would, in your own voice and from what you wrote and did. When a question is past what you could have known, say so.`,
60
+ look: `${who}, ${line(f).replace(/^./, (c) => c.toLowerCase())}`,
61
+ };
62
+ }
package/dist/crew.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { useAgents } from '@hanzo/ai/react';
2
+ import { type Shelf } from './agents.js';
2
3
  /**
3
4
  * The org's agents, read again whenever the sheet makes one. `useAgents` holds
4
5
  * one list per caller, so the strip, the chat header and the sheet each hold
@@ -7,6 +8,19 @@ import { useAgents } from '@hanzo/ai/react';
7
8
  export declare function useCrew(): ReturnType<typeof useAgents>;
8
9
  /** The house crew's names, which a reply may be split by even after one has left the room. */
9
10
  export declare const HOUSE: readonly string[];
11
+ /** Opens the New agent sheet, blank or on its persona list; null shuts it. */
12
+ export declare function make(how: 'new' | 'persona' | null): void;
13
+ /**
14
+ * The New agent sheet, wherever the host mounts this once (beside the
15
+ * composer). It is drawn while `make` holds it open, and an agent it makes joins
16
+ * the conversation that is open.
17
+ */
18
+ export declare function AgentMaker(): import("react").JSX.Element;
19
+ /**
20
+ * The composer's `+` › Agents: the org's agents, the default cast first, each
21
+ * by its face; on is in the open conversation, and a pick toggles it there.
22
+ */
23
+ export declare function useAgentShelf(): Shelf;
10
24
  /**
11
25
  * `to` is the chat a press opens, for a strip on a page that is not one. Absent,
12
26
  * the strip sits under a chat and a press changes who is in it.
@@ -20,17 +34,24 @@ export declare function Crew({ to }?: {
20
34
  * A name, a mind and a spending limit are what the agents API requires; what it
21
35
  * does becomes its instructions; the likeness is whichever the maker gives — a
22
36
  * photo uploaded onto the round picture (pressed, or dropped on), or a face
23
- * described and drawn by the image model. Both go through `mark`, the one crop
24
- * every saved face takes, so a drawn face and an uploaded one are the same kind
25
- * of picture. The row keeps at most one of a picture and an emoji, so choosing
26
- * one clears the other here too.
37
+ * described and drawn by Hanzo's image model (`PAINTER`), one call per press of
38
+ * Draw or Regenerate. An agent created with neither a picture nor an emoji has
39
+ * one drawn then, once, from the description or else its name. Every face goes
40
+ * through `mark`, the one crop every saved face takes, and is kept as the row's
41
+ * `avatar`. The row keeps at most one of a picture and an emoji, so choosing one
42
+ * clears the other here too.
43
+ *
44
+ * Start from a persona fills the name, the instructions and the face's
45
+ * description from a figure of @hanzo/personas' roster (agents.ts `draft`);
46
+ * `start` opens the sheet on that list.
27
47
  *
28
48
  * Create stays off until the required fields are filled in the API's own
29
49
  * terms, and a refusal is said in plain words under the field it concerns.
30
50
  *
31
51
  * EXPORTED, because Contacts adds agents too and there is one way to make one.
32
52
  */
33
- export declare function Hire({ onClose, onMade }: {
53
+ export declare function Hire({ onClose, onMade, start }: {
34
54
  onClose: () => void;
35
55
  onMade: (name: string) => void;
56
+ start?: boolean;
36
57
  }): import("react").JSX.Element;