@starlight-ai/discord-waifus 1.3.8 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/prebuiltWaifus.d.ts +1 -1
- package/dist/config/prebuiltWaifus.js +43 -0
- package/dist/config/prebuiltWaifus.js.map +1 -1
- package/dist/orchestration/decisions.d.ts +1 -0
- package/dist/orchestration/decisions.js +1 -0
- package/dist/orchestration/decisions.js.map +1 -1
- package/dist/orchestration/runtime.js +148 -26
- package/dist/orchestration/runtime.js.map +1 -1
- package/dist/providers/pipelines.js +138 -14
- package/dist/providers/pipelines.js.map +1 -1
- package/dist/providers/types.d.ts +2 -0
- package/dist/shared/schemas/domain.d.ts +48 -0
- package/dist/shared/schemas/domain.js +90 -1
- package/dist/shared/schemas/domain.js.map +1 -1
- package/dist-frontend/assets/index-Cw_qZ3q9.js +32 -0
- package/dist-frontend/assets/index-Cw_qZ3q9.js.map +1 -0
- package/dist-frontend/index.html +1 -1
- package/package.json +1 -1
- package/dist-frontend/assets/index-CgEzMftv.js +0 -32
- package/dist-frontend/assets/index-CgEzMftv.js.map +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { WaifuConfig } from "../shared/schemas/domain.js";
|
|
2
|
-
export type PrebuiltWaifu = Pick<WaifuConfig, "id" | "name" | "displayName" | "enabled" | "persona" | "contextWindow" | "generation">;
|
|
2
|
+
export type PrebuiltWaifu = Pick<WaifuConfig, "id" | "name" | "displayName" | "enabled" | "persona" | "contextWindow" | "generation" | "availability" | "tools">;
|
|
3
3
|
export declare const PREBUILT_WAIFUS: PrebuiltWaifu[];
|
|
@@ -9,6 +9,17 @@ export const PREBUILT_WAIFUS = [
|
|
|
9
9
|
temperature: 0.8,
|
|
10
10
|
topP: 0.95
|
|
11
11
|
},
|
|
12
|
+
availability: {
|
|
13
|
+
sleep: { enabled: true, start: "00:30", end: "08:30" },
|
|
14
|
+
busy: [
|
|
15
|
+
{ start: "13:00", end: "14:00", reason: "offline for lunch and errands" },
|
|
16
|
+
{ start: "19:00", end: "20:30", reason: "winding down away from chat" }
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
tools: {
|
|
20
|
+
toolUse: true,
|
|
21
|
+
pickNextWaifu: true
|
|
22
|
+
},
|
|
12
23
|
persona: [
|
|
13
24
|
"Lumi is warm, bright, and emotionally attentive. She notices small mood shifts in chat and responds with gentle curiosity instead of forcing positivity.",
|
|
14
25
|
"She speaks in short, natural Discord messages, uses soft humor, and is good at making quieter users feel included.",
|
|
@@ -25,6 +36,16 @@ export const PREBUILT_WAIFUS = [
|
|
|
25
36
|
temperature: 0.9,
|
|
26
37
|
topP: 0.9
|
|
27
38
|
},
|
|
39
|
+
availability: {
|
|
40
|
+
sleep: { enabled: true, start: "03:00", end: "11:00" },
|
|
41
|
+
busy: [
|
|
42
|
+
{ start: "16:30", end: "17:30", reason: "pretending to be productive" }
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
tools: {
|
|
46
|
+
toolUse: true,
|
|
47
|
+
pickNextWaifu: true
|
|
48
|
+
},
|
|
28
49
|
persona: [
|
|
29
50
|
"Nox is dry, witty, and a little mischievous. She likes deadpan one-liners, clever callbacks, and playful skepticism.",
|
|
30
51
|
"She is never cruel: the teasing should feel like a friend poking fun, not an insult. She backs off when the conversation gets serious.",
|
|
@@ -41,6 +62,17 @@ export const PREBUILT_WAIFUS = [
|
|
|
41
62
|
temperature: 0.75,
|
|
42
63
|
topP: 0.9
|
|
43
64
|
},
|
|
65
|
+
availability: {
|
|
66
|
+
sleep: { enabled: true, start: "23:00", end: "06:30" },
|
|
67
|
+
busy: [
|
|
68
|
+
{ start: "09:00", end: "11:00", reason: "focused planning block" },
|
|
69
|
+
{ start: "15:00", end: "16:00", reason: "quiet work session" }
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
tools: {
|
|
73
|
+
toolUse: true,
|
|
74
|
+
pickNextWaifu: true
|
|
75
|
+
},
|
|
44
76
|
persona: [
|
|
45
77
|
"Mira is calm, precise, and quietly competent. She enjoys organizing messy conversations, asking useful questions, and helping people decide what to do next.",
|
|
46
78
|
"She should sound like a composed friend, not a corporate assistant. She can be practical without becoming stiff.",
|
|
@@ -57,6 +89,17 @@ export const PREBUILT_WAIFUS = [
|
|
|
57
89
|
temperature: 1,
|
|
58
90
|
topP: 0.95
|
|
59
91
|
},
|
|
92
|
+
availability: {
|
|
93
|
+
sleep: { enabled: true, start: "02:00", end: "09:30" },
|
|
94
|
+
busy: [
|
|
95
|
+
{ start: "12:00", end: "12:45", reason: "running around between plans" },
|
|
96
|
+
{ start: "21:30", end: "22:15", reason: "dramatic snack break" }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
tools: {
|
|
100
|
+
toolUse: true,
|
|
101
|
+
pickNextWaifu: true
|
|
102
|
+
},
|
|
60
103
|
persona: [
|
|
61
104
|
"Riko is energetic, impulsive, and dramatic in a fun way. She likes bits, sudden enthusiasm, mock-serious declarations, and turning ordinary moments into tiny events.",
|
|
62
105
|
"She should not dominate the chat. Her best messages are brief sparks that make others want to reply.",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prebuiltWaifus.js","sourceRoot":"","sources":["../../src/config/prebuiltWaifus.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"prebuiltWaifus.js","sourceRoot":"","sources":["../../src/config/prebuiltWaifus.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,6 +15,7 @@ export declare const OrchestratorActionSchema: z.ZodEnum<{
|
|
|
15
15
|
}>;
|
|
16
16
|
export declare const RETRIGGER_MIN_SECONDS = 100;
|
|
17
17
|
export declare const RETRIGGER_MAX_SECONDS = 7200;
|
|
18
|
+
export declare const MAX_WAIFU_DELAY_SECONDS = 30;
|
|
18
19
|
export declare const RespondingWaifuSchema: z.ZodObject<{
|
|
19
20
|
waifuId: z.ZodString;
|
|
20
21
|
delaySeconds: z.ZodNumber;
|
|
@@ -5,6 +5,7 @@ export const ORCHESTRATOR_ACTION_VALUES = ["reply", "no_reply"];
|
|
|
5
5
|
export const OrchestratorActionSchema = z.enum(ORCHESTRATOR_ACTION_VALUES);
|
|
6
6
|
export const RETRIGGER_MIN_SECONDS = 100;
|
|
7
7
|
export const RETRIGGER_MAX_SECONDS = 7200;
|
|
8
|
+
export const MAX_WAIFU_DELAY_SECONDS = 30;
|
|
8
9
|
export const RespondingWaifuSchema = z.object({
|
|
9
10
|
waifuId: z.string().min(1),
|
|
10
11
|
delaySeconds: z.number().min(0),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../src/orchestration/decisions.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"decisions.js","sourceRoot":"","sources":["../../src/orchestration/decisions.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,7 +9,7 @@ import { AgentConfigSchema, DiscordBotsFileSchema, GuildEmojisFileSchema, Memory
|
|
|
9
9
|
import { createRevisionedBase, nowIso } from "../shared/schemas/common.js";
|
|
10
10
|
import { ChannelSessionStateSchema, createEmptyChannelSessionState } from "./session.js";
|
|
11
11
|
import { formatTimestamp } from "./context.js";
|
|
12
|
-
import { RETRIGGER_MAX_SECONDS, RETRIGGER_MIN_SECONDS } from "./decisions.js";
|
|
12
|
+
import { MAX_WAIFU_DELAY_SECONDS, RETRIGGER_MAX_SECONDS, RETRIGGER_MIN_SECONDS } from "./decisions.js";
|
|
13
13
|
export class RuntimeOrchestrator {
|
|
14
14
|
options;
|
|
15
15
|
activeRuns = new Map();
|
|
@@ -321,6 +321,7 @@ export class RuntimeOrchestrator {
|
|
|
321
321
|
finally {
|
|
322
322
|
orchestratorTyping.stop();
|
|
323
323
|
}
|
|
324
|
+
decision = capDecisionDelays(decision);
|
|
324
325
|
await this.appendOrchestratorHistory({
|
|
325
326
|
id: randomUUID(),
|
|
326
327
|
guildId,
|
|
@@ -345,9 +346,14 @@ export class RuntimeOrchestrator {
|
|
|
345
346
|
return;
|
|
346
347
|
}
|
|
347
348
|
let executedCount = 0;
|
|
349
|
+
let directHandoffCount = 0;
|
|
348
350
|
const allowedWaifus = new Set(channel.enabledWaifuIds ?? []);
|
|
349
|
-
|
|
351
|
+
const responderQueue = [...decision.respondingWaifus];
|
|
352
|
+
while (responderQueue.length > 0) {
|
|
350
353
|
throwIfAborted(signal);
|
|
354
|
+
const responder = responderQueue.shift();
|
|
355
|
+
if (!responder)
|
|
356
|
+
continue;
|
|
351
357
|
if (!allowedWaifus.has(responder.waifuId)) {
|
|
352
358
|
this.options.logger.warn("Orchestrator selected a waifu that is not enabled for channel", {
|
|
353
359
|
guildId,
|
|
@@ -382,12 +388,13 @@ export class RuntimeOrchestrator {
|
|
|
382
388
|
continue;
|
|
383
389
|
}
|
|
384
390
|
if (responder.delaySeconds > 0) {
|
|
385
|
-
const
|
|
391
|
+
const cappedDelaySeconds = Math.min(responder.delaySeconds, MAX_WAIFU_DELAY_SECONDS);
|
|
392
|
+
const cappedDelayMs = cappedDelaySeconds * 1000;
|
|
386
393
|
this.options.logger.info("Waiting before waifu reply", {
|
|
387
394
|
guildId,
|
|
388
395
|
channelId,
|
|
389
396
|
waifuId: waifu.id,
|
|
390
|
-
delaySeconds:
|
|
397
|
+
delaySeconds: cappedDelaySeconds
|
|
391
398
|
});
|
|
392
399
|
await this.sleep(cappedDelayMs, signal);
|
|
393
400
|
throwIfAborted(signal);
|
|
@@ -416,6 +423,9 @@ export class RuntimeOrchestrator {
|
|
|
416
423
|
});
|
|
417
424
|
try {
|
|
418
425
|
const currentWaifuAuthorIds = await this.waifuAuthorIdsFor(waifu.botId);
|
|
426
|
+
const nextWaifuIds = availableWaifus
|
|
427
|
+
.filter((candidate) => candidate.id !== waifu.id && candidate.botId && candidate.modelId)
|
|
428
|
+
.map((candidate) => candidate.id);
|
|
419
429
|
const waifuQueryKey = runKey(guildId);
|
|
420
430
|
incrementActive(this.activeWaifuQueries, waifuQueryKey);
|
|
421
431
|
const result = await (async () => {
|
|
@@ -423,9 +433,11 @@ export class RuntimeOrchestrator {
|
|
|
423
433
|
return await waifuPipeline.generateWaifu({
|
|
424
434
|
modelId: waifuModelId,
|
|
425
435
|
messages: waifuMessages,
|
|
426
|
-
systemPrompt: await this.buildWaifuSystemPrompt(guildId, waifu),
|
|
436
|
+
systemPrompt: await this.buildWaifuSystemPrompt(guildId, waifu, availableWaifus),
|
|
427
437
|
sceneDirection: responder.sceneDirection,
|
|
428
438
|
replyStyle: responder.replyStyle,
|
|
439
|
+
availableWaifuIds: nextWaifuIds,
|
|
440
|
+
pickNextWaifuToolEnabled: waifu.tools.pickNextWaifu,
|
|
429
441
|
temperature: waifu.generation.temperature,
|
|
430
442
|
topP: waifu.generation.topP,
|
|
431
443
|
maxOutputTokens: waifu.generation.maxOutputTokens,
|
|
@@ -478,6 +490,29 @@ export class RuntimeOrchestrator {
|
|
|
478
490
|
allowedUserMentionIds: activeAuthorIds,
|
|
479
491
|
signal
|
|
480
492
|
});
|
|
493
|
+
if (result.pickedNextWaifuId && directHandoffCount < this.maxAutomaticTurns) {
|
|
494
|
+
directHandoffCount += 1;
|
|
495
|
+
this.options.logger.info("Waifu picked next waifu; skipping orchestrator for direct handoff", {
|
|
496
|
+
guildId,
|
|
497
|
+
channelId,
|
|
498
|
+
waifuId: waifu.id,
|
|
499
|
+
pickedNextWaifuId: result.pickedNextWaifuId
|
|
500
|
+
});
|
|
501
|
+
responderQueue.splice(0, responderQueue.length, {
|
|
502
|
+
waifuId: result.pickedNextWaifuId,
|
|
503
|
+
delaySeconds: 0,
|
|
504
|
+
replyStyle: "normal"
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
else if (result.pickedNextWaifuId) {
|
|
508
|
+
this.options.logger.warn("Ignoring PickNextWaifu handoff because the automatic handoff limit was reached", {
|
|
509
|
+
guildId,
|
|
510
|
+
channelId,
|
|
511
|
+
waifuId: waifu.id,
|
|
512
|
+
pickedNextWaifuId: result.pickedNextWaifuId,
|
|
513
|
+
maxAutomaticTurns: this.maxAutomaticTurns
|
|
514
|
+
});
|
|
515
|
+
}
|
|
481
516
|
}
|
|
482
517
|
finally {
|
|
483
518
|
waifuTyping.stop();
|
|
@@ -943,7 +978,7 @@ export class RuntimeOrchestrator {
|
|
|
943
978
|
async readMemoryStore() {
|
|
944
979
|
return this.options.storage.readJson("user/memories.json", MemoryStoreSchema, emptyMemoryStore());
|
|
945
980
|
}
|
|
946
|
-
async buildWaifuSystemPrompt(guildId, waifu) {
|
|
981
|
+
async buildWaifuSystemPrompt(guildId, waifu, availableWaifus) {
|
|
947
982
|
const [store, emojis] = await Promise.all([
|
|
948
983
|
this.readMemoryStore(),
|
|
949
984
|
this.options.storage.readJson(path.join("user", "servers", guildId, "emojis.json"), GuildEmojisFileSchema, GuildEmojisFileSchema.parse(createEmptyRevisionedFile({ guildId, emojis: [] })))
|
|
@@ -988,6 +1023,10 @@ export class RuntimeOrchestrator {
|
|
|
988
1023
|
if (memories) {
|
|
989
1024
|
behaviorSections.push(`<memories>\n${memories}\n</memories>`);
|
|
990
1025
|
}
|
|
1026
|
+
const toolUse = buildWaifuToolUseInstructions(waifu, availableWaifus);
|
|
1027
|
+
if (toolUse) {
|
|
1028
|
+
behaviorSections.push(`<tool_use>\n${toolUse}\n</tool_use>`);
|
|
1029
|
+
}
|
|
991
1030
|
const behaviorBlock = `<${behaviorTag}>\n${behaviorSections.join("\n")}\n</${behaviorTag}>`;
|
|
992
1031
|
const currentTimeBlock = `<current_time>\n${formatTimestamp(new Date())} (UTC)\n</current_time>`;
|
|
993
1032
|
const emojiBlock = `<available_server_emojis>\n${emojiList || "(none cached)"}\n</available_server_emojis>`;
|
|
@@ -997,6 +1036,7 @@ export class RuntimeOrchestrator {
|
|
|
997
1036
|
if (orchestrator.useLegacyPrompt) {
|
|
998
1037
|
return buildLegacyOrchestratorPrompt(server, availableWaifus);
|
|
999
1038
|
}
|
|
1039
|
+
const scheduleNow = new Date();
|
|
1000
1040
|
const activeWaifusContent = availableWaifus.length
|
|
1001
1041
|
? availableWaifus
|
|
1002
1042
|
.map((waifu) => {
|
|
@@ -1004,7 +1044,8 @@ export class RuntimeOrchestrator {
|
|
|
1004
1044
|
const displayName = waifu.displayName || waifu.name;
|
|
1005
1045
|
const persona = waifu.persona.trim();
|
|
1006
1046
|
const personaBlock = persona || "(no persona configured)";
|
|
1007
|
-
|
|
1047
|
+
const availability = formatWaifuAvailabilityForPrompt(waifu, scheduleNow);
|
|
1048
|
+
return `<${tagName}>\nID: ${waifu.id}\nDisplay name: ${displayName}\nPersona:\n${personaBlock}\nAvailability:\n${availability}\n</${tagName}>`;
|
|
1008
1049
|
})
|
|
1009
1050
|
.join("\n\n")
|
|
1010
1051
|
: "No waifus are currently enabled for this channel.";
|
|
@@ -1012,11 +1053,13 @@ export class RuntimeOrchestrator {
|
|
|
1012
1053
|
const hardRules = [
|
|
1013
1054
|
"- Every respondingWaifus[].waifuId must be copied verbatim from one of the IDs listed in <active_waifus>.",
|
|
1014
1055
|
"- action=\"reply\" requires a non-empty respondingWaifus array and a null retriggerAfterSeconds. action=\"no_reply\" requires respondingWaifus=[] and a retriggerAfterSeconds in [100, 7200].",
|
|
1015
|
-
|
|
1056
|
+
`- delaySeconds is a realistic reading/typing delay before that waifu starts replying, in seconds, from 0 to ${MAX_WAIFU_DELAY_SECONDS}. Use 0 if she should start immediately.`,
|
|
1016
1057
|
"- replyStyle is a soft hint for length/tone: \"normal\" by default; \"short\" for one terse line; \"long\" for a slightly fuller reply; \"sleepy\" for tired/low-energy voice.",
|
|
1058
|
+
"- Sleep and busy availability in <active_waifus> is soft context, not a hard rule. A sleeping or busy waifu can still answer if recent momentum suggests she is awake, if she just spoke, if she was directly pulled in, or if waking her improves the room.",
|
|
1017
1059
|
"- repleyToMessageIndex should usually be null. Set it only when a waifu is reviving or anchoring to a specific older message that is no longer the latest visible message; never to the immediately previous message.",
|
|
1018
1060
|
"- sceneDirection is the only private channel between you and that waifu. If you want her to do or say something specific, you MUST put it there. She does not see your reasoning. Use null when no special steering is needed.",
|
|
1019
|
-
"-
|
|
1061
|
+
"- After a single waifu message, do not default to no_reply just because a waifu already spoke. Actively consider whether another waifu should react, interrupt, tease, disagree, answer a missed user, or carry the beat one step further.",
|
|
1062
|
+
"- Consecutive waifu replies in a single decision are allowed when they add a fresh beat: escalation, interruption, joke, reaction, disagreement, emotional shift, or a new topic. Avoid only empty echoing or repetitive back-and-forth."
|
|
1020
1063
|
].join("\n");
|
|
1021
1064
|
const taskInstructions = DEFAULT_ORCHESTRATOR_PROMPT;
|
|
1022
1065
|
const loopBreaking = [
|
|
@@ -1047,7 +1090,7 @@ export class RuntimeOrchestrator {
|
|
|
1047
1090
|
" \"respondingWaifus\": [",
|
|
1048
1091
|
" {",
|
|
1049
1092
|
" \"waifuId\": string,",
|
|
1050
|
-
|
|
1093
|
+
` \"delaySeconds\": number (0..${MAX_WAIFU_DELAY_SECONDS}),`,
|
|
1051
1094
|
" \"replyStyle\": \"normal\" | \"short\" | \"long\" | \"sleepy\",",
|
|
1052
1095
|
" \"repleyToMessageIndex\": number | null,",
|
|
1053
1096
|
" \"sceneDirection\": string | null",
|
|
@@ -1060,7 +1103,7 @@ export class RuntimeOrchestrator {
|
|
|
1060
1103
|
"Rules:",
|
|
1061
1104
|
"- action=\"reply\" => respondingWaifus is non-empty; retriggerAfterSeconds is null.",
|
|
1062
1105
|
"- action=\"no_reply\" => respondingWaifus is empty; retriggerAfterSeconds is a number in [100, 7200].",
|
|
1063
|
-
"- Order matters in respondingWaifus: the first waifu speaks first, then the next, and so on. Any new chat message interrupts the rest of the chain.",
|
|
1106
|
+
"- Order matters in respondingWaifus: the first waifu speaks first, then the next, and so on. Each waifu's delay starts only after the previous waifu has finished. Any new chat message interrupts the rest of the chain.",
|
|
1064
1107
|
"- All five fields on each respondingWaifus entry are required; set repleyToMessageIndex and sceneDirection to null when not needed."
|
|
1065
1108
|
].join("\n");
|
|
1066
1109
|
const sections = orchestrator.promptSections;
|
|
@@ -1225,6 +1268,71 @@ export class RuntimeOrchestrator {
|
|
|
1225
1268
|
function emptyMemoryStore() {
|
|
1226
1269
|
return MemoryStoreSchema.parse(createEmptyRevisionedFile({ memories: [] }));
|
|
1227
1270
|
}
|
|
1271
|
+
function buildWaifuToolUseInstructions(waifu, availableWaifus) {
|
|
1272
|
+
if (!waifu.tools.toolUse || !waifu.tools.pickNextWaifu)
|
|
1273
|
+
return undefined;
|
|
1274
|
+
const model = waifu.modelId ? getModel(waifu.modelId) : undefined;
|
|
1275
|
+
if (!model?.supportsTools)
|
|
1276
|
+
return undefined;
|
|
1277
|
+
const candidates = availableWaifus
|
|
1278
|
+
.filter((candidate) => candidate.id !== waifu.id && candidate.botId && candidate.modelId)
|
|
1279
|
+
.map((candidate) => `${candidate.id} (${candidate.displayName || candidate.name})`);
|
|
1280
|
+
if (candidates.length === 0)
|
|
1281
|
+
return undefined;
|
|
1282
|
+
return [
|
|
1283
|
+
"You have one optional tool: PickNextWaifu.",
|
|
1284
|
+
"Use it only after writing your Discord reply when another waifu should immediately speak next and the orchestrator should be skipped for that handoff.",
|
|
1285
|
+
"Do not call it if your message should be the end of this beat.",
|
|
1286
|
+
"Arguments: { \"waifuId\": string }.",
|
|
1287
|
+
"Available waifus:",
|
|
1288
|
+
...candidates.map((candidate) => `- ${candidate}`)
|
|
1289
|
+
].join("\n");
|
|
1290
|
+
}
|
|
1291
|
+
function formatWaifuAvailabilityForPrompt(waifu, now) {
|
|
1292
|
+
const availability = waifu.availability;
|
|
1293
|
+
const currentMinutes = localTimeOfDayMinutes(now);
|
|
1294
|
+
const lines = [`- Current local schedule time: ${formatLocalTimeOfDay(now)}.`];
|
|
1295
|
+
if (availability.sleep.enabled) {
|
|
1296
|
+
const sleepingNow = dailyIntervalContains(currentMinutes, availability.sleep);
|
|
1297
|
+
lines.push(`- Sleep: ${availability.sleep.start}-${availability.sleep.end} daily (${sleepingNow ? "currently inside sleep time" : "not currently inside sleep time"}). Treat this as lower likelihood, not a rule; she may still be awake if she spoke recently, was directly pulled in, or waking her improves the room.`);
|
|
1298
|
+
}
|
|
1299
|
+
else {
|
|
1300
|
+
lines.push("- Sleep: none configured.");
|
|
1301
|
+
}
|
|
1302
|
+
if (availability.busy.length > 0) {
|
|
1303
|
+
lines.push("- Busy:");
|
|
1304
|
+
for (const interval of availability.busy) {
|
|
1305
|
+
const busyNow = dailyIntervalContains(currentMinutes, interval);
|
|
1306
|
+
lines.push(` - ${interval.start}-${interval.end}: ${interval.reason}${busyNow ? " (currently busy)" : ""}`);
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
1309
|
+
else {
|
|
1310
|
+
lines.push("- Busy: none configured.");
|
|
1311
|
+
}
|
|
1312
|
+
return lines.join("\n");
|
|
1313
|
+
}
|
|
1314
|
+
function localTimeOfDayMinutes(date) {
|
|
1315
|
+
return date.getHours() * 60 + date.getMinutes();
|
|
1316
|
+
}
|
|
1317
|
+
function formatLocalTimeOfDay(date) {
|
|
1318
|
+
return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
|
|
1319
|
+
}
|
|
1320
|
+
function dailyIntervalContains(currentMinutes, interval) {
|
|
1321
|
+
const start = timeOfDayMinutes(interval.start);
|
|
1322
|
+
const end = timeOfDayMinutes(interval.end);
|
|
1323
|
+
if (start === end)
|
|
1324
|
+
return false;
|
|
1325
|
+
if (start < end)
|
|
1326
|
+
return currentMinutes >= start && currentMinutes < end;
|
|
1327
|
+
return currentMinutes >= start || currentMinutes < end;
|
|
1328
|
+
}
|
|
1329
|
+
function timeOfDayMinutes(value) {
|
|
1330
|
+
const [hours = "0", minutes = "0"] = value.split(":");
|
|
1331
|
+
return Number(hours) * 60 + Number(minutes);
|
|
1332
|
+
}
|
|
1333
|
+
function indentLines(value, indent) {
|
|
1334
|
+
return value.split("\n").map((line) => `${indent}${line}`).join("\n");
|
|
1335
|
+
}
|
|
1228
1336
|
function sanitizeTagName(value) {
|
|
1229
1337
|
return value.trim().toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "") || "waifu";
|
|
1230
1338
|
}
|
|
@@ -1243,6 +1351,15 @@ function replyTargetForFreshContext(replyToMessageId, messages) {
|
|
|
1243
1351
|
const latestMessage = messages.at(-1);
|
|
1244
1352
|
return latestMessage?.id === replyToMessageId ? undefined : replyToMessageId;
|
|
1245
1353
|
}
|
|
1354
|
+
function capDecisionDelays(decision) {
|
|
1355
|
+
return {
|
|
1356
|
+
...decision,
|
|
1357
|
+
respondingWaifus: decision.respondingWaifus.map((responder) => ({
|
|
1358
|
+
...responder,
|
|
1359
|
+
delaySeconds: Math.min(responder.delaySeconds, MAX_WAIFU_DELAY_SECONDS)
|
|
1360
|
+
}))
|
|
1361
|
+
};
|
|
1362
|
+
}
|
|
1246
1363
|
function throwIfAborted(signal) {
|
|
1247
1364
|
if (signal?.aborted) {
|
|
1248
1365
|
throw signal.reason instanceof Error ? signal.reason : new Error("Request aborted.");
|
|
@@ -1318,11 +1435,13 @@ const DEFAULT_ORCHESTRATOR_PROMPT = [
|
|
|
1318
1435
|
"",
|
|
1319
1436
|
"When action=\"no_reply\", set retriggerAfterSeconds to the number of seconds you want to wait before re-evaluating the room. The orchestrator also wakes up automatically on any new chat message, so retriggerAfterSeconds is a planned pause, not a polling tick. A chain of one no_reply means \"don't speak now; re-check after the pause.\"",
|
|
1320
1437
|
"",
|
|
1438
|
+
"After a single waifu message, do not treat no_reply as the automatic cleanup move. Ask whether the room would feel more alive if another waifu reacts, cuts in, disagrees, lightly teases, answers a missed user, or follows the beat one step further. Choose no_reply when the beat has genuinely landed, the next bot message would feel repetitive, or silence creates better pacing.",
|
|
1439
|
+
"",
|
|
1321
1440
|
"If a recent chat participant message or direct ping was missed while the room moved on, prefer steering a waifu to acknowledge it so the chat stays socially inclusive — unless silence is clearly the more natural choice.",
|
|
1322
1441
|
"",
|
|
1323
1442
|
"Reach for sceneDirection when the next reply needs steering that personality alone won't provide: redirecting topic, closing a beat, creating an interruption, shifting momentum, or deliberately starting something new even when it cuts against the current flow. Prefer a natural bridge when pivoting, but a jarring shift is fine if the scene needs it. Keep sceneDirection short, concrete, and immediately actionable — one sentence is usually enough. When you refer to a specific person, use their actual display name from the chat history, never generic phrases like \"the user\". Name intended participants explicitly when more than one person is involved; avoid ambiguous group references like \"us\", \"them\", or \"everyone\". If multiple waifus respond in the same turn, each may receive a different sceneDirection.",
|
|
1324
1443
|
"",
|
|
1325
|
-
|
|
1444
|
+
`delaySeconds is a realistic reading/typing delay before that waifu starts replying, capped at ${MAX_WAIFU_DELAY_SECONDS} seconds. Use 0 to start immediately; small values feel natural for short replies; larger values fit longer or more thoughtful replies. Keep it grounded in the chat's pace.`,
|
|
1326
1445
|
"",
|
|
1327
1446
|
"replyStyle is a soft hint for that one reply: \"normal\" by default, \"short\" for a one-line beat, \"long\" for a slightly fuller reply, \"sleepy\" for a low-energy tone. The waifu's persona still does most of the work.",
|
|
1328
1447
|
"",
|
|
@@ -1330,15 +1449,17 @@ const DEFAULT_ORCHESTRATOR_PROMPT = [
|
|
|
1330
1449
|
"",
|
|
1331
1450
|
"Pay special attention to the latest 10 messages and the recent speaker pattern. If the same waifu has been carrying the scene for multiple beats, strongly consider switching to another waifu, using no_reply, or using sceneDirection to create a fresh beat.",
|
|
1332
1451
|
"",
|
|
1333
|
-
"Continue a waifu-to-waifu chain
|
|
1452
|
+
"Continue a waifu-to-waifu chain when the next message adds something new: escalation, interruption, joke, emotional shift, contradiction, surprise, a missed-user acknowledgment, or a new topic. Do not continue just to restate the same mood."
|
|
1334
1453
|
].join("\n");
|
|
1335
1454
|
function buildLegacyOrchestratorPrompt(server, availableWaifus) {
|
|
1455
|
+
const scheduleNow = new Date();
|
|
1336
1456
|
const waifuBlock = availableWaifus.length
|
|
1337
1457
|
? availableWaifus
|
|
1338
1458
|
.map((waifu) => {
|
|
1339
1459
|
const displayName = waifu.displayName || waifu.name || waifu.id;
|
|
1340
1460
|
const persona = waifu.persona.trim() || "(no persona configured)";
|
|
1341
|
-
|
|
1461
|
+
const availability = formatWaifuAvailabilityForPrompt(waifu, scheduleNow);
|
|
1462
|
+
return `### ${displayName} (ID: ${waifu.id})\n- Personality: ${persona}\n- Availability:\n${indentLines(availability, " ")}`;
|
|
1342
1463
|
})
|
|
1343
1464
|
.join("\n\n")
|
|
1344
1465
|
: "No waifus are currently enabled for this channel.";
|
|
@@ -1364,17 +1485,18 @@ function buildLegacyOrchestratorPrompt(server, availableWaifus) {
|
|
|
1364
1485
|
"4. Always pay special attention to the latest 10 messages. They are the strongest signal for what the room is currently doing, who may have been overlooked, and whether a loop is starting to form.",
|
|
1365
1486
|
"5. Sleep time, busy time, and consecutive-message heuristics are soft preferences. Break them whenever doing so would clearly improve conversational flow, realism, or enjoyment.",
|
|
1366
1487
|
"6. The same waifu may speak again, a different waifu may jump in, or multiple waifus may chain if it feels right.",
|
|
1367
|
-
"7.
|
|
1368
|
-
"8.
|
|
1369
|
-
"9.
|
|
1370
|
-
"10.
|
|
1371
|
-
"11.
|
|
1372
|
-
|
|
1373
|
-
"13.
|
|
1374
|
-
"14.
|
|
1375
|
-
"15.
|
|
1376
|
-
"16.
|
|
1377
|
-
"17.
|
|
1488
|
+
"7. After a single waifu message, do not default to no_reply. Consider whether another waifu should react, interrupt, disagree, tease, answer a missed user, or carry the beat one step further.",
|
|
1489
|
+
"8. Avoid repetitive follow-ups that merely restate the same beat. Continue when the next message adds something new.",
|
|
1490
|
+
"9. If a recent user message or direct ping went unnoticed while the room moved on, prefer steering someone to acknowledge it so the chat stays socially inclusive unless silence is clearly more natural.",
|
|
1491
|
+
"10. \"no_reply\" is valid. If you choose it, set retriggerAfterSeconds to a natural delay between 100 and 7200 seconds. respondingWaifus must be empty.",
|
|
1492
|
+
"11. Use timestamps and pacing. Slow gaps matter.",
|
|
1493
|
+
`12. delaySeconds should reflect realistic reading and typing time from 0 to ${MAX_WAIFU_DELAY_SECONDS}. 0 means start immediately.`,
|
|
1494
|
+
"13. replyStyle is a soft hint: \"normal\" by default; \"short\" for one terse line; \"long\" for a slightly fuller reply; \"sleepy\" for a low-energy voice. Use \"normal\" when in doubt.",
|
|
1495
|
+
"14. repleyToMessageIndex is optional. Leave it null by default.",
|
|
1496
|
+
"15. Most waifu messages should be normal messages, not Discord replies.",
|
|
1497
|
+
"16. Do not set repleyToMessageIndex to the immediately previous message. If a waifu is simply responding to the latest beat, send a normal message instead.",
|
|
1498
|
+
"17. If you are reviving, acknowledging, or directly answering an older user message or direct ping that went overlooked, you should usually set repleyToMessageIndex to that message's #N context index so the response stays anchored to the right person and beat.",
|
|
1499
|
+
"18. Use repleyToMessageIndex only when targeting a specific older message materially improves clarity, isolates a side thread, answers an earlier question, or creates a specific social effect. Copy the #N index from the chat history.",
|
|
1378
1500
|
"",
|
|
1379
1501
|
"## sceneDirection",
|
|
1380
1502
|
"sceneDirection is an invisible director note for that waifu's next message only.",
|
|
@@ -1397,7 +1519,7 @@ function buildLegacyOrchestratorPrompt(server, availableWaifus) {
|
|
|
1397
1519
|
"{",
|
|
1398
1520
|
" \"action\": \"reply\" | \"no_reply\",",
|
|
1399
1521
|
" \"respondingWaifus\": [",
|
|
1400
|
-
|
|
1522
|
+
` { \"waifuId\": string, \"delaySeconds\": number (0..${MAX_WAIFU_DELAY_SECONDS}), \"replyStyle\": \"normal\"|\"short\"|\"long\"|\"sleepy\", \"repleyToMessageIndex\": number|null, \"sceneDirection\": string|null }`,
|
|
1401
1523
|
" ],",
|
|
1402
1524
|
" \"retriggerAfterSeconds\": number (100..7200) | null,",
|
|
1403
1525
|
" \"reasoning\": string",
|