@waifucave/discord-waifus 1.5.176 → 1.5.178

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 (36) hide show
  1. package/dist/api/assistant/conversations.d.ts +51 -0
  2. package/dist/api/assistant/conversations.js +63 -0
  3. package/dist/api/assistant/conversations.js.map +1 -0
  4. package/dist/api/assistant/routes.d.ts +9 -0
  5. package/dist/api/assistant/routes.js +63 -0
  6. package/dist/api/assistant/routes.js.map +1 -0
  7. package/dist/api/assistant/service.d.ts +37 -0
  8. package/dist/api/assistant/service.js +124 -0
  9. package/dist/api/assistant/service.js.map +1 -0
  10. package/dist/api/assistant/tools.d.ts +15 -0
  11. package/dist/api/assistant/tools.js +507 -0
  12. package/dist/api/assistant/tools.js.map +1 -0
  13. package/dist/api/docsKb.d.ts +14 -0
  14. package/dist/api/docsKb.js +46 -0
  15. package/dist/api/docsKb.js.map +1 -0
  16. package/dist/api/server.d.ts +8 -0
  17. package/dist/api/server.js +21 -0
  18. package/dist/api/server.js.map +1 -1
  19. package/dist/config/layout.d.ts +1 -1
  20. package/dist/config/layout.js +1 -0
  21. package/dist/config/layout.js.map +1 -1
  22. package/dist/orchestration/pipeline/gatewayPipeline.d.ts +2 -1
  23. package/dist/orchestration/pipeline/gatewayPipeline.js +49 -0
  24. package/dist/orchestration/pipeline/gatewayPipeline.js.map +1 -1
  25. package/dist/providers/types.d.ts +28 -0
  26. package/dist/shared/queryLog.d.ts +1 -1
  27. package/dist/shared/queryLog.js.map +1 -1
  28. package/docs/assistant-kb/api.md +70 -0
  29. package/docs/assistant-kb/discord-setup.md +40 -0
  30. package/docs/assistant-kb/getting-started.md +42 -0
  31. package/docs/assistant-kb/memory.md +34 -0
  32. package/docs/assistant-kb/orchestrator-and-direction.md +46 -0
  33. package/docs/assistant-kb/providers-and-models.md +39 -0
  34. package/docs/assistant-kb/troubleshooting.md +46 -0
  35. package/docs/assistant-kb/waifus.md +38 -0
  36. package/package.json +2 -1
@@ -0,0 +1,51 @@
1
+ import type { ChatMessage } from "@waifucave/gateway";
2
+ export type AssistantEvent = {
3
+ type: "turn_started";
4
+ } | {
5
+ type: "text";
6
+ content: string;
7
+ } | {
8
+ type: "tool_call";
9
+ name: string;
10
+ arguments: string;
11
+ } | {
12
+ type: "tool_result";
13
+ name: string;
14
+ result: string;
15
+ } | {
16
+ type: "turn_completed";
17
+ } | {
18
+ type: "error";
19
+ message: string;
20
+ };
21
+ export type StoredMessage = {
22
+ role: "user" | "assistant";
23
+ content: string;
24
+ at: string;
25
+ } | {
26
+ role: "event";
27
+ event: AssistantEvent;
28
+ at: string;
29
+ };
30
+ /**
31
+ * In-memory conversation state for the dashboard assistant. `messages` is the display
32
+ * transcript (user/assistant text plus tool events); `chat` is the model-facing gateway
33
+ * transcript. Both live only for the process lifetime — v1 has no persistence by design.
34
+ */
35
+ export declare class ConversationStore {
36
+ private readonly conversations;
37
+ create(): {
38
+ id: string;
39
+ };
40
+ get(id: string): {
41
+ id: string;
42
+ messages: StoredMessage[];
43
+ chat: ChatMessage[];
44
+ busy: boolean;
45
+ } | undefined;
46
+ appendChat(id: string, messages: ChatMessage[]): void;
47
+ appendStored(id: string, message: StoredMessage): void;
48
+ setBusy(id: string, busy: boolean): void;
49
+ subscribe(id: string, listener: (event: AssistantEvent) => void): () => void;
50
+ emit(id: string, event: AssistantEvent): void;
51
+ }
@@ -0,0 +1,63 @@
1
+ import { randomUUID } from "node:crypto";
2
+ const MAX_CONVERSATIONS = 20;
3
+ const MAX_STORED_MESSAGES = 200;
4
+ /**
5
+ * In-memory conversation state for the dashboard assistant. `messages` is the display
6
+ * transcript (user/assistant text plus tool events); `chat` is the model-facing gateway
7
+ * transcript. Both live only for the process lifetime — v1 has no persistence by design.
8
+ */
9
+ export class ConversationStore {
10
+ conversations = new Map();
11
+ create() {
12
+ const id = randomUUID();
13
+ this.conversations.set(id, { id, messages: [], chat: [], busy: false, listeners: new Set() });
14
+ // Map preserves insertion order — evict the oldest past the cap.
15
+ while (this.conversations.size > MAX_CONVERSATIONS) {
16
+ const oldest = this.conversations.keys().next().value;
17
+ this.conversations.delete(oldest);
18
+ }
19
+ return { id };
20
+ }
21
+ get(id) {
22
+ const conversation = this.conversations.get(id);
23
+ if (!conversation)
24
+ return undefined;
25
+ const { listeners: _listeners, ...visible } = conversation;
26
+ return visible;
27
+ }
28
+ appendChat(id, messages) {
29
+ const conversation = this.conversations.get(id);
30
+ if (conversation)
31
+ conversation.chat = messages;
32
+ }
33
+ appendStored(id, message) {
34
+ const conversation = this.conversations.get(id);
35
+ if (!conversation)
36
+ return;
37
+ conversation.messages.push(message);
38
+ if (conversation.messages.length > MAX_STORED_MESSAGES) {
39
+ conversation.messages.splice(0, conversation.messages.length - MAX_STORED_MESSAGES);
40
+ }
41
+ }
42
+ setBusy(id, busy) {
43
+ const conversation = this.conversations.get(id);
44
+ if (conversation)
45
+ conversation.busy = busy;
46
+ }
47
+ subscribe(id, listener) {
48
+ const conversation = this.conversations.get(id);
49
+ if (!conversation)
50
+ return () => undefined;
51
+ conversation.listeners.add(listener);
52
+ return () => conversation.listeners.delete(listener);
53
+ }
54
+ emit(id, event) {
55
+ const conversation = this.conversations.get(id);
56
+ if (!conversation)
57
+ return;
58
+ this.appendStored(id, { role: "event", event, at: new Date().toISOString() });
59
+ for (const listener of conversation.listeners)
60
+ listener(event);
61
+ }
62
+ }
63
+ //# sourceMappingURL=conversations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../../src/api/assistant/conversations.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { ModelPipeline } from "../../providers/types.js";
3
+ export declare function registerAssistantRoutes(app: FastifyInstance, options: {
4
+ dataRoot: string;
5
+ createPipeline?: (target: {
6
+ providerId: string;
7
+ modelId: string;
8
+ }) => ModelPipeline;
9
+ }): void;
@@ -0,0 +1,63 @@
1
+ import { z } from "zod";
2
+ import { ConversationStore } from "./conversations.js";
3
+ import { AssistantTurnError, runAssistantTurn } from "./service.js";
4
+ const MessageBodySchema = z.object({ content: z.string().min(1).max(8000) });
5
+ export function registerAssistantRoutes(app, options) {
6
+ const store = new ConversationStore();
7
+ app.post("/api/assistant/conversations", async () => {
8
+ const { id } = store.create();
9
+ return { conversationId: id };
10
+ });
11
+ app.get("/api/assistant/conversations/:id", async (request, reply) => {
12
+ const { id } = request.params;
13
+ const conversation = store.get(id);
14
+ if (!conversation)
15
+ return reply.code(404).send({ error: "unknown conversation" });
16
+ return { id: conversation.id, busy: conversation.busy, messages: conversation.messages };
17
+ });
18
+ app.post("/api/assistant/conversations/:id/messages", async (request, reply) => {
19
+ const { id } = request.params;
20
+ const body = MessageBodySchema.parse(request.body);
21
+ try {
22
+ const content = await runAssistantTurn({ app, store, dataRoot: options.dataRoot, createPipeline: options.createPipeline }, id, body.content);
23
+ return { reply: content };
24
+ }
25
+ catch (error) {
26
+ if (error instanceof AssistantTurnError) {
27
+ return reply.code(error.statusCode).send({ error: error.message });
28
+ }
29
+ throw error;
30
+ }
31
+ });
32
+ app.get("/api/assistant/conversations/:id/stream", (request, reply) => {
33
+ const { id } = request.params;
34
+ const conversation = store.get(id);
35
+ if (!conversation) {
36
+ reply.code(404).send({ error: "unknown conversation" });
37
+ return;
38
+ }
39
+ reply.raw.writeHead(200, {
40
+ "content-type": "text/event-stream",
41
+ "cache-control": "no-cache",
42
+ connection: "keep-alive"
43
+ });
44
+ // Replay recorded events so a reopened panel catches up, then stream live.
45
+ for (const message of conversation.messages) {
46
+ if (message.role === "event") {
47
+ reply.raw.write(`event: assistant\ndata: ${JSON.stringify(message.event)}\n\n`);
48
+ }
49
+ }
50
+ const unsubscribe = store.subscribe(id, (event) => {
51
+ reply.raw.write(`event: assistant\ndata: ${JSON.stringify(event)}\n\n`);
52
+ });
53
+ const heartbeat = setInterval(() => {
54
+ reply.raw.write(`event: heartbeat\ndata: ${JSON.stringify({ time: new Date().toISOString() })}\n\n`);
55
+ }, 30_000);
56
+ request.raw.on("close", () => {
57
+ clearInterval(heartbeat);
58
+ unsubscribe();
59
+ reply.raw.end();
60
+ });
61
+ });
62
+ }
63
+ //# sourceMappingURL=routes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/api/assistant/routes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { ModelPipeline } from "../../providers/types.js";
3
+ import { ConversationStore } from "./conversations.js";
4
+ export type AssistantTarget = {
5
+ ok: true;
6
+ providerId: string;
7
+ modelId: string;
8
+ params: Record<string, unknown>;
9
+ } | {
10
+ ok: false;
11
+ reason: string;
12
+ };
13
+ /**
14
+ * The assistant runs on its own configured model when set, otherwise it borrows the
15
+ * orchestrator's. Params always come from the config that supplied the model.
16
+ */
17
+ export declare function resolveAssistantTarget(app: FastifyInstance, dataRoot: string): Promise<AssistantTarget>;
18
+ export declare function assistantSystemPrompt(snapshot: {
19
+ waifuCount: number;
20
+ serverCount: number;
21
+ providerIds: string[];
22
+ discordConnected: boolean;
23
+ }): string;
24
+ export type AssistantServiceDeps = {
25
+ app: FastifyInstance;
26
+ store: ConversationStore;
27
+ dataRoot: string;
28
+ createPipeline?: (target: {
29
+ providerId: string;
30
+ modelId: string;
31
+ }) => ModelPipeline;
32
+ };
33
+ export declare class AssistantTurnError extends Error {
34
+ readonly statusCode: number;
35
+ constructor(statusCode: number, message: string);
36
+ }
37
+ export declare function runAssistantTurn(deps: AssistantServiceDeps, conversationId: string, userContent: string): Promise<string>;
@@ -0,0 +1,124 @@
1
+ import { createGatewayModelPipeline } from "../../orchestration/pipeline/gatewayPipeline.js";
2
+ import { resolveModelTarget } from "../../orchestration/pipeline/resolveTarget.js";
3
+ import { createProviderCredentialsLookup } from "../llmGatewayCredentials.js";
4
+ import { executeAssistantTool, toolDefs } from "./tools.js";
5
+ async function getJson(app, url) {
6
+ const response = await app.inject({ method: "GET", url });
7
+ if (response.statusCode !== 200)
8
+ return undefined;
9
+ return JSON.parse(response.body);
10
+ }
11
+ /**
12
+ * The assistant runs on its own configured model when set, otherwise it borrows the
13
+ * orchestrator's. Params always come from the config that supplied the model.
14
+ */
15
+ export async function resolveAssistantTarget(app, dataRoot) {
16
+ const assistant = await getJson(app, "/api/assistant/config");
17
+ const orchestrator = await getJson(app, "/api/orchestrator/config");
18
+ const source = assistant?.modelId ? assistant : orchestrator?.modelId ? orchestrator : undefined;
19
+ if (!source) {
20
+ return { ok: false, reason: "No model configured — set one on the assistant or the orchestrator." };
21
+ }
22
+ let target;
23
+ try {
24
+ target = resolveModelTarget({
25
+ providerId: source.providerId,
26
+ modelId: source.modelId
27
+ });
28
+ }
29
+ catch {
30
+ return { ok: false, reason: `Model ${String(source.modelId)} is not recognized.` };
31
+ }
32
+ if (!createProviderCredentialsLookup(dataRoot)(target.providerId)) {
33
+ return { ok: false, reason: `Provider ${target.providerId} has no API key configured.` };
34
+ }
35
+ return { ok: true, ...target, params: source.params ?? {} };
36
+ }
37
+ export function assistantSystemPrompt(snapshot) {
38
+ return [
39
+ "You are the Discord Waifus dashboard assistant. You operate a locally-hosted multi-character Discord bot app on the user's behalf.",
40
+ "You have tools that read AND directly modify live configuration (waifus, servers, providers, agent configs, memories) plus a docs knowledge base (docs_search/docs_read).",
41
+ "Rules:",
42
+ "- Changes apply immediately. For destructive or hard-to-reverse actions (deleting a waifu, replacing a bot token, clearing a provider key), restate what you are about to do and ask the user to confirm in chat before calling the tool.",
43
+ "- Never echo API keys or bot tokens back to the user, even if they appear in tool output.",
44
+ "- Prefer docs_search before answering how-to questions you are not certain about.",
45
+ "- Be concise. Report what you changed with the field values that matter.",
46
+ `Current state: ${snapshot.waifuCount} waifus, ${snapshot.serverCount} servers, providers configured: ${snapshot.providerIds.join(", ") || "none"}, discord ${snapshot.discordConnected ? "connected" : "disconnected"}.`
47
+ ].join("\n");
48
+ }
49
+ async function buildSnapshot(app) {
50
+ const [status, waifus, servers, providers] = await Promise.all([
51
+ getJson(app, "/api/status"),
52
+ getJson(app, "/api/waifus"),
53
+ getJson(app, "/api/servers"),
54
+ getJson(app, "/api/providers")
55
+ ]);
56
+ const providerList = providers?.providers ?? [];
57
+ return {
58
+ waifuCount: (waifus?.waifus ?? []).length,
59
+ serverCount: (servers?.servers ?? []).length,
60
+ providerIds: providerList
61
+ .filter((provider) => provider.credentials?.configured)
62
+ .map((provider) => String(provider.providerId ?? provider.id)),
63
+ discordConnected: Boolean(status?.discord?.connected)
64
+ };
65
+ }
66
+ export class AssistantTurnError extends Error {
67
+ statusCode;
68
+ constructor(statusCode, message) {
69
+ super(message);
70
+ this.statusCode = statusCode;
71
+ }
72
+ }
73
+ export async function runAssistantTurn(deps, conversationId, userContent) {
74
+ const { app, store, dataRoot } = deps;
75
+ const conversation = store.get(conversationId);
76
+ if (!conversation)
77
+ throw new AssistantTurnError(404, "Unknown conversation.");
78
+ if (conversation.busy)
79
+ throw new AssistantTurnError(409, "A turn is already running in this conversation.");
80
+ const target = await resolveAssistantTarget(app, dataRoot);
81
+ if (!target.ok)
82
+ throw new AssistantTurnError(503, target.reason);
83
+ const pipeline = deps.createPipeline
84
+ ? deps.createPipeline({ providerId: target.providerId, modelId: target.modelId })
85
+ : createGatewayModelPipeline({
86
+ providerId: target.providerId,
87
+ modelId: target.modelId,
88
+ queryRole: "assistant",
89
+ dataRoot
90
+ });
91
+ if (!pipeline.generateAssistantTurn)
92
+ throw new AssistantTurnError(503, "The resolved pipeline cannot run assistant turns.");
93
+ const transcript = conversation.chat.length > 0
94
+ ? [...conversation.chat]
95
+ : [{ role: "system", content: assistantSystemPrompt(await buildSnapshot(app)) }];
96
+ transcript.push({ role: "user", content: userContent });
97
+ store.setBusy(conversationId, true);
98
+ store.appendStored(conversationId, { role: "user", content: userContent, at: new Date().toISOString() });
99
+ store.emit(conversationId, { type: "turn_started" });
100
+ try {
101
+ const result = await pipeline.generateAssistantTurn({
102
+ modelId: target.modelId,
103
+ messages: transcript,
104
+ tools: toolDefs(),
105
+ params: target.params,
106
+ executeTool: (name, argsJson) => executeAssistantTool({ app }, name, argsJson),
107
+ onEvent: (event) => store.emit(conversationId, event)
108
+ });
109
+ store.appendChat(conversationId, result.messages);
110
+ store.appendStored(conversationId, { role: "assistant", content: result.content, at: new Date().toISOString() });
111
+ store.emit(conversationId, { type: "text", content: result.content });
112
+ store.emit(conversationId, { type: "turn_completed" });
113
+ return result.content;
114
+ }
115
+ catch (error) {
116
+ const message = error instanceof Error ? error.message : String(error);
117
+ store.emit(conversationId, { type: "error", message });
118
+ throw new AssistantTurnError(500, message);
119
+ }
120
+ finally {
121
+ store.setBusy(conversationId, false);
122
+ }
123
+ }
124
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/api/assistant/service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { FastifyInstance } from "fastify";
2
+ import type { ToolDef } from "@waifucave/gateway";
3
+ export type AssistantToolContext = {
4
+ app: FastifyInstance;
5
+ };
6
+ export type AssistantTool = {
7
+ name: string;
8
+ description: string;
9
+ parameters: Record<string, unknown>;
10
+ execute: (ctx: AssistantToolContext, args: Record<string, unknown>) => Promise<string>;
11
+ };
12
+ export declare const ASSISTANT_TOOLS: AssistantTool[];
13
+ export declare function toolDefs(): ToolDef[];
14
+ /** Execute a tool by name. Never throws — failures come back as strings the model can react to. */
15
+ export declare function executeAssistantTool(ctx: AssistantToolContext, name: string, argsJson: string): Promise<string>;