wizards 0.3.7 → 0.3.8
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/apps/runtime/dist/agents/studio-assistant.js +79 -7
- package/apps/runtime/dist/assistant/runs.js +24 -0
- package/apps/runtime/dist/docs/user/assistant.md +25 -7
- package/apps/runtime/dist/env.js +5 -0
- package/apps/runtime/dist/manage.js +4 -4
- package/apps/runtime/dist/models.js +6 -0
- package/apps/runtime/dist/routes/studio.js +100 -39
- package/apps/runtime/dist/services/assistant-chats.js +49 -6
- package/apps/web/dist/assets/{AboutDialog-C0dN86zd.js → AboutDialog-ClV4gNiW.js} +1 -1
- package/apps/web/dist/assets/{ChangelogDialog-Cv81t7tj.js → ChangelogDialog-DsSEkayM.js} +1 -1
- package/apps/web/dist/assets/ChatView-BdYCnDLk.js +10 -0
- package/apps/web/dist/assets/EditorPage-7PZzkNBT.js +3 -0
- package/apps/web/dist/assets/{FlowPreview-D4dp1MWS.js → FlowPreview-C_x5Kc2j.js} +1 -1
- package/apps/web/dist/assets/{Harness-v7r0e6vK.js → Harness-CvvtSTZC.js} +2 -2
- package/apps/web/dist/assets/{NewWizardChannels-Be-7VhBi.js → NewWizardChannels-g1qCd_tQ.js} +1 -1
- package/apps/web/dist/assets/{NewWizardPage-C8hPvrK8.js → NewWizardPage-B3hswj5r.js} +2 -2
- package/apps/web/dist/assets/ProjectPage-CFDncRxT.js +1 -0
- package/apps/web/dist/assets/{PublicRunner-DeCpVfZP.js → PublicRunner-C9N5r-sT.js} +1 -1
- package/apps/web/dist/assets/{ReadOnly-B-UCgnqW.js → ReadOnly-C1UcqOlS.js} +1 -1
- package/apps/web/dist/assets/RunDrawer-B8YzFvgz.js +4 -0
- package/apps/web/dist/assets/RunnerView-ii-PP5c-.js +1 -0
- package/apps/web/dist/assets/SettingsPage-ClEP5Q0t.js +1 -0
- package/apps/web/dist/assets/{SetupPage-9CH3qXdH.js → SetupPage-CDFzo6LT.js} +1 -1
- package/apps/web/dist/assets/{SharePage-B778xUFo.js → SharePage-BV2S0g84.js} +1 -1
- package/apps/web/dist/assets/changelog-wZI-UaUi.js +1 -0
- package/apps/web/dist/assets/{circle-alert-DLvC9ePT.js → circle-alert-D-Q6DK7b.js} +1 -1
- package/apps/web/dist/assets/{esm-BJskogLD.js → esm-CTna4iL0.js} +1 -1
- package/apps/web/dist/assets/index-DUjJx-uk.css +1 -0
- package/apps/web/dist/assets/index-DnQgOKOQ.js +15 -0
- package/apps/web/dist/assets/{lock-DR7NakPn.js → lock-DwGN_KOC.js} +1 -1
- package/apps/web/dist/assets/{phone-BORh3jfv.js → phone-XIVIe17d.js} +1 -1
- package/apps/web/dist/assets/{settings-menu-DV5D91TD.js → settings-menu-JuSRq6NP.js} +1 -1
- package/apps/web/dist/assets/{ui-BOCXzzHi.js → ui-DrjXmHdO.js} +2 -2
- package/apps/web/dist/assets/{upload-B3yv8gsk.js → upload-CDxTpAqx.js} +1 -1
- package/apps/web/dist/index.html +3 -3
- package/apps/web/dist/mcp/flow.html +3 -3
- package/node_modules/@engenty-wizards/shared/dist/assistant.d.ts +62 -1
- package/node_modules/@engenty-wizards/shared/dist/assistant.js +10 -0
- package/package.json +1 -1
- package/apps/web/dist/assets/ChatView-uogJ3nht.js +0 -10
- package/apps/web/dist/assets/EditorPage-n8YuTh6j.js +0 -3
- package/apps/web/dist/assets/ProjectPage-B686ArYy.js +0 -1
- package/apps/web/dist/assets/RunDrawer-C6pzi8AJ.js +0 -4
- package/apps/web/dist/assets/RunnerView-Deenymq0.js +0 -1
- package/apps/web/dist/assets/SettingsPage-CK6L0yEe.js +0 -1
- package/apps/web/dist/assets/changelog-DCJo2mbx.js +0 -1
- package/apps/web/dist/assets/index-D6hUM8NU.css +0 -1
- package/apps/web/dist/assets/index-Df6UxElH.js +0 -12
|
@@ -6,7 +6,7 @@ import { nanoid } from "nanoid";
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { knowsTools } from "../assistant/knows.js";
|
|
8
8
|
import { PRIMER } from "../assistant/primer.js";
|
|
9
|
-
import { attachTools, costOf, gatewayTools, textModel } from "../models.js";
|
|
9
|
+
import { attachTools, contextWindowOf, costOf, gatewayTools, textModel, usageOf, } from "../models.js";
|
|
10
10
|
import { ServiceError } from "../services/errors.js";
|
|
11
11
|
import { knowledgeCounts } from "../services/knowledge.js";
|
|
12
12
|
import { addProjectFileFromUrl, projectFileContent, projectFiles, updateProjectFile, } from "../services/project-files.js";
|
|
@@ -16,8 +16,12 @@ import { categoryRows } from "../services/space-categories.js";
|
|
|
16
16
|
import { currentTenant } from "../tenants/tenant.js";
|
|
17
17
|
import { assistantPageOf, assistantToolsOf } from "../tools/plugin.js";
|
|
18
18
|
import { readWebsite } from "./website.js";
|
|
19
|
+
/** How a message written meanwhile reaches the model. */
|
|
20
|
+
export function signalText(taken) {
|
|
21
|
+
return `The person wrote this while you were working. What they asked before still stands unless this changes it:\n\n${taken.map((s) => s.message).join("\n\n")}`;
|
|
22
|
+
}
|
|
19
23
|
const MAX_STEPS = 24;
|
|
20
|
-
const SYSTEM = `You are the assistant of the engenty wizards studio. You stand on every page of the studio except the wizard editor, which has a chat of its own: the space's pages, the settings, the plugins' pages. You help the person who works here understand the app, set it up and find their way.
|
|
24
|
+
const SYSTEM = `You are Goose, the assistant of the engenty wizards studio. You stand on every page of the studio except the wizard editor, which has a chat of its own: the space's pages, the settings, the plugins' pages. You help the person who works here understand the app, set it up and find their way.
|
|
21
25
|
|
|
22
26
|
You talk to the person. Answer in their language, briefly and warmly: a few sentences, a short list where it helps. Never mention JSON, ids, keys or tool names. Call it a space (German „Space“), never a project: the tools say project, the app says space.
|
|
23
27
|
|
|
@@ -641,12 +645,29 @@ function partOf(route) {
|
|
|
641
645
|
const m = route.match(/^\/space\/(info|knowledge|data|results)/);
|
|
642
646
|
return m ? m[1] : route === "/space" ? "info" : null;
|
|
643
647
|
}
|
|
648
|
+
/** How large each tool is in the prompt: its description and its schema, in characters. */
|
|
649
|
+
function toolSizes(tools) {
|
|
650
|
+
return Object.entries(tools)
|
|
651
|
+
.map(([name, tool]) => {
|
|
652
|
+
let schema = "";
|
|
653
|
+
try {
|
|
654
|
+
schema = JSON.stringify(tool?.inputSchema ? z.toJSONSchema(tool.inputSchema, { unrepresentable: "any" }) : {});
|
|
655
|
+
}
|
|
656
|
+
catch {
|
|
657
|
+
// A provider's own tool (web search) has no schema of ours.
|
|
658
|
+
}
|
|
659
|
+
return { name, chars: String(tool?.description ?? "").length + schema.length };
|
|
660
|
+
})
|
|
661
|
+
.sort((a, b) => b.chars - a.chars);
|
|
662
|
+
}
|
|
644
663
|
/** One turn of the studio assistant. */
|
|
645
664
|
export async function runStudioAssistant(input) {
|
|
646
|
-
|
|
665
|
+
// How often the space or a plugin's data changed: a part of the answer changed something when
|
|
666
|
+
// this grew during it.
|
|
667
|
+
let changes = 0;
|
|
647
668
|
const resolved = await textModel("high");
|
|
648
669
|
const wrote = () => {
|
|
649
|
-
|
|
670
|
+
changes++;
|
|
650
671
|
input.onChanged?.();
|
|
651
672
|
};
|
|
652
673
|
const cards = [];
|
|
@@ -746,6 +767,31 @@ export async function runStudioAssistant(input) {
|
|
|
746
767
|
]
|
|
747
768
|
.filter(Boolean)
|
|
748
769
|
.join("\n\n");
|
|
770
|
+
input.onPrompt?.({
|
|
771
|
+
model: resolved.ref,
|
|
772
|
+
system: SYSTEM,
|
|
773
|
+
context,
|
|
774
|
+
history: history.messages.map((m) => ({ role: m.role, chars: m.content.length })),
|
|
775
|
+
tools: toolSizes(tools),
|
|
776
|
+
});
|
|
777
|
+
let reply = "";
|
|
778
|
+
// Where the part the result holds begins: a message written meanwhile cuts the answer there.
|
|
779
|
+
const from = { reply: 0, cards: 0, toolCalls: 0, changes: 0 };
|
|
780
|
+
const cut = () => {
|
|
781
|
+
const part = {
|
|
782
|
+
reply: reply.slice(from.reply).trim(),
|
|
783
|
+
changed: changes > from.changes,
|
|
784
|
+
cards: cards.slice(from.cards),
|
|
785
|
+
toolCalls: toolCalls.slice(from.toolCalls),
|
|
786
|
+
};
|
|
787
|
+
Object.assign(from, {
|
|
788
|
+
reply: reply.length,
|
|
789
|
+
cards: cards.length,
|
|
790
|
+
toolCalls: toolCalls.length,
|
|
791
|
+
changes,
|
|
792
|
+
});
|
|
793
|
+
return part;
|
|
794
|
+
};
|
|
749
795
|
const stream = await agent.stream([
|
|
750
796
|
...history.messages.map((m) => m.role === "user"
|
|
751
797
|
? { role: "user", content: m.content }
|
|
@@ -755,11 +801,37 @@ export async function runStudioAssistant(input) {
|
|
|
755
801
|
maxSteps: MAX_STEPS,
|
|
756
802
|
abortSignal: input.signal,
|
|
757
803
|
modelSettings: { maxOutputTokens: 8000 },
|
|
804
|
+
// What the person wrote meanwhile comes before the next step, after the tools' results.
|
|
805
|
+
prepareStep: async ({ stepNumber, messageList }) => {
|
|
806
|
+
const taken = stepNumber > 0 ? (input.takeSignals?.() ?? []) : [];
|
|
807
|
+
if (!taken.length) {
|
|
808
|
+
return;
|
|
809
|
+
}
|
|
810
|
+
await input.onSignal?.(taken, cut());
|
|
811
|
+
messageList.add({ role: "user", content: signalText(taken) }, "input");
|
|
812
|
+
return { messageList };
|
|
813
|
+
},
|
|
758
814
|
});
|
|
759
|
-
|
|
815
|
+
const window = contextWindowOf(resolved.ref);
|
|
816
|
+
// A step lasts from the end of the one before – its tools' work included – to its own end.
|
|
817
|
+
let stepStarted = Date.now();
|
|
760
818
|
for await (const chunk of stream.fullStream) {
|
|
761
819
|
const payload = chunk.payload ?? chunk;
|
|
762
|
-
if (chunk.type === "
|
|
820
|
+
if (chunk.type === "step-finish" && input.onStep) {
|
|
821
|
+
const usage = usageOf(payload.output?.usage);
|
|
822
|
+
input.onStep({
|
|
823
|
+
model: resolved.ref,
|
|
824
|
+
inputTokens: usage.inputTokens ?? 0,
|
|
825
|
+
outputTokens: usage.outputTokens ?? 0,
|
|
826
|
+
cachedInputTokens: usage.cachedInputTokens ?? 0,
|
|
827
|
+
ms: Date.now() - stepStarted,
|
|
828
|
+
costUsd: costOf(resolved, payload.output?.usage),
|
|
829
|
+
tools: (payload.output?.toolCalls ?? []).map((c) => c.payload?.toolName ?? c.toolName ?? ""),
|
|
830
|
+
window,
|
|
831
|
+
});
|
|
832
|
+
stepStarted = Date.now();
|
|
833
|
+
}
|
|
834
|
+
else if (chunk.type === "text-delta") {
|
|
763
835
|
const text = payload.text ?? payload.delta ?? "";
|
|
764
836
|
reply += text;
|
|
765
837
|
input.onText?.(text);
|
|
@@ -780,5 +852,5 @@ export async function runStudioAssistant(input) {
|
|
|
780
852
|
}
|
|
781
853
|
}
|
|
782
854
|
const usage = (await stream.totalUsage) ?? (await stream.usage);
|
|
783
|
-
return {
|
|
855
|
+
return { ...cut(), costUsd: costOf(resolved, usage) };
|
|
784
856
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const running = new Map();
|
|
2
|
+
export function startRun(runId, userId) {
|
|
3
|
+
running.set(runId, { userId, inbox: [] });
|
|
4
|
+
}
|
|
5
|
+
export function endRun(runId) {
|
|
6
|
+
running.delete(runId);
|
|
7
|
+
}
|
|
8
|
+
/** Puts a message into the person's running turn; false when that turn is over. */
|
|
9
|
+
export function signalRun(userId, runId, signal) {
|
|
10
|
+
const run = running.get(runId);
|
|
11
|
+
if (!run || run.userId !== userId) {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
run.inbox.push(signal);
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
/** The messages that came in since the turn last looked, oldest first. */
|
|
18
|
+
export function takeSignals(runId) {
|
|
19
|
+
const run = running.get(runId);
|
|
20
|
+
if (!run?.inbox.length) {
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
return run.inbox.splice(0);
|
|
24
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
---
|
|
2
|
-
title:
|
|
2
|
+
title: Goose, the assistant
|
|
3
3
|
description: The chat at the bottom of every studio page. It knows the app, sees the page you are on, shows you where things are and fills the space in.
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.7
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Goose, the assistant, stands at the bottom of every page of the studio but the editor, which has a
|
|
8
8
|
conversation of its own. It thinks on the High class of Text & reasoning: your subscription,
|
|
9
9
|
your keys or credits ([Models and account](./models.md)).
|
|
10
10
|
|
|
@@ -26,20 +26,38 @@ a value into a field for you; you check it and save. It does not press buttons.
|
|
|
26
26
|
| | |
|
|
27
27
|
|---|---|
|
|
28
28
|
| At the bottom | The default. Click the field or the engenty, and the chat rises above it |
|
|
29
|
-
|
|
|
29
|
+
| Closed | ✕ in the header: the engenty at the lower right; a click opens the chat where it last stood. Data and Results start closed |
|
|
30
30
|
| Sidebar | Beside the page, on a window 1,520 pixels wide or more |
|
|
31
31
|
|
|
32
32
|
The buttons in the chat's header switch between them; the studio remembers your choice.
|
|
33
33
|
|
|
34
|
+
At the bottom, the chat moves out of the way of what it covers: drag it by the grip before its
|
|
35
|
+
name (or the engenty, while it is folded). It stays where you put it. Near the bottom it snaps to
|
|
36
|
+
the middle or the right; near the right side, higher up, it becomes the sidebar, and the
|
|
37
|
+
sidebar's grip pulls it out again. A dashed outline shows where it will snap. The handle on top of
|
|
38
|
+
the chat sets how tall the thread is; a click on it folds the chat away.
|
|
39
|
+
|
|
40
|
+
## Writing while it works
|
|
41
|
+
|
|
42
|
+
You can write on while Goose answers. What you send waits above the field:
|
|
43
|
+
|
|
44
|
+
| | |
|
|
45
|
+
|---|---|
|
|
46
|
+
| Enter | Goose reads it before its next step, and keeps what you asked before unless the message changes it |
|
|
47
|
+
| ⌘↩ (Ctrl+↩) | It goes out as the next question once the answer is done |
|
|
48
|
+
| Send now | Stops the answer and sends it at once |
|
|
49
|
+
|
|
50
|
+
The stop button stands where Send does while the field is empty.
|
|
51
|
+
|
|
34
52
|
## One chat
|
|
35
53
|
|
|
36
54
|
You have one chat, the same on every page and in every space, after a reload and on another
|
|
37
55
|
device. Nobody else in the team sees it.
|
|
38
56
|
|
|
39
|
-
- **New chat** (the menu at the chat's name)
|
|
40
|
-
|
|
57
|
+
- **New chat** (the pen in the header, or the menu at the chat's name) clears the chat at once and
|
|
58
|
+
sums the old one up meanwhile. Goose keeps the summaries of your last chats in mind.
|
|
41
59
|
- **Earlier chats** lists the closed ones to read. Ask about something you discussed before, and
|
|
42
|
-
|
|
60
|
+
Goose searches them.
|
|
43
61
|
|
|
44
62
|
## Live chat
|
|
45
63
|
|
package/apps/runtime/dist/env.js
CHANGED
|
@@ -152,6 +152,11 @@ export const env = {
|
|
|
152
152
|
/** Encrypts connected accounts and kept browser sessions; falls back to the app secret. */
|
|
153
153
|
storeKey: str("STORE_ENC_KEY"),
|
|
154
154
|
devLogin: process.env.NODE_ENV !== "production" && str("DEV_LOGIN") === "1",
|
|
155
|
+
/**
|
|
156
|
+
* `1`: the studio assistant's developer mode may be switched on here (steps, tools, prompt,
|
|
157
|
+
* the turns' events). A managed runtime offers it to the platform's admins instead.
|
|
158
|
+
*/
|
|
159
|
+
assistantDev: str("ASSISTANT_DEV") === "1",
|
|
155
160
|
/**
|
|
156
161
|
* The Manage-App this runtime belongs to. Set = people sign in there, tenants come from its
|
|
157
162
|
* tokens and model calls go through its gateway. Empty = the runtime runs alone with one tenant.
|
|
@@ -110,15 +110,15 @@ export function tenantMembers(id) {
|
|
|
110
110
|
return call("GET", `/v1/tenants/${encodeURIComponent(id)}/members`);
|
|
111
111
|
}
|
|
112
112
|
const users = new Map();
|
|
113
|
-
/**
|
|
114
|
-
export async function
|
|
113
|
+
/** What the Manage-App says of a person beyond their token; cached a minute. */
|
|
114
|
+
export async function userInfo(userId) {
|
|
115
115
|
const hit = users.get(userId);
|
|
116
116
|
if (hit && Date.now() - hit.at < 60_000) {
|
|
117
117
|
return hit.value;
|
|
118
118
|
}
|
|
119
119
|
const value = await call("GET", `/v1/users/${encodeURIComponent(userId)}`)
|
|
120
|
-
.then((u) => u.features ?? {})
|
|
121
|
-
.catch(() => hit?.value ?? {});
|
|
120
|
+
.then((u) => ({ features: u.features ?? {}, admin: u.admin === true }))
|
|
121
|
+
.catch(() => hit?.value ?? { features: {}, admin: false });
|
|
122
122
|
users.set(userId, { at: Date.now(), value });
|
|
123
123
|
return value;
|
|
124
124
|
}
|
|
@@ -1013,6 +1013,12 @@ function priceOf(ref) {
|
|
|
1013
1013
|
return (catalog.get(provider === null || provider === "gateway" ? id : `${provider}/${id}`)?.pricing ??
|
|
1014
1014
|
{});
|
|
1015
1015
|
}
|
|
1016
|
+
/** A model's context window in tokens, as the AI Gateway's catalog gives it; null where unknown. */
|
|
1017
|
+
export function contextWindowOf(ref) {
|
|
1018
|
+
const { provider, id } = parse(ref);
|
|
1019
|
+
const entry = catalog.get(provider === null || provider === "gateway" ? id : `${provider}/${id}`);
|
|
1020
|
+
return typeof entry?.context_window === "number" ? entry.context_window : null;
|
|
1021
|
+
}
|
|
1016
1022
|
const n = (v, fallback) => {
|
|
1017
1023
|
const x = Number(v);
|
|
1018
1024
|
return Number.isFinite(x) ? x : fallback;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { mkdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import { ASSISTANT_LAYOUTS, ASSISTANT_PAGE_BYTES, assistantTurnSchema, } from "@engenty-wizards/shared/assistant";
|
|
3
|
+
import { ASSISTANT_LAYOUTS, ASSISTANT_PAGE_BYTES, assistantSignalSchema, assistantTurnSchema, } from "@engenty-wizards/shared/assistant";
|
|
4
4
|
import { MODEL_CLASSES, TEXT_CLASSES, } from "@engenty-wizards/shared/definition";
|
|
5
5
|
import { CATEGORY_TYPES, whereSchema } from "@engenty-wizards/shared/knowledge";
|
|
6
6
|
import { PROJECT_FILE_KINDS } from "@engenty-wizards/shared/projects";
|
|
@@ -11,10 +11,11 @@ import { Hono } from "hono";
|
|
|
11
11
|
import { streamSSE } from "hono/streaming";
|
|
12
12
|
import { nanoid } from "nanoid";
|
|
13
13
|
import { z } from "zod";
|
|
14
|
-
import { runStudioAssistant } from "../agents/studio-assistant.js";
|
|
14
|
+
import { runStudioAssistant, signalText } from "../agents/studio-assistant.js";
|
|
15
15
|
import { chatEngine, setChatEngine, subscriptionClients } from "../agents/subscription.js";
|
|
16
16
|
import { scanColors, scanFonts, scanImages, scanLogos } from "../agents/website.js";
|
|
17
17
|
import { answerBrowser, awaitBrowser } from "../assistant/browser.js";
|
|
18
|
+
import { endRun, signalRun, startRun, takeSignals } from "../assistant/runs.js";
|
|
18
19
|
import { accountOverview, linkedAccount, startLink, unlink } from "../auth/account.js";
|
|
19
20
|
import { createLocalKey, deleteLocalKey, listLocalKeys } from "../auth/keys.js";
|
|
20
21
|
import { appStates, connectApps } from "../cli/connect.js";
|
|
@@ -29,7 +30,7 @@ import { appleStatus } from "../harness/apple.js";
|
|
|
29
30
|
import { freshAuth, loginEnv } from "../harness/env.js";
|
|
30
31
|
import { detectHarness, detectHarnesses, HARNESS_IDS, harness, installSpec, } from "../harness/index.js";
|
|
31
32
|
import { closeTerminal, openTerminal, resizeTerminal, subscribeTerminal, writeTerminal, } from "../harness/terminal.js";
|
|
32
|
-
import { managed, tenantInfo, tenantMembers,
|
|
33
|
+
import { managed, tenantInfo, tenantMembers, userInfo } from "../manage.js";
|
|
33
34
|
import { seenApps } from "../mcp/seen.js";
|
|
34
35
|
import { generateImageMedia, generateSpeechMedia, generateVideoMedia } from "../media/generate.js";
|
|
35
36
|
import { transcribeAudio } from "../media/transcribe.js";
|
|
@@ -190,6 +191,10 @@ async function testClass(cls, lang) {
|
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
/** The size of an inline terminal, as the page lays it out. */
|
|
194
|
+
/** Whether the studio assistant's developer mode is offered to this person. */
|
|
195
|
+
async function assistantDevFor(user) {
|
|
196
|
+
return managed ? (await userInfo(user.id)).admin : env.assistantDev;
|
|
197
|
+
}
|
|
193
198
|
const terminalSize = z.object({
|
|
194
199
|
cols: z.number().int().min(10).max(500).optional(),
|
|
195
200
|
rows: z.number().int().min(4).max(200).optional(),
|
|
@@ -203,7 +208,7 @@ export const studio = new Hono()
|
|
|
203
208
|
const profile = await userProfile(user);
|
|
204
209
|
// What the Manage-App says of the tenant: its plan and feature switches; and of the person.
|
|
205
210
|
const info = managed ? await tenantInfo(user.tenantId).catch(() => null) : null;
|
|
206
|
-
const
|
|
211
|
+
const person = managed ? await userInfo(user.id) : null;
|
|
207
212
|
return c.json({
|
|
208
213
|
user: { id: user.id, name: profile.name, email: profile.email, image: user.image ?? null },
|
|
209
214
|
/** What the person says about themselves; name and e-mail are the account's when managed. */
|
|
@@ -215,7 +220,9 @@ export const studio = new Hono()
|
|
|
215
220
|
plan: info?.plan ?? null,
|
|
216
221
|
},
|
|
217
222
|
/** The feature switches: the code's defaults, what the Manage-App says of the team and the person, this install's file. */
|
|
218
|
-
features: features(managed ? { ...info?.features, ...
|
|
223
|
+
features: features(managed ? { ...info?.features, ...person?.features } : overview?.tenant.features),
|
|
224
|
+
/** The studio assistant's developer mode may be switched on: ASSISTANT_DEV here, the platform's admins when managed. */
|
|
225
|
+
assistantDev: await assistantDevFor(user),
|
|
219
226
|
mode: managed ? "managed" : "local",
|
|
220
227
|
/** The tenant's balance; null where the runtime resolves models itself. */
|
|
221
228
|
credits: managed ? await balanceCredits() : null,
|
|
@@ -317,6 +324,12 @@ export const studio = new Hono()
|
|
|
317
324
|
}
|
|
318
325
|
const voice = await generateSpeechMedia({ text, abortSignal: c.req.raw.signal });
|
|
319
326
|
return c.body(new Uint8Array(voice.bytes), 200, { "content-type": voice.mime });
|
|
327
|
+
})
|
|
328
|
+
// A message written while a turn runs: the turn takes it before its next step or after its answer.
|
|
329
|
+
.post("/assistant/run/:runId/signal", async (c) => {
|
|
330
|
+
const body = assistantSignalSchema.parse(await c.req.json());
|
|
331
|
+
const taken = signalRun(c.get("user").id, c.req.param("runId"), body);
|
|
332
|
+
return c.json({ ok: taken }, taken ? 200 : 404);
|
|
320
333
|
})
|
|
321
334
|
// A browser tool's result: the turn that waits for it goes on.
|
|
322
335
|
.post("/assistant/tool/:callId", async (c) => {
|
|
@@ -339,53 +352,100 @@ export const studio = new Hono()
|
|
|
339
352
|
? { route: body.page.route, title: body.page.title, view: body.page.view }
|
|
340
353
|
: (body.page ?? null);
|
|
341
354
|
const chat = await openChat(user);
|
|
342
|
-
const
|
|
355
|
+
const debug = Boolean(body.debug) && (await assistantDevFor(user));
|
|
356
|
+
let history = await turnHistory(user, chat);
|
|
343
357
|
const runId = nanoid(10);
|
|
344
358
|
const userMessageId = await addMessage(chat.id, {
|
|
345
359
|
role: "user",
|
|
346
360
|
content: body.message,
|
|
347
361
|
page: page ? { route: page.route, title: page.title } : null,
|
|
348
362
|
});
|
|
363
|
+
startRun(runId, user.id);
|
|
349
364
|
return streamSSE(c, async (stream) => {
|
|
350
365
|
const abort = new AbortController();
|
|
351
366
|
stream.onAbort(() => abort.abort());
|
|
352
367
|
const send = (event, data) => stream.writeSSE({ event, data: JSON.stringify(data) });
|
|
353
|
-
|
|
368
|
+
let messageId = nanoid(14);
|
|
354
369
|
await send("run_started", { threadId: chat.id, runId, messageId: userMessageId });
|
|
370
|
+
// The answer's text since the last message written meanwhile.
|
|
355
371
|
let reply = "";
|
|
372
|
+
let changed = false;
|
|
373
|
+
let steps = 0;
|
|
374
|
+
/** What the answer said and did up to messages written meanwhile; null when nothing. */
|
|
375
|
+
const storePart = async (before) => {
|
|
376
|
+
changed ||= before.changed;
|
|
377
|
+
return before.reply || before.toolCalls.length || before.cards.length
|
|
378
|
+
? await addMessage(chat.id, { role: "assistant", content: before.reply, ...before })
|
|
379
|
+
: null;
|
|
380
|
+
};
|
|
381
|
+
/** The messages written meanwhile, after the part of the answer before them. */
|
|
382
|
+
const storeSignals = async (taken, answerId) => {
|
|
383
|
+
for (const [i, s] of taken.entries()) {
|
|
384
|
+
const stored = await addMessage(chat.id, { role: "user", content: s.message });
|
|
385
|
+
await send("signal", {
|
|
386
|
+
ref: s.ref,
|
|
387
|
+
messageId: stored,
|
|
388
|
+
content: s.message,
|
|
389
|
+
answerId: i === 0 ? answerId : null,
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
reply = "";
|
|
393
|
+
messageId = nanoid(14);
|
|
394
|
+
};
|
|
356
395
|
try {
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
396
|
+
let message = body.message;
|
|
397
|
+
for (;;) {
|
|
398
|
+
const result = await runStudioAssistant({
|
|
399
|
+
user,
|
|
400
|
+
chat,
|
|
401
|
+
history,
|
|
402
|
+
project,
|
|
403
|
+
writable,
|
|
404
|
+
message,
|
|
405
|
+
page,
|
|
406
|
+
browserTools: body.tools,
|
|
407
|
+
lang: body.lang,
|
|
408
|
+
signal: abort.signal,
|
|
409
|
+
onText: (delta) => {
|
|
410
|
+
reply += delta;
|
|
411
|
+
void send("text", { messageId, delta });
|
|
412
|
+
},
|
|
413
|
+
onActivity: (label) => void send("activity", { label }),
|
|
414
|
+
onChanged: () => void send("changed", {}),
|
|
415
|
+
onCard: (card) => void send("card", card),
|
|
416
|
+
onToolCall: (call) => void send("tool_call", call),
|
|
417
|
+
onToolResult: (result) => void send("tool_result", result),
|
|
418
|
+
callBrowser: (call) => awaitBrowser(user.id, call.toolCallId, abort.signal),
|
|
419
|
+
takeSignals: () => takeSignals(runId),
|
|
420
|
+
onSignal: async (taken, before) => storeSignals(taken, await storePart(before)),
|
|
421
|
+
...(debug
|
|
422
|
+
? {
|
|
423
|
+
onPrompt: (prompt) => void send("prompt", prompt),
|
|
424
|
+
onStep: (step) => void send("step", { ...step, step: ++steps }),
|
|
425
|
+
}
|
|
426
|
+
: {}),
|
|
427
|
+
});
|
|
428
|
+
// Written after the answer's last step: the turn goes on with it.
|
|
429
|
+
const left = takeSignals(runId);
|
|
430
|
+
if (!left.length || abort.signal.aborted) {
|
|
431
|
+
changed ||= result.changed;
|
|
432
|
+
reply = result.reply || (changed ? (body.lang === "de" ? "Erledigt." : "Done.") : "");
|
|
433
|
+
const stored = await addMessage(chat.id, {
|
|
434
|
+
role: "assistant",
|
|
435
|
+
content: reply,
|
|
436
|
+
cards: result.cards,
|
|
437
|
+
toolCalls: result.toolCalls,
|
|
438
|
+
changed: result.changed,
|
|
439
|
+
});
|
|
440
|
+
await send("done", { messageId: stored, reply, changed });
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
// The next answer knows this one: it is stored before the history is read again.
|
|
444
|
+
const answerId = await storePart(result);
|
|
445
|
+
history = await turnHistory(user, chat);
|
|
446
|
+
await storeSignals(left, answerId);
|
|
447
|
+
message = signalText(left);
|
|
448
|
+
}
|
|
389
449
|
}
|
|
390
450
|
catch (err) {
|
|
391
451
|
console.error("[studio-assistant]", err);
|
|
@@ -400,6 +460,7 @@ export const studio = new Hono()
|
|
|
400
460
|
: "Something went wrong. Please try again.",
|
|
401
461
|
});
|
|
402
462
|
}
|
|
463
|
+
endRun(runId);
|
|
403
464
|
// Older messages fold into the chat's running summary once enough have piled up.
|
|
404
465
|
void foldChat(chat.id).catch((err) => console.error("[studio-assistant] fold:", err));
|
|
405
466
|
});
|
|
@@ -9,8 +9,8 @@ import { ServiceError } from "./errors.js";
|
|
|
9
9
|
import { trigramQuery } from "./project-index.js";
|
|
10
10
|
/**
|
|
11
11
|
* The studio assistant's chats: one open chat per person and tenant, the same on every page and
|
|
12
|
-
* space. "New chat" closes it
|
|
13
|
-
* summaries of the last chats, and older ones are found with `searchChats`.
|
|
12
|
+
* space. "New chat" closes it at once and writes its dense summary meanwhile; every turn of the
|
|
13
|
+
* next one carries the summaries of the last chats, and older ones are found with `searchChats`.
|
|
14
14
|
*/
|
|
15
15
|
/** Messages of the open chat a turn sends as they are; older ones live in its running summary. */
|
|
16
16
|
export const TURN_MESSAGES = 12;
|
|
@@ -74,6 +74,7 @@ export async function addMessage(chatId, message) {
|
|
|
74
74
|
}
|
|
75
75
|
/** What a turn of the open chat carries besides the new message. */
|
|
76
76
|
export async function turnHistory(user, chat) {
|
|
77
|
+
await closedSummaries(user);
|
|
77
78
|
const all = await messagesOf(chat.id);
|
|
78
79
|
const earlier = await db.query.assistantChat.findMany({
|
|
79
80
|
where: and(mine(user), isNotNull(schema.assistantChat.closedAt)),
|
|
@@ -81,8 +82,11 @@ export async function turnHistory(user, chat) {
|
|
|
81
82
|
limit: EARLIER_IN_TURN,
|
|
82
83
|
});
|
|
83
84
|
return {
|
|
85
|
+
// A part of an answer that only called tools (before a message written meanwhile) has no
|
|
86
|
+
// text: models refuse empty messages.
|
|
84
87
|
messages: all
|
|
85
88
|
.slice(chat.folded)
|
|
89
|
+
.filter((m) => m.content.trim())
|
|
86
90
|
.slice(-TURN_MESSAGES)
|
|
87
91
|
.map(({ role, content }) => ({ role, content })),
|
|
88
92
|
running: chat.summary,
|
|
@@ -170,9 +174,45 @@ export async function foldChat(chatId) {
|
|
|
170
174
|
.set({ summary, folded: upTo })
|
|
171
175
|
.where(and(eq(schema.assistantChat.id, chat.id), eq(schema.assistantChat.folded, chat.folded)));
|
|
172
176
|
}
|
|
177
|
+
/** Summaries of chats "New chat" closed, still being written: by chat id, with whose chat it is. */
|
|
178
|
+
const summarising = new Map();
|
|
179
|
+
const whose = (user) => `${user.tenantId}:${user.id}`;
|
|
180
|
+
/** Writes the summary and the title of a closed chat. */
|
|
181
|
+
function summariseClosed(user, chat) {
|
|
182
|
+
const running = summarising.get(chat.id);
|
|
183
|
+
if (running) {
|
|
184
|
+
return running.done;
|
|
185
|
+
}
|
|
186
|
+
const done = (async () => {
|
|
187
|
+
const all = await messagesOf(chat.id);
|
|
188
|
+
const { title, summary } = await summarise(all.slice(chat.folded), chat.summary);
|
|
189
|
+
await db
|
|
190
|
+
.update(schema.assistantChat)
|
|
191
|
+
.set({ ...(title ? { title } : {}), summary })
|
|
192
|
+
.where(eq(schema.assistantChat.id, chat.id));
|
|
193
|
+
})()
|
|
194
|
+
.catch((err) => console.error("[assistant] summary of a closed chat:", err.message))
|
|
195
|
+
.finally(() => summarising.delete(chat.id));
|
|
196
|
+
summarising.set(chat.id, { user: whose(user), done });
|
|
197
|
+
return done;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Waits for the summaries of the person's closed chats: those being written, and any a stopped
|
|
201
|
+
* runtime left unwritten.
|
|
202
|
+
*/
|
|
203
|
+
export async function closedSummaries(user) {
|
|
204
|
+
const unwritten = await db.query.assistantChat.findMany({
|
|
205
|
+
where: and(mine(user), isNotNull(schema.assistantChat.closedAt), isNull(schema.assistantChat.summary)),
|
|
206
|
+
});
|
|
207
|
+
await Promise.all([
|
|
208
|
+
...unwritten.map((chat) => summariseClosed(user, chat)),
|
|
209
|
+
...[...summarising.values()].filter((s) => s.user === whose(user)).map((s) => s.done),
|
|
210
|
+
]);
|
|
211
|
+
}
|
|
173
212
|
/**
|
|
174
|
-
* "New chat": the open chat is
|
|
175
|
-
* an empty one opens. An open chat without
|
|
213
|
+
* "New chat": the open chat is closed at once (kept to read under "Earlier chats", titled by its
|
|
214
|
+
* first question until its summary is written) and an empty one opens. An open chat without
|
|
215
|
+
* messages stays as it is.
|
|
176
216
|
*/
|
|
177
217
|
export async function newChat(user) {
|
|
178
218
|
const chat = await openChat(user);
|
|
@@ -180,11 +220,12 @@ export async function newChat(user) {
|
|
|
180
220
|
if (!all.length) {
|
|
181
221
|
return { id: chat.id, messages: [] };
|
|
182
222
|
}
|
|
183
|
-
const
|
|
223
|
+
const firstAsk = all.find((m) => m.role === "user")?.content ?? "";
|
|
184
224
|
await db
|
|
185
225
|
.update(schema.assistantChat)
|
|
186
|
-
.set({ title,
|
|
226
|
+
.set({ title: firstAsk.replace(/\s+/g, " ").trim().slice(0, 60), closedAt: new Date() })
|
|
187
227
|
.where(eq(schema.assistantChat.id, chat.id));
|
|
228
|
+
void summariseClosed(user, chat);
|
|
188
229
|
const next = await openChat(user);
|
|
189
230
|
return { id: next.id, messages: [] };
|
|
190
231
|
}
|
|
@@ -199,6 +240,8 @@ export async function earlierChats(user, limit = 50) {
|
|
|
199
240
|
id: c.id,
|
|
200
241
|
title: c.title ?? "",
|
|
201
242
|
summary: c.summary ?? "",
|
|
243
|
+
/** Its summary is still being written. */
|
|
244
|
+
summarising: summarising.has(c.id),
|
|
202
245
|
startedAt: c.createdAt.toISOString(),
|
|
203
246
|
closedAt: c.closedAt.toISOString(),
|
|
204
247
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{t as r}from"./logo-C4V2_3Ar.js";import{t as i}from"./preload-helper-CNEI7N0N.js";import{Ct as a,Dt as o,Tt as s,Zt as c,bt as l,ft as u,ht as d,jt as f,kt as p,mt as m,nn as h,pt as g}from"./ui-BOCXzzHi.js";import{ft as _,pt as v,wt as y}from"./index-Df6UxElH.js";import{c as b,f as x}from"./settings-menu-DV5D91TD.js";import{ReleaseNotes as S,loadReleases as C,releaseDate as w,releaseLabel as T}from"./ChangelogDialog-Cv81t7tj.js";var E=e(t(),1),D=[{name:`engenty wizards`,description:{de:`KI-Wizards aus einer Beschreibung, mit einem Link geteilt.`,en:`AI wizards built from a description, shared with a link.`},homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:{de:`Konnektoren, Datentabellen, Suche und die Engenties stammen aus dem engenty-Framework.`,en:`Connectors, data tables, search and the engenties come from the engenty framework.`},homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],O=[{name:`Mastra`,description:{de:`Agent-Framework hinter den KI-Schritten.`,en:`Agent framework behind the AI steps.`},homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:{de:`Modell-Aufrufe für Text, Bild, Video und Sprache.`,en:`Model calls for text, image, video and speech.`},homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:{de:`MCP-Server zum Bauen von Wizards, MCP-Client für Werkzeuge der Schritte.`,en:`MCP server for building wizards, MCP client for the steps' tools.`},homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:{de:`HTTP-API der Runtime.`,en:`The runtime's HTTP API.`},homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:{de:`Schema und Migrationen der Datenbanken.`,en:`Schema and migrations of the databases.`},homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:{de:`Eine Datenbank je Mandant, lokal als Datei oder bei Turso.`,en:`One database per tenant, a local file or at Turso.`},homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:{de:`Browser-Schritte sowie PDF- und PNG-Export.`,en:`Browser steps and the PDF and PNG export.`},homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`agentOS`,description:{de:`Sandbox für Code-Schritte ohne Docker.`,en:`Sandbox for code steps without Docker.`},homepage:`https://github.com/rivet-dev/agentos`,license:`Apache-2.0`},{name:`React`,description:{de:`UI-Bibliothek der Web-App.`,en:`UI library of the web app.`},homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:{de:`Build-Werkzeug der Web-App.`,en:`Build tool of the web app.`},homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:{de:`Styling der Oberfläche.`,en:`Styling of the interface.`},homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:{de:`Das Diagramm der Schritte im Editor.`,en:`The diagram of steps in the editor.`},homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:{de:`Server-Daten in der Web-App.`,en:`Server data in the web app.`},homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:{de:`Das Terminal auf der Seite, in dem sich KI-Clients anmelden.`,en:`The terminal on the page where AI clients sign in.`},homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:{de:`Icons.`,en:`Icons.`},homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:{de:`Schrift der Oberfläche.`,en:`Typeface of the interface.`},homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:{de:`Schrift der Überschriften.`,en:`Typeface of the headings.`},homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:{de:`Erzeugt den Changelog aus den Commits.`,en:`Generates the changelog from the commits.`},homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],k=e(n(),1),A=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function j({name:e,homepage:t,version:n,license:r,description:i}){return(0,k.jsxs)(`li`,{className:`py-2.5`,children:[(0,k.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,k.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[0.875rem] text-ink underline-offset-4 hover:underline`,children:[e,(0,k.jsx)(c,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,k.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),i?(0,k.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:i}):null]})}function M({title:e,hint:t,children:n}){return(0,k.jsxs)(`section`,{className:`mt-6`,children:[(0,k.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,k.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function N({credits:e}){return(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,k.jsx)(j,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[o]},e.name))})}function P({label:e,packages:t}){return(0,k.jsxs)(`div`,{className:`mt-4`,children:[(0,k.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,k.jsx)(j,{...e},e.name))})]})}function F({active:e}){let[t,n]=(0,E.useState)(null),[r,a]=(0,E.useState)(``);(0,E.useEffect)(()=>{if(!e||t)return;let r=!1;return i(()=>import(`./oss-credits-BQQX0kvk.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,E.useEffect)(()=>{e||a(``)},[e]);let o=r.trim().toLowerCase(),s=e=>o?e.filter(e=>A(o,e.name,e.license,e.description.de,e.description.en)):e,c=e=>o?e.filter(e=>A(o,e.name,e.license,e.version)):e,u=s(D),f=s(O),m=t?[{id:`shared`,label:p(`about.creditsShared`),packages:c(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:c(e.packages)}))].filter(e=>e.packages.length>0):[],h=u.length===0&&f.length===0&&m.length===0;return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:p(`about.creditsHint`)}),(0,k.jsx)(d,{type:`search`,value:r,placeholder:p(`about.search`),onChange:e=>a(e.target.value)}),u.length>0?(0,k.jsx)(M,{title:p(`about.creditsProduct`),children:(0,k.jsx)(N,{credits:u})}):null,f.length>0?(0,k.jsx)(M,{title:p(`about.creditsBuiltWith`),children:(0,k.jsx)(N,{credits:f})}):null,t===null?(0,k.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,k.jsx)(l,{})}):m.length>0?(0,k.jsx)(M,{title:p(`about.creditsDeps`),hint:o?void 0:p(`about.creditsDepsHint`),children:m.map(e=>(0,k.jsx)(P,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&h?(0,k.jsx)(g,{children:p(`about.noMatches`)}):null]})}var I=[`new`,`earlier`,`credits`],L=e=>p(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),R=e=>e===1?p(`about.change`):p(`about.changes`,{n:e});function z({open:e,onClose:t,me:n}){let i=n?.mode===`local`,{data:o,latest:c}=v(i&&e),[d,g]=(0,E.useState)(null),[D,O]=(0,E.useState)(`new`),A=x(I.indexOf(D)),j=o?.newer&&c?c:y;(0,E.useEffect)(()=>{if(!e)return;let t=!1;return g(null),C(j===`0.3.7`?void 0:j).then(e=>{t||g(e)}),()=>{t=!0}},[e,j]);let M=d?.find(e=>e.version===`v${j}`)??d?.[0]??null,N=d?.filter(e=>e!==M)??[],P=i?o?.kind??null:`cloud`;return(0,k.jsxs)(u,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,k.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,k.jsx)(m,{label:p(`common.close`),onClick:t,children:(0,k.jsx)(f,{className:`size-4`})})}),(0,k.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,k.jsx)(r,{size:96}),(0,k.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,k.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,k.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,k.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[p(`about.version`,{v:y}),P?` · ${p(`about.kind.${P}`)}`:``]})]}),i?(0,k.jsx)(`div`,{className:`mt-5`,children:(0,k.jsx)(_,{enabled:e})}):null,(0,k.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,k.jsx)(a,{value:D,onChange:O,options:I.map(e=>({id:e,label:L(e)})),label:p(`about.title`)})}),(0,k.jsxs)(`div`,{className:s(`mt-5`,b(A)),children:[D===`new`?d===null?(0,k.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,k.jsx)(l,{})}):M?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[T(M),(0,k.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:w(M)})]}),(0,k.jsx)(`div`,{className:`mt-3`,children:(0,k.jsx)(S,{release:M})})]}):(0,k.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:p(`about.changelogEmpty`)}):null,D===`earlier`?(0,k.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,k.jsxs)(`details`,{className:`group/r py-2`,children:[(0,k.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,k.jsx)(h,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,k.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:T(e)}),(0,k.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:w(e)}),(0,k.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:R(e.commits.length)})]}),(0,k.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,k.jsx)(S,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,k.jsx)(F,{active:!0}):null]},D)]})}export{z as AboutDialog};
|
|
1
|
+
import{r as e}from"./rolldown-runtime-hePW80VL.js";import{n as t,t as n}from"./jsx-runtime-BNakU3Ej.js";import{t as r}from"./logo-C4V2_3Ar.js";import{t as i}from"./preload-helper-CNEI7N0N.js";import{$t as a,At as o,Et as s,Mt as c,Ot as l,gt as u,ht as d,in as f,mt as p,pt as m,wt as h,xt as g}from"./ui-DrjXmHdO.js";import{ft as _,pt as v,wt as y}from"./index-DnQgOKOQ.js";import{c as b,f as x}from"./settings-menu-JuSRq6NP.js";import{ReleaseNotes as S,loadReleases as C,releaseDate as w,releaseLabel as T}from"./ChangelogDialog-DsSEkayM.js";var E=e(t(),1),D=[{name:`engenty wizards`,description:{de:`KI-Wizards aus einer Beschreibung, mit einem Link geteilt.`,en:`AI wizards built from a description, shared with a link.`},homepage:`https://github.com/engenty/wizards`,license:`FSL-1.1-MIT`},{name:`engenty`,description:{de:`Konnektoren, Datentabellen, Suche und die Engenties stammen aus dem engenty-Framework.`,en:`Connectors, data tables, search and the engenties come from the engenty framework.`},homepage:`https://github.com/engenty/engenty`,license:`FSL-1.1-MIT`}],O=[{name:`Mastra`,description:{de:`Agent-Framework hinter den KI-Schritten.`,en:`Agent framework behind the AI steps.`},homepage:`https://github.com/mastra-ai/mastra`,license:`Apache-2.0`},{name:`AI SDK`,description:{de:`Modell-Aufrufe für Text, Bild, Video und Sprache.`,en:`Model calls for text, image, video and speech.`},homepage:`https://github.com/vercel/ai`,license:`Apache-2.0`},{name:`Model Context Protocol`,description:{de:`MCP-Server zum Bauen von Wizards, MCP-Client für Werkzeuge der Schritte.`,en:`MCP server for building wizards, MCP client for the steps' tools.`},homepage:`https://github.com/modelcontextprotocol/typescript-sdk`,license:`MIT`},{name:`Hono`,description:{de:`HTTP-API der Runtime.`,en:`The runtime's HTTP API.`},homepage:`https://github.com/honojs/hono`,license:`MIT`},{name:`Drizzle ORM`,description:{de:`Schema und Migrationen der Datenbanken.`,en:`Schema and migrations of the databases.`},homepage:`https://github.com/drizzle-team/drizzle-orm`,license:`Apache-2.0`},{name:`libSQL`,description:{de:`Eine Datenbank je Mandant, lokal als Datei oder bei Turso.`,en:`One database per tenant, a local file or at Turso.`},homepage:`https://github.com/tursodatabase/libsql`,license:`MIT`},{name:`Playwright`,description:{de:`Browser-Schritte sowie PDF- und PNG-Export.`,en:`Browser steps and the PDF and PNG export.`},homepage:`https://github.com/microsoft/playwright`,license:`Apache-2.0`},{name:`agentOS`,description:{de:`Sandbox für Code-Schritte ohne Docker.`,en:`Sandbox for code steps without Docker.`},homepage:`https://github.com/rivet-dev/agentos`,license:`Apache-2.0`},{name:`React`,description:{de:`UI-Bibliothek der Web-App.`,en:`UI library of the web app.`},homepage:`https://github.com/facebook/react`,license:`MIT`},{name:`Vite`,description:{de:`Build-Werkzeug der Web-App.`,en:`Build tool of the web app.`},homepage:`https://github.com/vitejs/vite`,license:`MIT`},{name:`Tailwind CSS`,description:{de:`Styling der Oberfläche.`,en:`Styling of the interface.`},homepage:`https://github.com/tailwindlabs/tailwindcss`,license:`MIT`},{name:`React Flow`,description:{de:`Das Diagramm der Schritte im Editor.`,en:`The diagram of steps in the editor.`},homepage:`https://github.com/xyflow/xyflow`,license:`MIT`},{name:`TanStack Query`,description:{de:`Server-Daten in der Web-App.`,en:`Server data in the web app.`},homepage:`https://github.com/TanStack/query`,license:`MIT`},{name:`xterm.js`,description:{de:`Das Terminal auf der Seite, in dem sich KI-Clients anmelden.`,en:`The terminal on the page where AI clients sign in.`},homepage:`https://github.com/xtermjs/xterm.js`,license:`MIT`},{name:`Lucide`,description:{de:`Icons.`,en:`Icons.`},homepage:`https://github.com/lucide-icons/lucide`,license:`ISC`},{name:`Geist & Geist Mono`,description:{de:`Schrift der Oberfläche.`,en:`Typeface of the interface.`},homepage:`https://github.com/vercel/geist-font`,license:`OFL-1.1`},{name:`Space Grotesk`,description:{de:`Schrift der Überschriften.`,en:`Typeface of the headings.`},homepage:`https://github.com/floriankarsten/space-grotesk`,license:`OFL-1.1`},{name:`git-cliff`,description:{de:`Erzeugt den Changelog aus den Commits.`,en:`Generates the changelog from the commits.`},homepage:`https://github.com/orhun/git-cliff`,license:`MIT OR Apache-2.0`}],k=e(n(),1),A=(e,...t)=>t.some(t=>t?.toLowerCase().includes(e));function j({name:e,homepage:t,version:n,license:r,description:i}){return(0,k.jsxs)(`li`,{className:`py-2.5`,children:[(0,k.jsxs)(`div`,{className:`flex flex-wrap items-baseline gap-x-2.5 gap-y-0.5`,children:[t?(0,k.jsxs)(`a`,{href:t,target:`_blank`,rel:`noreferrer`,className:`inline-flex items-center gap-1 font-medium text-[0.875rem] text-ink underline-offset-4 hover:underline`,children:[e,(0,k.jsx)(a,{"aria-hidden":!0,className:`size-3 text-ink-4`})]}):(0,k.jsx)(`span`,{className:`font-medium text-[0.875rem] text-ink`,children:e}),n?(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3 tabular-nums`,children:n}):null,(0,k.jsx)(`span`,{className:`text-[0.75rem] text-ink-3`,children:r})]}),i?(0,k.jsx)(`p`,{className:`mt-0.5 text-[0.8125rem] text-ink-3`,children:i}):null]})}function M({title:e,hint:t,children:n}){return(0,k.jsxs)(`section`,{className:`mt-6`,children:[(0,k.jsx)(`h3`,{className:`border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:e}),t?(0,k.jsx)(`p`,{className:`mt-2 text-[0.8125rem] text-ink-3`,children:t}):null,n]})}function N({credits:e}){return(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:e.map(e=>(0,k.jsx)(j,{name:e.name,homepage:e.homepage,license:e.license,description:e.description[l]},e.name))})}function P({label:e,packages:t}){return(0,k.jsxs)(`div`,{className:`mt-4`,children:[(0,k.jsx)(`h4`,{className:`font-medium text-[0.75rem] text-ink-3 tracking-wide`,children:e}),(0,k.jsx)(`ul`,{className:`divide-y divide-border-soft`,children:t.map(e=>(0,k.jsx)(j,{...e},e.name))})]})}function F({active:e}){let[t,n]=(0,E.useState)(null),[r,a]=(0,E.useState)(``);(0,E.useEffect)(()=>{if(!e||t)return;let r=!1;return i(()=>import(`./oss-credits-BQQX0kvk.js`).then(e=>{r||n(e.default)}),[]),()=>{r=!0}},[e,t]),(0,E.useEffect)(()=>{e||a(``)},[e]);let s=r.trim().toLowerCase(),c=e=>s?e.filter(e=>A(s,e.name,e.license,e.description.de,e.description.en)):e,l=e=>s?e.filter(e=>A(s,e.name,e.license,e.version)):e,d=c(D),f=c(O),m=t?[{id:`shared`,label:o(`about.creditsShared`),packages:l(t.shared)},...t.groups.map(e=>({id:e.id,label:e.label,packages:l(e.packages)}))].filter(e=>e.packages.length>0):[],h=d.length===0&&f.length===0&&m.length===0;return(0,k.jsxs)(k.Fragment,{children:[(0,k.jsx)(`p`,{className:`mb-4 text-[0.875rem] text-ink-3`,children:o(`about.creditsHint`)}),(0,k.jsx)(u,{type:`search`,value:r,placeholder:o(`about.search`),onChange:e=>a(e.target.value)}),d.length>0?(0,k.jsx)(M,{title:o(`about.creditsProduct`),children:(0,k.jsx)(N,{credits:d})}):null,f.length>0?(0,k.jsx)(M,{title:o(`about.creditsBuiltWith`),children:(0,k.jsx)(N,{credits:f})}):null,t===null?(0,k.jsx)(`div`,{className:`flex justify-center py-10 text-ink-3`,children:(0,k.jsx)(g,{})}):m.length>0?(0,k.jsx)(M,{title:o(`about.creditsDeps`),hint:s?void 0:o(`about.creditsDepsHint`),children:m.map(e=>(0,k.jsx)(P,{label:e.label,packages:e.packages},e.id))}):null,t!==null&&h?(0,k.jsx)(p,{children:o(`about.noMatches`)}):null]})}var I=[`new`,`earlier`,`credits`],L=e=>o(e===`new`?`about.tab.new`:e===`earlier`?`about.tab.earlier`:`about.credits`),R=e=>e===1?o(`about.change`):o(`about.changes`,{n:e});function z({open:e,onClose:t,me:n}){let i=n?.mode===`local`,{data:a,latest:l}=v(i&&e),[u,p]=(0,E.useState)(null),[D,O]=(0,E.useState)(`new`),A=x(I.indexOf(D)),j=a?.newer&&l?l:y;(0,E.useEffect)(()=>{if(!e)return;let t=!1;return p(null),C(j===`0.3.8`?void 0:j).then(e=>{t||p(e)}),()=>{t=!0}},[e,j]);let M=u?.find(e=>e.version===`v${j}`)??u?.[0]??null,N=u?.filter(e=>e!==M)??[],P=i?a?.kind??null:`cloud`;return(0,k.jsxs)(m,{open:e,onClose:t,wide:!0,height:`tall`,children:[(0,k.jsx)(`div`,{className:`-mt-2 -mr-2 sticky top-0 z-10 flex h-0 justify-end`,children:(0,k.jsx)(d,{label:o(`common.close`),onClick:t,children:(0,k.jsx)(c,{className:`size-4`})})}),(0,k.jsxs)(`div`,{className:`flex flex-col items-center pt-4 text-center`,children:[(0,k.jsx)(r,{size:96}),(0,k.jsxs)(`div`,{className:`mt-4 font-display font-semibold text-[1.5rem] tracking-tight`,children:[`engenty`,(0,k.jsx)(`span`,{className:`text-ember`,children:`.`}),(0,k.jsx)(`span`,{className:`ml-1.5 font-normal text-ink-3`,children:`wizards`})]}),(0,k.jsxs)(`div`,{className:`mt-1 text-[0.9375rem] text-ink-3 tabular-nums`,children:[o(`about.version`,{v:y}),P?` · ${o(`about.kind.${P}`)}`:``]})]}),i?(0,k.jsx)(`div`,{className:`mt-5`,children:(0,k.jsx)(_,{enabled:e})}):null,(0,k.jsx)(`div`,{className:`mt-6 flex justify-center`,children:(0,k.jsx)(h,{value:D,onChange:O,options:I.map(e=>({id:e,label:L(e)})),label:o(`about.title`)})}),(0,k.jsxs)(`div`,{className:s(`mt-5`,b(A)),children:[D===`new`?u===null?(0,k.jsx)(`div`,{className:`flex justify-center py-6 text-ink-3`,children:(0,k.jsx)(g,{})}):M?(0,k.jsxs)(k.Fragment,{children:[(0,k.jsxs)(`h3`,{className:`flex items-baseline gap-3 border-border-soft border-b pb-2 font-display font-semibold text-[1rem]`,children:[T(M),(0,k.jsx)(`span`,{className:`font-normal font-sans text-[0.8125rem] text-ink-3 tabular-nums`,children:w(M)})]}),(0,k.jsx)(`div`,{className:`mt-3`,children:(0,k.jsx)(S,{release:M})})]}):(0,k.jsx)(`p`,{className:`text-[0.875rem] text-ink-3`,children:o(`about.changelogEmpty`)}):null,D===`earlier`?(0,k.jsx)(`div`,{className:`flex flex-col divide-y divide-border-soft`,children:N.map(e=>(0,k.jsxs)(`details`,{className:`group/r py-2`,children:[(0,k.jsxs)(`summary`,{className:`flex cursor-pointer list-none items-baseline gap-3 text-[0.875rem] [&::-webkit-details-marker]:hidden`,children:[(0,k.jsx)(f,{className:`size-3.5 shrink-0 self-center text-ink-4 transition group-open/r:rotate-90`}),(0,k.jsx)(`span`,{className:`font-medium text-ink tabular-nums`,children:T(e)}),(0,k.jsx)(`span`,{className:`text-[0.8125rem] text-ink-3 tabular-nums`,children:w(e)}),(0,k.jsx)(`span`,{className:`ml-auto text-[0.8125rem] text-ink-4`,children:R(e.commits.length)})]}),(0,k.jsx)(`div`,{className:`mt-2 pl-6`,children:(0,k.jsx)(S,{release:e})})]},e.version??`unreleased`))}):null,D===`credits`?(0,k.jsx)(F,{active:!0}):null]},D)]})}export{z as AboutDialog};
|