@g9tm/aiassistant-svelte 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +29 -2
  2. package/dist/AssistantRoot.svelte +118 -0
  3. package/dist/AssistantRoot.svelte.d.ts +13 -0
  4. package/dist/admin.d.ts +2 -0
  5. package/dist/admin.js +2 -0
  6. package/dist/blocks/BlockView.svelte +74 -0
  7. package/dist/blocks/BlockView.svelte.d.ts +7 -0
  8. package/dist/blocks/Chart.svelte +51 -0
  9. package/dist/blocks/Chart.svelte.d.ts +9 -0
  10. package/dist/browser.d.ts +16 -0
  11. package/dist/browser.js +80 -0
  12. package/dist/cards/ApprovalCard.svelte +56 -0
  13. package/dist/cards/ApprovalCard.svelte.d.ts +7 -0
  14. package/dist/cards/Cards.svelte +55 -0
  15. package/dist/cards/Cards.svelte.d.ts +11 -0
  16. package/dist/cards/SecretField.svelte +21 -0
  17. package/dist/cards/SecretField.svelte.d.ts +6 -0
  18. package/dist/context.d.ts +33 -0
  19. package/dist/context.js +35 -0
  20. package/dist/index.d.ts +34 -0
  21. package/dist/index.js +32 -0
  22. package/dist/pages/Abilities.svelte +6 -0
  23. package/dist/pages/Abilities.svelte.d.ts +6 -0
  24. package/dist/pages/ActionsPage.svelte +6 -0
  25. package/dist/pages/ActionsPage.svelte.d.ts +6 -0
  26. package/dist/pages/AdminPage.svelte +53 -0
  27. package/dist/pages/AdminPage.svelte.d.ts +6 -0
  28. package/dist/pages/FindingsPage.svelte +6 -0
  29. package/dist/pages/FindingsPage.svelte.d.ts +6 -0
  30. package/dist/pages/History.svelte +6 -0
  31. package/dist/pages/History.svelte.d.ts +6 -0
  32. package/dist/pages/KnowledgePage.svelte +6 -0
  33. package/dist/pages/KnowledgePage.svelte.d.ts +6 -0
  34. package/dist/pages/LearnedPage.svelte +6 -0
  35. package/dist/pages/LearnedPage.svelte.d.ts +6 -0
  36. package/dist/pages/Page.svelte +88 -0
  37. package/dist/pages/Page.svelte.d.ts +8 -0
  38. package/dist/pages/RoutineWizard.svelte +6 -0
  39. package/dist/pages/RoutineWizard.svelte.d.ts +6 -0
  40. package/dist/pages/admin/AdminAudit.svelte +33 -0
  41. package/dist/pages/admin/AdminAudit.svelte.d.ts +3 -0
  42. package/dist/pages/admin/AdminCapabilities.svelte +91 -0
  43. package/dist/pages/admin/AdminCapabilities.svelte.d.ts +3 -0
  44. package/dist/pages/admin/AdminConnections.svelte +58 -0
  45. package/dist/pages/admin/AdminConnections.svelte.d.ts +6 -0
  46. package/dist/pages/admin/AdminFeedback.svelte +38 -0
  47. package/dist/pages/admin/AdminFeedback.svelte.d.ts +3 -0
  48. package/dist/pages/admin/AdminModels.svelte +103 -0
  49. package/dist/pages/admin/AdminModels.svelte.d.ts +6 -0
  50. package/dist/pages/admin/AdminUsage.svelte +92 -0
  51. package/dist/pages/admin/AdminUsage.svelte.d.ts +3 -0
  52. package/dist/parts/Composer.svelte +69 -0
  53. package/dist/parts/Composer.svelte.d.ts +14 -0
  54. package/dist/parts/Message.svelte +40 -0
  55. package/dist/parts/Message.svelte.d.ts +9 -0
  56. package/dist/parts/MessageList.svelte +28 -0
  57. package/dist/parts/MessageList.svelte.d.ts +9 -0
  58. package/dist/parts/RevealText.svelte +26 -0
  59. package/dist/parts/RevealText.svelte.d.ts +7 -0
  60. package/dist/parts/Small.svelte +24 -0
  61. package/dist/parts/Small.svelte.d.ts +10 -0
  62. package/dist/parts/StagesThinking.svelte +15 -0
  63. package/dist/parts/StagesThinking.svelte.d.ts +8 -0
  64. package/dist/parts/TextView.svelte +36 -0
  65. package/dist/parts/TextView.svelte.d.ts +6 -0
  66. package/dist/parts/ThinkingOrb.svelte +7 -0
  67. package/dist/parts/ThinkingOrb.svelte.d.ts +18 -0
  68. package/dist/parts/WorkCard.svelte +41 -0
  69. package/dist/parts/WorkCard.svelte.d.ts +7 -0
  70. package/dist/slots/Dialog.svelte +20 -0
  71. package/dist/slots/Dialog.svelte.d.ts +9 -0
  72. package/dist/slots/PinPad.svelte +6 -0
  73. package/dist/slots/PinPad.svelte.d.ts +7 -0
  74. package/dist/store.svelte.d.ts +15 -0
  75. package/dist/store.svelte.js +31 -0
  76. package/dist/surfaces/AssistantChat.svelte +31 -0
  77. package/dist/surfaces/AssistantChat.svelte.d.ts +6 -0
  78. package/dist/surfaces/AssistantPanel.svelte +49 -0
  79. package/dist/surfaces/AssistantPanel.svelte.d.ts +13 -0
  80. package/dist/surfaces/Surfaces.svelte +43 -0
  81. package/dist/surfaces/Surfaces.svelte.d.ts +16 -0
  82. package/dist/theme.d.ts +9 -0
  83. package/dist/theme.js +22 -0
  84. package/package.json +46 -3
@@ -0,0 +1,35 @@
1
+ /**
2
+ * What every component of one assistant shares: the core assistant, its texts, the host's per-part classes
3
+ * and styles, the unstyled switch, and component overrides. Any component, small or large, can be replaced by the host;
4
+ * the replacement gets the same view model and the default component, so it can wrap it.
5
+ */
6
+ import { getContext, setContext } from "svelte";
7
+ import { partClass } from "@g9tm/aiassistant";
8
+ const KEY = Symbol("g9-assistant-ui");
9
+ export function provideUi(ui) {
10
+ setContext(KEY, ui);
11
+ return ui;
12
+ }
13
+ export function useUi() {
14
+ const ui = getContext(KEY);
15
+ if (!ui)
16
+ throw new Error("Use the assistant's components inside AssistantRoot, AssistantPanel or AssistantChat.");
17
+ return ui;
18
+ }
19
+ /** The class of a part (the library's stable class and the host's). */
20
+ export function cls(ui, part) {
21
+ return partClass(part, ui.styles, ui.unstyled);
22
+ }
23
+ /** The inline style the host gave a part. */
24
+ export function sty(ui, part) {
25
+ return ui.styles.styles ? new Map(Object.entries(ui.styles.styles)).get(part) : undefined;
26
+ }
27
+ /** The host's replacement of a component, if any. */
28
+ export function override(ui, name) {
29
+ const c = new Map(Object.entries(ui.components)).get(name);
30
+ return typeof c === "function" ? c : undefined;
31
+ }
32
+ /** The host's renderer of a block type, if any. */
33
+ export function blockOverride(ui, type) {
34
+ return ui.components.Block ? new Map(Object.entries(ui.components.Block)).get(type) : undefined;
35
+ }
@@ -0,0 +1,34 @@
1
+ /** @g9tm/aiassistant-svelte: Svelte 5 components of the G9 AI assistant, customisable in five layers. */
2
+ export { default as AssistantRoot } from "./AssistantRoot.svelte";
3
+ export { default as AssistantPanel } from "./surfaces/AssistantPanel.svelte";
4
+ export { default as AssistantChat } from "./surfaces/AssistantChat.svelte";
5
+ export { default as Surfaces } from "./surfaces/Surfaces.svelte";
6
+ export { default as MessageList } from "./parts/MessageList.svelte";
7
+ export { default as Message } from "./parts/Message.svelte";
8
+ export { default as Composer } from "./parts/Composer.svelte";
9
+ export { default as WorkCard } from "./parts/WorkCard.svelte";
10
+ export { default as TextView } from "./parts/TextView.svelte";
11
+ export { default as RevealText } from "./parts/RevealText.svelte";
12
+ export { default as ThinkingOrb } from "./parts/ThinkingOrb.svelte";
13
+ export { default as StagesThinking } from "./parts/StagesThinking.svelte";
14
+ export { default as Small } from "./parts/Small.svelte";
15
+ export { default as BlockView } from "./blocks/BlockView.svelte";
16
+ export { default as Chart } from "./blocks/Chart.svelte";
17
+ export { default as ApprovalCard } from "./cards/ApprovalCard.svelte";
18
+ export { default as Cards } from "./cards/Cards.svelte";
19
+ export { default as SecretField } from "./cards/SecretField.svelte";
20
+ export { default as PinPad } from "./slots/PinPad.svelte";
21
+ export { default as Dialog } from "./slots/Dialog.svelte";
22
+ export { default as History } from "./pages/History.svelte";
23
+ export { default as Abilities } from "./pages/Abilities.svelte";
24
+ export { default as KnowledgePage } from "./pages/KnowledgePage.svelte";
25
+ export { default as LearnedPage } from "./pages/LearnedPage.svelte";
26
+ export { default as FindingsPage } from "./pages/FindingsPage.svelte";
27
+ export { default as RoutineWizard } from "./pages/RoutineWizard.svelte";
28
+ export { default as ActionsPage } from "./pages/ActionsPage.svelte";
29
+ export { browserHost } from "./browser.js";
30
+ export type { BrowserHostOptions } from "./browser.js";
31
+ export { provideUi, useUi, cls, sty, override, blockOverride } from "./context.js";
32
+ export type { AssistantUi, Overrides } from "./context.js";
33
+ export { AssistantState, Ticker } from "./store.svelte.js";
34
+ export { themeVars, LIGHT, DARK } from "./theme.js";
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ /** @g9tm/aiassistant-svelte: Svelte 5 components of the G9 AI assistant, customisable in five layers. */
2
+ export { default as AssistantRoot } from "./AssistantRoot.svelte";
3
+ export { default as AssistantPanel } from "./surfaces/AssistantPanel.svelte";
4
+ export { default as AssistantChat } from "./surfaces/AssistantChat.svelte";
5
+ export { default as Surfaces } from "./surfaces/Surfaces.svelte";
6
+ export { default as MessageList } from "./parts/MessageList.svelte";
7
+ export { default as Message } from "./parts/Message.svelte";
8
+ export { default as Composer } from "./parts/Composer.svelte";
9
+ export { default as WorkCard } from "./parts/WorkCard.svelte";
10
+ export { default as TextView } from "./parts/TextView.svelte";
11
+ export { default as RevealText } from "./parts/RevealText.svelte";
12
+ export { default as ThinkingOrb } from "./parts/ThinkingOrb.svelte";
13
+ export { default as StagesThinking } from "./parts/StagesThinking.svelte";
14
+ export { default as Small } from "./parts/Small.svelte";
15
+ export { default as BlockView } from "./blocks/BlockView.svelte";
16
+ export { default as Chart } from "./blocks/Chart.svelte";
17
+ export { default as ApprovalCard } from "./cards/ApprovalCard.svelte";
18
+ export { default as Cards } from "./cards/Cards.svelte";
19
+ export { default as SecretField } from "./cards/SecretField.svelte";
20
+ export { default as PinPad } from "./slots/PinPad.svelte";
21
+ export { default as Dialog } from "./slots/Dialog.svelte";
22
+ export { default as History } from "./pages/History.svelte";
23
+ export { default as Abilities } from "./pages/Abilities.svelte";
24
+ export { default as KnowledgePage } from "./pages/KnowledgePage.svelte";
25
+ export { default as LearnedPage } from "./pages/LearnedPage.svelte";
26
+ export { default as FindingsPage } from "./pages/FindingsPage.svelte";
27
+ export { default as RoutineWizard } from "./pages/RoutineWizard.svelte";
28
+ export { default as ActionsPage } from "./pages/ActionsPage.svelte";
29
+ export { browserHost } from "./browser.js";
30
+ export { provideUi, useUi, cls, sty, override, blockOverride } from "./context.js";
31
+ export { AssistantState, Ticker } from "./store.svelte.js";
32
+ export { themeVars, LIGHT, DARK } from "./theme.js";
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="abilities" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const Abilities: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type Abilities = ReturnType<typeof Abilities>;
6
+ export default Abilities;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="actions" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const ActionsPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type ActionsPage = ReturnType<typeof ActionsPage>;
6
+ export default ActionsPage;
@@ -0,0 +1,53 @@
1
+ <script lang="ts">
2
+ // The manager's pages (07 phases 11-12): capabilities, usage, audit, models, connections, suggestions and gates,
3
+ // with a scope picker that says where a new provider or server goes (this app, or the whole installation).
4
+ import Page from "./Page.svelte";
5
+ import AdminCapabilities from "./admin/AdminCapabilities.svelte";
6
+ import AdminUsage from "./admin/AdminUsage.svelte";
7
+ import AdminAudit from "./admin/AdminAudit.svelte";
8
+ import AdminModels from "./admin/AdminModels.svelte";
9
+ import AdminConnections from "./admin/AdminConnections.svelte";
10
+ import AdminFeedback from "./admin/AdminFeedback.svelte";
11
+ import { AdminClient, adminTexts } from "@g9tm/aiassistant/admin";
12
+ import { cls, useUi } from "../context.js";
13
+
14
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
15
+ const ui = useUi();
16
+ const t = adminTexts(ui.texts).t;
17
+ type Tab = "gates" | "capabilities" | "usage" | "audit" | "models" | "connections" | "feedback";
18
+ const tabs: Tab[] = ["capabilities", "models", "connections", "usage", "audit", "feedback", "gates"];
19
+ let tab = $state<Tab>("capabilities");
20
+ let level = $state("app");
21
+ let chain = $state<{ level: string; space: string | null; user: string | null }[]>([]);
22
+ $effect(() => { new AdminClient(ui.assistant.api).scopes().then((c) => { chain = c; }, () => { chain = []; }); });
23
+ </script>
24
+
25
+ <div class={cls(ui, "panel.body")} data-page="admin">
26
+ <div role="tablist" class="g9a-tabs">
27
+ {#each tabs as x (x)}
28
+ <button type="button" role="tab" aria-selected={tab === x} onclick={() => { tab = x; }}>{t("admin." + x)}</button>
29
+ {/each}
30
+ </div>
31
+ {#if tab === "models" || tab === "connections"}
32
+ <label class="g9a-row">{t("admin.scope")}
33
+ <select bind:value={level}>
34
+ {#each chain.filter((c) => c.level === "app" || c.level === "general") as c (c.level)}<option value={c.level}>{c.level}</option>{/each}
35
+ </select>
36
+ </label>
37
+ {/if}
38
+ {#if tab === "capabilities"}
39
+ <AdminCapabilities />
40
+ {:else if tab === "models"}
41
+ <AdminModels {level} />
42
+ {:else if tab === "connections"}
43
+ <AdminConnections {level} />
44
+ {:else if tab === "usage"}
45
+ <AdminUsage />
46
+ {:else if tab === "audit"}
47
+ <AdminAudit />
48
+ {:else if tab === "feedback"}
49
+ <AdminFeedback />
50
+ {:else}
51
+ <Page kind="admin" {onopen} />
52
+ {/if}
53
+ </div>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const AdminPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type AdminPage = ReturnType<typeof AdminPage>;
6
+ export default AdminPage;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="findings" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const FindingsPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type FindingsPage = ReturnType<typeof FindingsPage>;
6
+ export default FindingsPage;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="history" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const History: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type History = ReturnType<typeof History>;
6
+ export default History;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="knowledge" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const KnowledgePage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type KnowledgePage = ReturnType<typeof KnowledgePage>;
6
+ export default KnowledgePage;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="learned" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const LearnedPage: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type LearnedPage = ReturnType<typeof LearnedPage>;
6
+ export default LearnedPage;
@@ -0,0 +1,88 @@
1
+ <script lang="ts" module>
2
+ export type PageKind = "history" | "abilities" | "knowledge" | "learned" | "findings" | "routines" | "actions" | "admin";
3
+ </script>
4
+
5
+ <script lang="ts">
6
+ // The assistant's pages, each from its own endpoint, read with type checks only.
7
+ import { arr, num, obj, str } from "@g9tm/aiassistant";
8
+ import { cls, useUi } from "../context.js";
9
+
10
+ let { kind, onopen = undefined }: { kind: PageKind; onopen?: ((conversationId: string) => void) | undefined } = $props();
11
+ const ui = useUi();
12
+ const t = ui.texts.t;
13
+ const api = ui.assistant.api;
14
+ let data = $state<Record<string, unknown>>({});
15
+ let error = $state<string | null>(null);
16
+ let search = $state("");
17
+ let purpose = $state("");
18
+ let code = $state("");
19
+ let token = $state<string | null>(null);
20
+
21
+ const PATHS: Record<PageKind, string> = {
22
+ history: "conversations", abilities: "capabilities", knowledge: "knowledge", learned: "learned/dashboard?capability=chat",
23
+ findings: "findings", routines: "routines", actions: "conversations", admin: "gates",
24
+ };
25
+
26
+ async function load(): Promise<void> {
27
+ try {
28
+ data = obj(await api.get(new Map(Object.entries(PATHS)).get(kind) ?? ""));
29
+ error = null;
30
+ } catch (e) {
31
+ error = e instanceof Error ? e.message : "error.network";
32
+ }
33
+ }
34
+ $effect(() => { void load(); });
35
+
36
+ const today = new Date().toDateString();
37
+ const conversations = $derived(arr(data["conversations"]).map(obj).filter((c) => str(c["title"]).includes(search)));
38
+ async function act(path: string, body?: unknown): Promise<void> {
39
+ await api.post(path, body).catch(() => undefined);
40
+ await load();
41
+ }
42
+ </script>
43
+
44
+ <div class={cls(ui, "panel.body")} data-page={kind}>
45
+ {#if error}<p class="g9a-note" role="alert">{t("error.network")}</p>{/if}
46
+ {#if kind === "history"}
47
+ <input bind:value={search} placeholder={t("history.search")} aria-label={t("history.search")} />
48
+ {#each [["history.pinned", conversations.filter((c) => c["pinned"] === true)], ["history.today", conversations.filter((c) => c["pinned"] !== true && new Date(str(c["updatedAt"])).toDateString() === today)], ["history.earlier", conversations.filter((c) => c["pinned"] !== true && new Date(str(c["updatedAt"])).toDateString() !== today)]] as [title, list] (title)}
49
+ {#if (list as Record<string, unknown>[]).length > 0}
50
+ <h4>{t(title as string)}</h4>
51
+ <ul>{#each list as Record<string, unknown>[] as c (str(c["id"]))}<li><button type="button" onclick={async () => { await ui.assistant.open(str(c["id"])); onopen?.(str(c["id"])); }}>{str(c["title"])}</button></li>{/each}</ul>
52
+ {/if}
53
+ {/each}
54
+ {:else if kind === "abilities"}
55
+ {#each arr(data["groups"]).map(obj) as g (str(g["name"]))}
56
+ <h4>{str(g["title"], str(g["name"]))}</h4>
57
+ <ul>{#each arr(g["tools"]).map(obj) as tool (str(tool["name"]))}<li>{str(tool["title"], str(tool["name"]))}</li>{/each}</ul>
58
+ {/each}
59
+ {#each arr(data["closed"]).map(obj) as c (str(c["name"]))}<p class="g9a-note">{str(c["name"])} · {str(c["layer"])}</p>{/each}
60
+ {:else if kind === "knowledge"}
61
+ <ul>{#each arr(data["notes"] ?? data["items"]).map(obj) as n, i (i)}<li>{str(n["text"], str(n["name"]))}</li>{/each}</ul>
62
+ {:else if kind === "learned"}
63
+ <p>{Math.round(num(data["modelFreeRate"]) * 100)}% · {num(data["tokensSaved"])}</p>
64
+ <ul>{#each arr(data["items"]).map(obj) as it, i (i)}<li data-status={str(it["status"])}>{str(it["signature"])} · {str(it["status"])} · {num(it["hits"])}</li>{/each}</ul>
65
+ {:else if kind === "findings"}
66
+ <ul>
67
+ {#each arr(data["findings"]).map(obj) as f (str(f["id"]))}
68
+ <li>
69
+ {str(obj(f["title"])["text"], t(str(obj(f["title"])["key"])))}
70
+ <button type="button" onclick={() => act("findings/" + encodeURIComponent(str(f["id"])) + "/fixed")}>✓</button>
71
+ <button type="button" onclick={() => act("findings/" + encodeURIComponent(str(f["id"])) + "/dismiss", { reason: "—" })}>{t("no")}</button>
72
+ </li>
73
+ {/each}
74
+ </ul>
75
+ {:else if kind === "routines"}
76
+ <ul>{#each arr(data["routines"] ?? data["items"]).map(obj) as r (str(r["id"]))}<li>{str(r["title"], str(r["name"]))} <button type="button" onclick={() => act("routines/" + encodeURIComponent(str(r["id"])) + "/preview")}>▶</button></li>{/each}</ul>
77
+ {:else if kind === "actions"}
78
+ <ul>{#each [...ui.assistant.snapshot().plans.values()].filter((p) => p.answered === "confirmed") as p (p.id)}<li>{p.title} <button type="button" onclick={() => ui.assistant.undo(p.id)}>{t("undo.all")}</button></li>{/each}</ul>
79
+ {:else if kind === "admin"}
80
+ <form onsubmit={async (e) => { e.preventDefault(); const r = obj(await api.post("gates", { purpose, code }).catch(() => null)); token = str(r["token"]) || null; purpose = ""; code = ""; await load(); }}>
81
+ <input bind:value={purpose} placeholder="purpose" aria-label="purpose" />
82
+ <input bind:value={code} type="password" placeholder={t("card.code")} aria-label={t("card.code")} />
83
+ <button type="submit">+</button>
84
+ </form>
85
+ {#if token}<p class="g9a-note"><code>{token}</code></p>{/if}
86
+ <ul>{#each arr(data["gates"]).map(obj) as g (str(g["id"]))}<li data-revoked={g["revoked"] === true}>{str(g["purpose"])} · {num(g["callsToday"])} <button type="button" onclick={async () => { await api.delete("gates/" + encodeURIComponent(str(g["id"]))); await load(); }}>×</button></li>{/each}</ul>
87
+ {/if}
88
+ </div>
@@ -0,0 +1,8 @@
1
+ export type PageKind = "history" | "abilities" | "knowledge" | "learned" | "findings" | "routines" | "actions" | "admin";
2
+ type $$ComponentProps = {
3
+ kind: PageKind;
4
+ onopen?: ((conversationId: string) => void) | undefined;
5
+ };
6
+ declare const Page: import("svelte").Component<$$ComponentProps, {}, "">;
7
+ type Page = ReturnType<typeof Page>;
8
+ export default Page;
@@ -0,0 +1,6 @@
1
+ <script lang="ts">
2
+ import Page from "./Page.svelte";
3
+ let { onopen = undefined }: { onopen?: ((id: string) => void) | undefined } = $props();
4
+ </script>
5
+
6
+ <Page kind="routines" {onopen} />
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ onopen?: ((id: string) => void) | undefined;
3
+ };
4
+ declare const RoutineWizard: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type RoutineWizard = ReturnType<typeof RoutineWizard>;
6
+ export default RoutineWizard;
@@ -0,0 +1,33 @@
1
+ <script lang="ts">
2
+ // The audit, newest first: steps run, cards answered, undos, gates and settings changed.
3
+ import type { AuditView } from "@g9tm/aiassistant/admin";
4
+ import { AdminClient, adminTexts } from "@g9tm/aiassistant/admin";
5
+ import { useUi } from "../../context.js";
6
+
7
+ const ui = useUi();
8
+ const t = adminTexts(ui.texts).t;
9
+ let rows = $state<AuditView[]>([]);
10
+ let error = $state<string | null>(null);
11
+
12
+ $effect(() => {
13
+ new AdminClient(ui.assistant.api).audit(200).then((r) => { rows = r; error = null; }, (e: unknown) => { error = e instanceof Error ? e.message : "error.network"; });
14
+ });
15
+ </script>
16
+
17
+ {#if error}<p class="g9a-note" role="alert">{t("error.network")}</p>{/if}
18
+ {#if rows.length === 0 && !error}<p class="g9a-note">{t("admin.empty")}</p>{/if}
19
+ <table>
20
+ <tbody>
21
+ {#each rows as a, i (i)}
22
+ <tr data-tainted={a.tainted}>
23
+ <td><time>{new Date(a.at).toLocaleString(ui.texts.locale)}</time></td>
24
+ <td>{a.user}</td>
25
+ <td>{a.actor}</td>
26
+ <td dir="ltr">{a.action}</td>
27
+ <td dir="ltr">{a.tool ?? ""}</td>
28
+ <td dir="ltr">{a.decision ?? ""}</td>
29
+ <td dir="ltr">{a.outcome ?? ""}</td>
30
+ </tr>
31
+ {/each}
32
+ </tbody>
33
+ </table>
@@ -0,0 +1,3 @@
1
+ declare const AdminAudit: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type AdminAudit = ReturnType<typeof AdminAudit>;
3
+ export default AdminAudit;
@@ -0,0 +1,91 @@
1
+ <script lang="ts">
2
+ // Capabilities as they run now; a manager changes the route, the limits or the switch without a release, and runs
3
+ // one entry-test probe at a time on the route's first model.
4
+ import type { CapabilityLimitsView, CapabilityView, ProbeView } from "@g9tm/aiassistant/admin";
5
+ import { AdminClient, adminTexts, parseRoute } from "@g9tm/aiassistant/admin";
6
+ import { useUi } from "../../context.js";
7
+
8
+ const ui = useUi();
9
+ const t = adminTexts(ui.texts).t;
10
+ const admin = new AdminClient(ui.assistant.api);
11
+ let list = $state<CapabilityView[]>([]);
12
+ let probes = $state<string[]>([]);
13
+ let routes = $state<Record<string, string>>({});
14
+ let error = $state<string | null>(null);
15
+ let results = $state<ProbeView[]>([]);
16
+ let busy = $state(false);
17
+
18
+ const FIELDS: [keyof CapabilityLimitsView, string][] = [
19
+ ["maxRounds", "admin.max_rounds"], ["perMinute", "admin.per_minute"], ["perPersonPerDay", "admin.per_person_day"],
20
+ ["perTenantPerDay", "admin.per_space_day"], ["monthlyTokenBudget", "admin.monthly_tokens"],
21
+ ];
22
+
23
+ async function load(): Promise<void> {
24
+ try {
25
+ const r = await admin.capabilities();
26
+ list = r.capabilities;
27
+ probes = r.probes;
28
+ routes = Object.fromEntries(r.capabilities.map((c) => [c.id, c.route.join(" > ")]));
29
+ error = null;
30
+ } catch (e) {
31
+ error = e instanceof Error ? e.message : "error.network";
32
+ }
33
+ }
34
+ $effect(() => { void load(); });
35
+
36
+ function replace(c: CapabilityView): void {
37
+ list = list.map((x) => (x.id === c.id ? c : x));
38
+ routes = { ...routes, [c.id]: c.route.join(" > ") };
39
+ }
40
+
41
+ async function save(c: CapabilityView): Promise<void> {
42
+ const route = parseRoute(new Map(Object.entries(routes)).get(c.id) ?? "");
43
+ try {
44
+ replace(await admin.setCapability(c.id, { route, limits: c.limits, enabled: c.enabled }));
45
+ } catch (e) {
46
+ error = e instanceof Error ? e.message : "error.network";
47
+ }
48
+ }
49
+
50
+ async function reset(c: CapabilityView): Promise<void> {
51
+ try { replace(await admin.resetCapability(c.id)); } catch (e) { error = e instanceof Error ? e.message : "error.network"; }
52
+ }
53
+
54
+ async function probeAll(c: CapabilityView): Promise<void> {
55
+ const model = c.route.at(0);
56
+ if (!model || busy) return;
57
+ busy = true;
58
+ results = [];
59
+ for (const p of probes) {
60
+ // one probe per request: each shows as it ends
61
+ try { results = [...results, await admin.probe(p, model)]; } catch { results = [...results, { probe: p, model, quality: "error", rightKind: false, tokensIn: 0, ms: 0, head: null, error: "error.network" }]; }
62
+ }
63
+ busy = false;
64
+ }
65
+ </script>
66
+
67
+ {#if error}<p class="g9a-note" role="alert">{t("error.network")}</p>{/if}
68
+ {#if list.length === 0 && !error}<p class="g9a-note">{t("admin.empty")}</p>{/if}
69
+ {#each list as c (c.id)}
70
+ <section class="g9a-admin-capability" data-overridden={c.overridden}>
71
+ <h4>{c.id} · {c.kind}{#if c.overridden} · <small>{t("admin.overridden")}</small>{/if}</h4>
72
+ <label>{t("admin.enabled")} <input type="checkbox" bind:checked={c.enabled} /></label>
73
+ <label>{t("admin.route")} <input dir="ltr" bind:value={routes[c.id]} /></label>
74
+ {#each FIELDS as [key, label] (key)}
75
+ <label>{t(label)} <input type="number" min="0" dir="ltr" bind:value={c.limits[key]} /></label>
76
+ {/each}
77
+ <div>
78
+ <button type="button" onclick={() => save(c)}>{t("admin.save")}</button>
79
+ {#if c.overridden}<button type="button" onclick={() => reset(c)}>{t("admin.reset")}</button>{/if}
80
+ {#if probes.length > 0 && c.route.length > 0}<button type="button" disabled={busy} onclick={() => probeAll(c)}>{t("admin.probe")}</button>{/if}
81
+ </div>
82
+ </section>
83
+ {/each}
84
+ {#if results.length > 0}
85
+ <h4>{t("admin.probe")}</h4>
86
+ <ul>
87
+ {#each results as r (r.probe)}
88
+ <li data-ok={r.rightKind}>{r.rightKind ? "✓" : "✗"} {r.probe} · {r.quality} · {r.tokensIn} · {r.ms} ms{#if r.error} · {r.error}{/if}</li>
89
+ {/each}
90
+ </ul>
91
+ {/if}
@@ -0,0 +1,3 @@
1
+ declare const AdminCapabilities: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type AdminCapabilities = ReturnType<typeof AdminCapabilities>;
3
+ export default AdminCapabilities;
@@ -0,0 +1,58 @@
1
+ <script lang="ts">
2
+ // The "Connections" group: other MCP servers (an address, a way in, a secret sent once, a test listing the
3
+ // tools as the assistant would name them, read-only or a change), with the route their traffic takes.
4
+ import { AdminClient, adminTexts } from "@g9tm/aiassistant/admin";
5
+ import type { McpServerView } from "@g9tm/aiassistant/admin";
6
+ import { useUi } from "../../context.js";
7
+
8
+ let { level = "app" }: { level?: string } = $props();
9
+ const ui = useUi();
10
+ const t = adminTexts(ui.texts).t;
11
+ const admin = new AdminClient(ui.assistant.api);
12
+ let client = $state(true);
13
+ let servers = $state<McpServerView[]>([]);
14
+ let tested = $state<{ id: string; ok: boolean; error: string | null; tools: { name: string; readOnly: boolean; allowed: boolean }[] } | null>(null);
15
+ let id = $state("");
16
+ let url = $state("");
17
+ let auth = $state("none");
18
+ let secret = $state("");
19
+ let error = $state<string | null>(null);
20
+
21
+ async function load(): Promise<void> {
22
+ try { const r = await admin.mcpServers(); client = r.client; servers = r.servers; error = null; } catch (e) { error = e instanceof Error ? e.message : "error.network"; }
23
+ }
24
+ $effect(() => { void load(); });
25
+
26
+ async function add(): Promise<void> {
27
+ if (!id.trim() || !url.trim()) return;
28
+ try {
29
+ await admin.saveMcpServer(id.trim(), { url: url.trim(), auth, secret: secret || undefined, level });
30
+ id = ""; url = ""; secret = "";
31
+ await load();
32
+ } catch (e) { error = e instanceof Error ? e.message : "error.network"; }
33
+ }
34
+ </script>
35
+
36
+ {#if error}<p class="g9a-note" role="alert">{t("error.network")}</p>{/if}
37
+ <h4>{t("admin.mcp")}</h4>
38
+ {#if !client}<p class="g9a-note">{t("admin.no_client")}</p>{/if}
39
+ <ul>
40
+ {#each servers as s (s.id)}
41
+ <li data-enabled={s.enabled}>
42
+ <strong dir="ltr">{s.id}</strong> · <span dir="ltr">{s.url}</span> · {s.auth}{s.hasSecret ? " 🔒" : ""} · {s.level} · <span dir="ltr">{s.route}</span>
43
+ <button type="button" onclick={async () => { tested = { id: s.id, ...(await admin.testMcpServer(s.id).catch(() => ({ ok: false, error: "error.network", tools: [] }))) }; }}>{t("admin.test")}</button>
44
+ <button type="button" onclick={async () => { await admin.deleteMcpServer(s.id).catch(() => undefined); await load(); }}>{t("admin.remove")}</button>
45
+ </li>
46
+ {/each}
47
+ </ul>
48
+ {#if tested}
49
+ <p>{tested.id}: {tested.ok ? "✓" : "✗ " + (tested.error ?? "")}</p>
50
+ <ul>{#each tested.tools as x (x.name)}<li dir="ltr" data-allowed={x.allowed}>{x.name} {x.readOnly ? "(read)" : "(change)"}</li>{/each}</ul>
51
+ {/if}
52
+ <form class="g9a-row" onsubmit={(e) => { e.preventDefault(); void add(); }}>
53
+ <input dir="ltr" bind:value={id} placeholder="name" aria-label="name" />
54
+ <input dir="ltr" bind:value={url} placeholder="https://…/mcp" aria-label="url" />
55
+ <select bind:value={auth} aria-label="auth"><option value="none">none</option><option value="bearer">bearer</option></select>
56
+ {#if auth !== "none"}<input dir="ltr" type="password" bind:value={secret} placeholder={t("admin.secret")} aria-label={t("admin.secret")} autocomplete="off" />{/if}
57
+ <button type="submit">{t("admin.add")}</button>
58
+ </form>
@@ -0,0 +1,6 @@
1
+ type $$ComponentProps = {
2
+ level?: string;
3
+ };
4
+ declare const AdminConnections: import("svelte").Component<$$ComponentProps, {}, "">;
5
+ type AdminConnections = ReturnType<typeof AdminConnections>;
6
+ export default AdminConnections;
@@ -0,0 +1,38 @@
1
+ <script lang="ts">
2
+ // The agents' suggestions: a repeat counts on one row; the manager marks each and leaves a note. The two
3
+ // knowledge budgets are shown as bars above.
4
+ import { AdminClient, adminTexts } from "@g9tm/aiassistant/admin";
5
+ import type { FeedbackView } from "@g9tm/aiassistant/admin";
6
+ import { useUi } from "../../context.js";
7
+
8
+ const ui = useUi();
9
+ const t = adminTexts(ui.texts).t;
10
+ const admin = new AdminClient(ui.assistant.api);
11
+ let rows = $state<FeedbackView[]>([]);
12
+ let budget = $state<{ notes: number; maxNotes: number; pinnedChars: number; maxPinnedChars: number } | null>(null);
13
+ const STATES = ["new", "seen", "planned", "done", "dismissed"];
14
+
15
+ async function load(): Promise<void> {
16
+ rows = (await admin.agentFeedback().catch(() => ({ fresh: 0, rows: [] }))).rows;
17
+ budget = await admin.budget().catch(() => null);
18
+ }
19
+ $effect(() => { void load(); });
20
+ </script>
21
+
22
+ {#if budget}
23
+ <p>{t("admin.budget", { notes: budget.notes, max: budget.maxNotes, chars: budget.pinnedChars, maxChars: budget.maxPinnedChars })}</p>
24
+ <progress max={budget.maxNotes} value={budget.notes}></progress>
25
+ <progress max={budget.maxPinnedChars} value={budget.pinnedChars}></progress>
26
+ {/if}
27
+ {#if rows.length === 0}<p class="g9a-note">{t("admin.empty")}</p>{/if}
28
+ <ul>
29
+ {#each rows as f (f.id)}
30
+ <li data-status={f.status}>
31
+ <strong>{f.title}</strong> · {f.kind} · ×{f.count}
32
+ {#if f.detail}<p>{f.detail}</p>{/if}
33
+ <select value={f.status} aria-label={f.title} onchange={async (e) => { await admin.setFeedback(f.id, (e.currentTarget as HTMLSelectElement).value).catch(() => undefined); await load(); }}>
34
+ {#each STATES as s (s)}<option value={s}>{s}</option>{/each}
35
+ </select>
36
+ </li>
37
+ {/each}
38
+ </ul>
@@ -0,0 +1,3 @@
1
+ declare const AdminFeedback: import("svelte").Component<Record<string, never>, {}, "">;
2
+ type AdminFeedback = ReturnType<typeof AdminFeedback>;
3
+ export default AdminFeedback;