@friendlyrobot/discord-pi-agent 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -2
- package/dist/agent-model-service.d.ts +20 -0
- package/dist/agent-resource-service.d.ts +8 -0
- package/dist/agent-service.d.ts +5 -19
- package/dist/index.js +287 -296
- package/dist/reply-buffer.d.ts +0 -1
- package/dist/types.d.ts +0 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,7 +132,6 @@ Pretty console logs use:
|
|
|
132
132
|
|
|
133
133
|
- `discordAllowedForumChannelIds` — string array of forum channel IDs to respond in
|
|
134
134
|
- `discordAllowedUserIds` — string array of allowed user IDs (defaults to `[discordAllowedUserId]`)
|
|
135
|
-
- `sessionIdleTimeoutMs` — auto-shutdown idle thread sessions (null = never)
|
|
136
135
|
|
|
137
136
|
## Env helpers
|
|
138
137
|
|
|
@@ -149,7 +148,6 @@ Pretty console logs use:
|
|
|
149
148
|
- `DISCORD_STARTUP_MESSAGE`
|
|
150
149
|
- `DISCORD_FORUM_CHANNEL_IDS` — comma-separated forum channel IDs
|
|
151
150
|
- `DISCORD_ALLOWED_USER_IDS` — comma-separated allowed user IDs
|
|
152
|
-
- `DISCORD_SESSION_IDLE_TIMEOUT_MS` — idle timeout in ms
|
|
153
151
|
|
|
154
152
|
If `PI_AGENT_CWD` is missing it falls back to `process.cwd()`.
|
|
155
153
|
Set `DISCORD_STARTUP_MESSAGE=false` to disable the startup DM.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AgentSession, ModelRegistry as ModelRegistryType } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
3
|
+
import type { ResolvedDiscordGatewayConfig, ThinkingLevel } from "./types";
|
|
4
|
+
export declare class AgentModelService {
|
|
5
|
+
private readonly config;
|
|
6
|
+
private readonly modelRegistry;
|
|
7
|
+
constructor(config: ResolvedDiscordGatewayConfig, modelRegistry: ModelRegistryType);
|
|
8
|
+
findModel(provider: string, modelId: string): Model<any> | undefined;
|
|
9
|
+
ensureSessionHasConfiguredModel(session: AgentSession): Promise<void>;
|
|
10
|
+
listModels(session?: AgentSession | null): Promise<string>;
|
|
11
|
+
switchModel(provider: string, modelId: string, session: AgentSession): Promise<string>;
|
|
12
|
+
getCurrentModelDisplay(session?: AgentSession | null): string;
|
|
13
|
+
getThinkingLevel(session: AgentSession): {
|
|
14
|
+
current: ThinkingLevel;
|
|
15
|
+
available: ThinkingLevel[];
|
|
16
|
+
supported: boolean;
|
|
17
|
+
};
|
|
18
|
+
setThinkingLevel(session: AgentSession, level: ThinkingLevel): string;
|
|
19
|
+
private applyConfiguredThinkingLevelForSession;
|
|
20
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DefaultResourceLoader } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
export declare class AgentResourceService {
|
|
3
|
+
private readonly resourceLoader;
|
|
4
|
+
constructor(resourceLoader: DefaultResourceLoader);
|
|
5
|
+
getSkillsSummary(): string;
|
|
6
|
+
getExtensionsSummary(): string;
|
|
7
|
+
reloadResources(): Promise<string>;
|
|
8
|
+
}
|
package/dist/agent-service.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import { AgentModelService } from "./agent-model-service";
|
|
3
|
+
import { AgentResourceService } from "./agent-resource-service";
|
|
4
|
+
import type { AgentStatus, ResolvedDiscordGatewayConfig } from "./types";
|
|
4
5
|
export declare class AgentService {
|
|
5
6
|
private readonly config;
|
|
6
7
|
private readonly authStorage;
|
|
@@ -8,6 +9,8 @@ export declare class AgentService {
|
|
|
8
9
|
private readonly settingsManager;
|
|
9
10
|
private readonly resourceLoader;
|
|
10
11
|
private session;
|
|
12
|
+
readonly models: AgentModelService;
|
|
13
|
+
readonly resources: AgentResourceService;
|
|
11
14
|
constructor(config: ResolvedDiscordGatewayConfig);
|
|
12
15
|
initialize(): Promise<void>;
|
|
13
16
|
getSession(): AgentSession | null;
|
|
@@ -18,31 +21,14 @@ export declare class AgentService {
|
|
|
18
21
|
* setModel() before prompting and dispose() when done.
|
|
19
22
|
*/
|
|
20
23
|
createTemporarySession(): Promise<AgentSession>;
|
|
21
|
-
/** Find a model by provider and ID. Returns undefined if not found. */
|
|
22
|
-
findModel(provider: string, modelId: string): Model<any> | undefined;
|
|
23
24
|
createSession(sessionDir: string): Promise<AgentSession>;
|
|
24
25
|
prompt(text: string): Promise<string>;
|
|
25
|
-
getSkillsSummary(): string;
|
|
26
|
-
reloadResources(): Promise<string>;
|
|
27
|
-
getExtensionsSummary(): string;
|
|
28
26
|
compact(): Promise<string>;
|
|
29
27
|
resetSession(): Promise<string>;
|
|
30
28
|
getStatus(): AgentStatus;
|
|
31
29
|
shutdown(): Promise<void>;
|
|
32
30
|
private createOrResumeSession;
|
|
33
31
|
private ensureConfiguredModel;
|
|
34
|
-
private ensureModelForSession;
|
|
35
32
|
private requireSession;
|
|
36
|
-
private applyConfiguredThinkingLevel;
|
|
37
|
-
private applyConfiguredThinkingLevelForSession;
|
|
38
|
-
listModels(session?: AgentSession | null): Promise<string>;
|
|
39
|
-
switchModel(provider: string, modelId: string, session?: AgentSession | null): Promise<string>;
|
|
40
|
-
getCurrentModelDisplay(session?: AgentSession | null): string;
|
|
41
|
-
getThinkingLevel(): {
|
|
42
|
-
current: ThinkingLevel;
|
|
43
|
-
available: ThinkingLevel[];
|
|
44
|
-
supported: boolean;
|
|
45
|
-
};
|
|
46
|
-
setThinkingLevel(level: ThinkingLevel): string;
|
|
47
33
|
private getSessionDir;
|
|
48
34
|
}
|
package/dist/index.js
CHANGED
|
@@ -39,6 +39,200 @@ function createModuleLogger(moduleName) {
|
|
|
39
39
|
return logger.child({ module: moduleName });
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
// src/agent-model-service.ts
|
|
43
|
+
var logger2 = createModuleLogger("agent-model-service");
|
|
44
|
+
|
|
45
|
+
class AgentModelService {
|
|
46
|
+
config;
|
|
47
|
+
modelRegistry;
|
|
48
|
+
constructor(config, modelRegistry) {
|
|
49
|
+
this.config = config;
|
|
50
|
+
this.modelRegistry = modelRegistry;
|
|
51
|
+
}
|
|
52
|
+
findModel(provider, modelId) {
|
|
53
|
+
return this.modelRegistry.find(provider, modelId);
|
|
54
|
+
}
|
|
55
|
+
async ensureSessionHasConfiguredModel(session) {
|
|
56
|
+
if (session.model) {
|
|
57
|
+
logger2.debug({
|
|
58
|
+
model: `${session.model.provider}/${session.model.id}`
|
|
59
|
+
}, "retaining existing session model");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const desiredModel = this.modelRegistry.find(this.config.modelProvider, this.config.modelId);
|
|
63
|
+
const availableModels = await this.modelRegistry.getAvailable();
|
|
64
|
+
logger2.debug({
|
|
65
|
+
count: availableModels.length,
|
|
66
|
+
matches: availableModels.filter((model) => {
|
|
67
|
+
return model.provider === this.config.modelProvider;
|
|
68
|
+
}).map((model) => `${model.provider}/${model.id}`)
|
|
69
|
+
}, "available models");
|
|
70
|
+
if (!desiredModel) {
|
|
71
|
+
throw new Error(`Configured model not found: ${this.config.modelProvider}/${this.config.modelId}. Check your pi agent config and installed extensions.`);
|
|
72
|
+
}
|
|
73
|
+
logger2.info({
|
|
74
|
+
to: `${desiredModel.provider}/${desiredModel.id}`
|
|
75
|
+
}, "setting initial session model");
|
|
76
|
+
await session.setModel(desiredModel);
|
|
77
|
+
await this.applyConfiguredThinkingLevelForSession(session);
|
|
78
|
+
}
|
|
79
|
+
async listModels(session) {
|
|
80
|
+
const availableModels = await this.modelRegistry.getAvailable();
|
|
81
|
+
const currentDisplay = session?.model ? `${session.model.provider}/${session.model.id}` : null;
|
|
82
|
+
const lines = availableModels.map((model) => {
|
|
83
|
+
const display = `${model.provider}/${model.id}`;
|
|
84
|
+
const marker = currentDisplay === display ? " (current)" : "";
|
|
85
|
+
return ` ${display}${marker}`;
|
|
86
|
+
});
|
|
87
|
+
return [
|
|
88
|
+
`Available models (${availableModels.length}):`,
|
|
89
|
+
...lines,
|
|
90
|
+
`
|
|
91
|
+
Usage: !model <provider/modelId> to switch.`
|
|
92
|
+
].join(`
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
95
|
+
async switchModel(provider, modelId, session) {
|
|
96
|
+
const model = this.modelRegistry.find(provider, modelId);
|
|
97
|
+
if (!model) {
|
|
98
|
+
const availableModels = await this.modelRegistry.getAvailable();
|
|
99
|
+
const matches = availableModels.filter((availableModel) => {
|
|
100
|
+
return availableModel.provider === provider;
|
|
101
|
+
}).map((availableModel) => {
|
|
102
|
+
return `${availableModel.provider}/${availableModel.id}`;
|
|
103
|
+
});
|
|
104
|
+
const hint = matches.length > 0 ? `
|
|
105
|
+
Models from "${provider}": ${matches.join(", ")}` : `
|
|
106
|
+
Use !model to see all available models.`;
|
|
107
|
+
return `Model not found: ${provider}/${modelId}.${hint}`;
|
|
108
|
+
}
|
|
109
|
+
if (isSameModel(session.model, model)) {
|
|
110
|
+
return `Already using ${provider}/${modelId}.`;
|
|
111
|
+
}
|
|
112
|
+
await session.setModel(model);
|
|
113
|
+
await this.applyConfiguredThinkingLevelForSession(session);
|
|
114
|
+
const thinkingInfo = session.supportsThinking() ? ` (thinking: ${session.thinkingLevel})` : "";
|
|
115
|
+
return `Switched to ${provider}/${modelId}${thinkingInfo}.`;
|
|
116
|
+
}
|
|
117
|
+
getCurrentModelDisplay(session) {
|
|
118
|
+
if (!session?.model) {
|
|
119
|
+
return "(no model selected)";
|
|
120
|
+
}
|
|
121
|
+
return `${session.model.provider}/${session.model.id}`;
|
|
122
|
+
}
|
|
123
|
+
getThinkingLevel(session) {
|
|
124
|
+
if (!session.supportsThinking()) {
|
|
125
|
+
return { current: "off", available: [], supported: false };
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
current: session.thinkingLevel,
|
|
129
|
+
available: session.getAvailableThinkingLevels(),
|
|
130
|
+
supported: true
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
setThinkingLevel(session, level) {
|
|
134
|
+
if (!session.supportsThinking()) {
|
|
135
|
+
return "Current model does not support reasoning/thinking.";
|
|
136
|
+
}
|
|
137
|
+
const available = session.getAvailableThinkingLevels();
|
|
138
|
+
if (!available.includes(level)) {
|
|
139
|
+
return `Invalid thinking level "${level}" for current model. Available: ${available.join(", ")}`;
|
|
140
|
+
}
|
|
141
|
+
session.setThinkingLevel(level);
|
|
142
|
+
return `Thinking level set to "${level}".`;
|
|
143
|
+
}
|
|
144
|
+
async applyConfiguredThinkingLevelForSession(session) {
|
|
145
|
+
if (session.supportsThinking()) {
|
|
146
|
+
const available = session.getAvailableThinkingLevels();
|
|
147
|
+
if (available.includes(this.config.thinkingLevel)) {
|
|
148
|
+
session.setThinkingLevel(this.config.thinkingLevel);
|
|
149
|
+
logger2.debug({
|
|
150
|
+
level: this.config.thinkingLevel
|
|
151
|
+
}, "thinking level applied");
|
|
152
|
+
} else {
|
|
153
|
+
logger2.debug({
|
|
154
|
+
requested: this.config.thinkingLevel,
|
|
155
|
+
available
|
|
156
|
+
}, "thinking level not available for model");
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function isSameModel(currentModel, desiredModel) {
|
|
162
|
+
if (!currentModel) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
return currentModel.provider === desiredModel.provider && currentModel.id === desiredModel.id;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// src/agent-resource-service.ts
|
|
169
|
+
class AgentResourceService {
|
|
170
|
+
resourceLoader;
|
|
171
|
+
constructor(resourceLoader) {
|
|
172
|
+
this.resourceLoader = resourceLoader;
|
|
173
|
+
}
|
|
174
|
+
getSkillsSummary() {
|
|
175
|
+
const result = this.resourceLoader.getSkills();
|
|
176
|
+
const { skills } = result;
|
|
177
|
+
if (skills.length === 0) {
|
|
178
|
+
return "Skills: (none loaded)";
|
|
179
|
+
}
|
|
180
|
+
const names = skills.map((skill) => {
|
|
181
|
+
return skill.name;
|
|
182
|
+
});
|
|
183
|
+
return `Skills (${skills.length}): ${names.join(", ") || "(none)"}`;
|
|
184
|
+
}
|
|
185
|
+
getExtensionsSummary() {
|
|
186
|
+
const result = this.resourceLoader.getExtensions();
|
|
187
|
+
const { extensions, errors } = result;
|
|
188
|
+
if (extensions.length === 0) {
|
|
189
|
+
return "Extensions: (none loaded)";
|
|
190
|
+
}
|
|
191
|
+
const lines = extensions.map((extension) => {
|
|
192
|
+
const toolCount = extension.tools.size;
|
|
193
|
+
const commandCount = extension.commands.size;
|
|
194
|
+
const parts = [];
|
|
195
|
+
if (toolCount > 0) {
|
|
196
|
+
parts.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""}`);
|
|
197
|
+
}
|
|
198
|
+
if (commandCount > 0) {
|
|
199
|
+
parts.push(`${commandCount} command${commandCount !== 1 ? "s" : ""}`);
|
|
200
|
+
}
|
|
201
|
+
const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
202
|
+
return ` ${extension.path}${summary}`;
|
|
203
|
+
});
|
|
204
|
+
const header = `Extensions (${extensions.length}):`;
|
|
205
|
+
const errorLines = errors.length > 0 ? [
|
|
206
|
+
`Errors (${errors.length}):`,
|
|
207
|
+
...errors.map((error) => {
|
|
208
|
+
return ` ${error.path}: ${error.error}`;
|
|
209
|
+
})
|
|
210
|
+
] : [];
|
|
211
|
+
return [header, ...lines, ...errorLines].join(`
|
|
212
|
+
`);
|
|
213
|
+
}
|
|
214
|
+
async reloadResources() {
|
|
215
|
+
await this.resourceLoader.reload();
|
|
216
|
+
const extensions = this.resourceLoader.getExtensions().extensions.map((extension) => {
|
|
217
|
+
return extension.path;
|
|
218
|
+
});
|
|
219
|
+
const skills = this.resourceLoader.getSkills();
|
|
220
|
+
const skillNames = skills.skills.map((skill) => {
|
|
221
|
+
return skill.name;
|
|
222
|
+
});
|
|
223
|
+
const agentsFiles = this.resourceLoader.getAgentsFiles().agentsFiles.map((file) => {
|
|
224
|
+
return file.path;
|
|
225
|
+
});
|
|
226
|
+
return [
|
|
227
|
+
"Resources reloaded.",
|
|
228
|
+
`Extensions (${extensions.length}): ${extensions.join(", ") || "(none)"}`,
|
|
229
|
+
`Skills (${skills.skills.length}): ${skillNames.join(", ") || "(none)"}`,
|
|
230
|
+
`AGENTS.md files (${agentsFiles.length}): ${agentsFiles.join(", ") || "(none)"}`
|
|
231
|
+
].join(`
|
|
232
|
+
`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
42
236
|
// src/debug-print.ts
|
|
43
237
|
function debugPrint(body, title) {
|
|
44
238
|
const WIDTH = 80;
|
|
@@ -57,7 +251,7 @@ function debugPrint(body, title) {
|
|
|
57
251
|
|
|
58
252
|
// src/markdown-table-transformer.ts
|
|
59
253
|
import { Lexer } from "marked";
|
|
60
|
-
var
|
|
254
|
+
var logger3 = createModuleLogger("markdown-table-transformer");
|
|
61
255
|
var CODE_BLOCK_WRAPPER = "```\n{TABLE}\n```";
|
|
62
256
|
async function transformMarkdownTablesToCodeBlocks(text) {
|
|
63
257
|
const normalized = normalizeCodeFences(text);
|
|
@@ -101,7 +295,7 @@ async function formatWithPrettier(text) {
|
|
|
101
295
|
});
|
|
102
296
|
return formatted.trim();
|
|
103
297
|
} catch (error) {
|
|
104
|
-
|
|
298
|
+
logger3.error({
|
|
105
299
|
error
|
|
106
300
|
}, "Prettier formatting failed");
|
|
107
301
|
return text;
|
|
@@ -109,7 +303,7 @@ async function formatWithPrettier(text) {
|
|
|
109
303
|
}
|
|
110
304
|
|
|
111
305
|
// src/reply-buffer.ts
|
|
112
|
-
var
|
|
306
|
+
var logger4 = createModuleLogger("reply-buffer");
|
|
113
307
|
async function runPromptAndCollectReply(session, prompt, options = {}) {
|
|
114
308
|
let streamedText = "";
|
|
115
309
|
let eventCount = 0;
|
|
@@ -126,20 +320,20 @@ async function runPromptAndCollectReply(session, prompt, options = {}) {
|
|
|
126
320
|
}
|
|
127
321
|
if (event.type === "tool_execution_start") {
|
|
128
322
|
toolCount += 1;
|
|
129
|
-
|
|
323
|
+
logger4.debug({
|
|
130
324
|
toolName: event.toolName,
|
|
131
325
|
input: event.toolName === "bash" ? event.args.command : event.args
|
|
132
326
|
}, `tool start: [${event.toolName}] `);
|
|
133
327
|
}
|
|
134
328
|
if (event.type === "tool_execution_end") {
|
|
135
|
-
|
|
329
|
+
logger4.debug({
|
|
136
330
|
toolName: event.toolName,
|
|
137
331
|
isError: event.isError,
|
|
138
332
|
output: truncateForLog(extractToolOutput(event.result))
|
|
139
333
|
}, `tool end: [${event.toolName}]`);
|
|
140
334
|
}
|
|
141
335
|
if (event.type === "agent_end") {
|
|
142
|
-
|
|
336
|
+
logger4.debug("agent end");
|
|
143
337
|
}
|
|
144
338
|
});
|
|
145
339
|
try {
|
|
@@ -193,7 +387,7 @@ function getLatestAssistantText(messages) {
|
|
|
193
387
|
}
|
|
194
388
|
|
|
195
389
|
// src/agent-service.ts
|
|
196
|
-
var
|
|
390
|
+
var logger5 = createModuleLogger("agent-service");
|
|
197
391
|
|
|
198
392
|
class AgentService {
|
|
199
393
|
config;
|
|
@@ -202,6 +396,8 @@ class AgentService {
|
|
|
202
396
|
settingsManager;
|
|
203
397
|
resourceLoader;
|
|
204
398
|
session = null;
|
|
399
|
+
models;
|
|
400
|
+
resources;
|
|
205
401
|
constructor(config) {
|
|
206
402
|
this.config = config;
|
|
207
403
|
this.authStorage = AuthStorage.create(path.join(config.agentDir, "auth.json"));
|
|
@@ -212,11 +408,13 @@ class AgentService {
|
|
|
212
408
|
agentDir: config.agentDir,
|
|
213
409
|
settingsManager: this.settingsManager
|
|
214
410
|
});
|
|
411
|
+
this.models = new AgentModelService(config, this.modelRegistry);
|
|
412
|
+
this.resources = new AgentResourceService(this.resourceLoader);
|
|
215
413
|
}
|
|
216
414
|
async initialize() {
|
|
217
415
|
await fs.mkdir(this.config.agentDir, { recursive: true });
|
|
218
416
|
await fs.mkdir(this.getSessionDir(), { recursive: true });
|
|
219
|
-
|
|
417
|
+
logger5.info({
|
|
220
418
|
cwd: this.config.cwd,
|
|
221
419
|
agentDir: this.config.agentDir,
|
|
222
420
|
sessionDir: this.getSessionDir(),
|
|
@@ -225,7 +423,7 @@ class AgentService {
|
|
|
225
423
|
thinkingLevel: this.config.thinkingLevel
|
|
226
424
|
}, "config");
|
|
227
425
|
await this.resourceLoader.reload();
|
|
228
|
-
|
|
426
|
+
logger5.info({
|
|
229
427
|
extensions: this.resourceLoader.getExtensions().extensions.map((extension) => extension.path),
|
|
230
428
|
agentsFiles: this.resourceLoader.getAgentsFiles().agentsFiles.map((file) => file.path)
|
|
231
429
|
}, "resources loaded");
|
|
@@ -249,12 +447,9 @@ class AgentService {
|
|
|
249
447
|
sessionManager: SessionManager.inMemory(),
|
|
250
448
|
thinkingLevel: "off"
|
|
251
449
|
});
|
|
252
|
-
|
|
450
|
+
logger5.debug({ sessionId: session.sessionId }, "temporary session created");
|
|
253
451
|
return session;
|
|
254
452
|
}
|
|
255
|
-
findModel(provider, modelId) {
|
|
256
|
-
return this.modelRegistry.find(provider, modelId);
|
|
257
|
-
}
|
|
258
453
|
async createSession(sessionDir) {
|
|
259
454
|
await fs.mkdir(sessionDir, { recursive: true });
|
|
260
455
|
const { session } = await createAgentSession({
|
|
@@ -267,70 +462,18 @@ class AgentService {
|
|
|
267
462
|
sessionManager: SessionManager.continueRecent(this.config.cwd, sessionDir),
|
|
268
463
|
thinkingLevel: this.config.thinkingLevel
|
|
269
464
|
});
|
|
270
|
-
|
|
465
|
+
logger5.debug({
|
|
271
466
|
sessionDir,
|
|
272
467
|
sessionId: session.sessionId,
|
|
273
468
|
sessionFile: session.sessionFile
|
|
274
469
|
}, "scoped session created");
|
|
275
|
-
await this.
|
|
470
|
+
await this.models.ensureSessionHasConfiguredModel(session);
|
|
276
471
|
return session;
|
|
277
472
|
}
|
|
278
473
|
async prompt(text) {
|
|
279
474
|
const session = this.requireSession();
|
|
280
475
|
const transformedPrompt = await this.config.promptTransform(text);
|
|
281
|
-
return runPromptAndCollectReply(session, transformedPrompt
|
|
282
|
-
logPrefix: `[agent:${session.sessionId}]`
|
|
283
|
-
});
|
|
284
|
-
}
|
|
285
|
-
getSkillsSummary() {
|
|
286
|
-
const result = this.resourceLoader.getSkills();
|
|
287
|
-
const { skills } = result;
|
|
288
|
-
if (skills.length === 0) {
|
|
289
|
-
return "Skills: (none loaded)";
|
|
290
|
-
}
|
|
291
|
-
const names = skills.map((s) => s.name);
|
|
292
|
-
return `Skills (${skills.length}): ${names.join(", ") || "(none)"}`;
|
|
293
|
-
}
|
|
294
|
-
async reloadResources() {
|
|
295
|
-
await this.resourceLoader.reload();
|
|
296
|
-
const extensions = this.resourceLoader.getExtensions().extensions.map((ext) => ext.path);
|
|
297
|
-
const skills = this.resourceLoader.getSkills();
|
|
298
|
-
const skillNames = skills.skills.map((s) => s.name);
|
|
299
|
-
const agentsFiles = this.resourceLoader.getAgentsFiles().agentsFiles.map((f) => f.path);
|
|
300
|
-
return [
|
|
301
|
-
"Resources reloaded.",
|
|
302
|
-
`Extensions (${extensions.length}): ${extensions.join(", ") || "(none)"}`,
|
|
303
|
-
`Skills (${skills.skills.length}): ${skillNames.join(", ") || "(none)"}`,
|
|
304
|
-
`AGENTS.md files (${agentsFiles.length}): ${agentsFiles.join(", ") || "(none)"}`
|
|
305
|
-
].join(`
|
|
306
|
-
`);
|
|
307
|
-
}
|
|
308
|
-
getExtensionsSummary() {
|
|
309
|
-
const result = this.resourceLoader.getExtensions();
|
|
310
|
-
const { extensions, errors } = result;
|
|
311
|
-
if (extensions.length === 0) {
|
|
312
|
-
return "Extensions: (none loaded)";
|
|
313
|
-
}
|
|
314
|
-
const lines = extensions.map((ext) => {
|
|
315
|
-
const toolCount = ext.tools.size;
|
|
316
|
-
const commandCount = ext.commands.size;
|
|
317
|
-
const parts = [];
|
|
318
|
-
if (toolCount > 0) {
|
|
319
|
-
parts.push(`${toolCount} tool${toolCount !== 1 ? "s" : ""}`);
|
|
320
|
-
}
|
|
321
|
-
if (commandCount > 0) {
|
|
322
|
-
parts.push(`${commandCount} command${commandCount !== 1 ? "s" : ""}`);
|
|
323
|
-
}
|
|
324
|
-
const summary = parts.length > 0 ? ` (${parts.join(", ")})` : "";
|
|
325
|
-
return ` ${ext.path}${summary}`;
|
|
326
|
-
});
|
|
327
|
-
const header = `Extensions (${extensions.length}):`;
|
|
328
|
-
const errorLines = errors.length > 0 ? [
|
|
329
|
-
`Errors (${errors.length}):`,
|
|
330
|
-
...errors.map((e) => ` ${e.path}: ${e.error}`)
|
|
331
|
-
] : [];
|
|
332
|
-
return [header, ...lines, ...errorLines].join(`
|
|
333
|
-
`);
|
|
476
|
+
return runPromptAndCollectReply(session, transformedPrompt);
|
|
334
477
|
}
|
|
335
478
|
async compact() {
|
|
336
479
|
const session = this.requireSession();
|
|
@@ -358,7 +501,7 @@ class AgentService {
|
|
|
358
501
|
}
|
|
359
502
|
getStatus() {
|
|
360
503
|
const session = this.requireSession();
|
|
361
|
-
const model =
|
|
504
|
+
const model = this.models.getCurrentModelDisplay(session);
|
|
362
505
|
const contextUsage = session.getContextUsage();
|
|
363
506
|
const thinkingInfo = session.supportsThinking() ? `thinking: ${session.thinkingLevel} (available: ${session.getAvailableThinkingLevels().join(", ")})` : "thinking: not supported";
|
|
364
507
|
return {
|
|
@@ -390,38 +533,14 @@ class AgentService {
|
|
|
390
533
|
thinkingLevel: this.config.thinkingLevel
|
|
391
534
|
});
|
|
392
535
|
this.session = session;
|
|
393
|
-
|
|
536
|
+
logger5.info({
|
|
394
537
|
sessionId: session.sessionId,
|
|
395
538
|
sessionFile: session.sessionFile,
|
|
396
539
|
restoredModel: session.model ? `${session.model.provider}/${session.model.id}` : null
|
|
397
540
|
}, "session ready");
|
|
398
541
|
}
|
|
399
542
|
async ensureConfiguredModel() {
|
|
400
|
-
await this.
|
|
401
|
-
}
|
|
402
|
-
async ensureModelForSession(session) {
|
|
403
|
-
if (session.model) {
|
|
404
|
-
logger4.debug({
|
|
405
|
-
model: `${session.model.provider}/${session.model.id}`
|
|
406
|
-
}, "retaining existing session model");
|
|
407
|
-
return;
|
|
408
|
-
}
|
|
409
|
-
const desiredModel = this.modelRegistry.find(this.config.modelProvider, this.config.modelId);
|
|
410
|
-
const availableModels = await this.modelRegistry.getAvailable();
|
|
411
|
-
logger4.debug({
|
|
412
|
-
count: availableModels.length,
|
|
413
|
-
matches: availableModels.filter((model) => {
|
|
414
|
-
return model.provider === this.config.modelProvider;
|
|
415
|
-
}).map((model) => `${model.provider}/${model.id}`)
|
|
416
|
-
}, "available models");
|
|
417
|
-
if (!desiredModel) {
|
|
418
|
-
throw new Error(`Configured model not found: ${this.config.modelProvider}/${this.config.modelId}. Check your pi agent config and installed extensions.`);
|
|
419
|
-
}
|
|
420
|
-
logger4.info({
|
|
421
|
-
to: `${desiredModel.provider}/${desiredModel.id}`
|
|
422
|
-
}, "setting initial session model");
|
|
423
|
-
await session.setModel(desiredModel);
|
|
424
|
-
await this.applyConfiguredThinkingLevelForSession(session);
|
|
543
|
+
await this.models.ensureSessionHasConfiguredModel(this.requireSession());
|
|
425
544
|
}
|
|
426
545
|
requireSession() {
|
|
427
546
|
if (!this.session) {
|
|
@@ -429,103 +548,10 @@ class AgentService {
|
|
|
429
548
|
}
|
|
430
549
|
return this.session;
|
|
431
550
|
}
|
|
432
|
-
async applyConfiguredThinkingLevel() {
|
|
433
|
-
await this.applyConfiguredThinkingLevelForSession(this.requireSession());
|
|
434
|
-
}
|
|
435
|
-
async applyConfiguredThinkingLevelForSession(session) {
|
|
436
|
-
if (session.supportsThinking()) {
|
|
437
|
-
const available = session.getAvailableThinkingLevels();
|
|
438
|
-
if (available.includes(this.config.thinkingLevel)) {
|
|
439
|
-
session.setThinkingLevel(this.config.thinkingLevel);
|
|
440
|
-
logger4.debug({
|
|
441
|
-
level: this.config.thinkingLevel
|
|
442
|
-
}, "thinking level applied");
|
|
443
|
-
} else {
|
|
444
|
-
logger4.debug({
|
|
445
|
-
requested: this.config.thinkingLevel,
|
|
446
|
-
available
|
|
447
|
-
}, "thinking level not available for model");
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
}
|
|
451
|
-
async listModels(session) {
|
|
452
|
-
const effectiveSession = session ?? this.session;
|
|
453
|
-
const availableModels = await this.modelRegistry.getAvailable();
|
|
454
|
-
const currentDisplay = effectiveSession?.model ? `${effectiveSession.model.provider}/${effectiveSession.model.id}` : null;
|
|
455
|
-
const lines = availableModels.map((model) => {
|
|
456
|
-
const display = `${model.provider}/${model.id}`;
|
|
457
|
-
const marker = currentDisplay === display ? " (current)" : "";
|
|
458
|
-
return ` ${display}${marker}`;
|
|
459
|
-
});
|
|
460
|
-
return [
|
|
461
|
-
`Available models (${availableModels.length}):`,
|
|
462
|
-
...lines,
|
|
463
|
-
`
|
|
464
|
-
Usage: !model <provider/modelId> to switch.`
|
|
465
|
-
].join(`
|
|
466
|
-
`);
|
|
467
|
-
}
|
|
468
|
-
async switchModel(provider, modelId, session) {
|
|
469
|
-
const effectiveSession = session ?? this.requireSession();
|
|
470
|
-
const model = this.modelRegistry.find(provider, modelId);
|
|
471
|
-
if (!model) {
|
|
472
|
-
const availableModels = await this.modelRegistry.getAvailable();
|
|
473
|
-
const matches = availableModels.filter((m) => {
|
|
474
|
-
return m.provider === provider;
|
|
475
|
-
}).map((m) => `${m.provider}/${m.id}`);
|
|
476
|
-
const hint = matches.length > 0 ? `
|
|
477
|
-
Models from "${provider}": ${matches.join(", ")}` : `
|
|
478
|
-
Use !model to see all available models.`;
|
|
479
|
-
return `Model not found: ${provider}/${modelId}.${hint}`;
|
|
480
|
-
}
|
|
481
|
-
if (isSameModel(effectiveSession.model, model)) {
|
|
482
|
-
return `Already using ${provider}/${modelId}.`;
|
|
483
|
-
}
|
|
484
|
-
await effectiveSession.setModel(model);
|
|
485
|
-
await this.applyConfiguredThinkingLevelForSession(effectiveSession);
|
|
486
|
-
const thinkingInfo = effectiveSession.supportsThinking() ? ` (thinking: ${effectiveSession.thinkingLevel})` : "";
|
|
487
|
-
return `Switched to ${provider}/${modelId}${thinkingInfo}.`;
|
|
488
|
-
}
|
|
489
|
-
getCurrentModelDisplay(session) {
|
|
490
|
-
const effectiveSession = session ?? this.session;
|
|
491
|
-
if (!effectiveSession?.model) {
|
|
492
|
-
return "(no model selected)";
|
|
493
|
-
}
|
|
494
|
-
return `${effectiveSession.model.provider}/${effectiveSession.model.id}`;
|
|
495
|
-
}
|
|
496
|
-
getThinkingLevel() {
|
|
497
|
-
const session = this.requireSession();
|
|
498
|
-
if (!session.supportsThinking()) {
|
|
499
|
-
return { current: "off", available: [], supported: false };
|
|
500
|
-
}
|
|
501
|
-
return {
|
|
502
|
-
current: session.thinkingLevel,
|
|
503
|
-
available: session.getAvailableThinkingLevels(),
|
|
504
|
-
supported: true
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
setThinkingLevel(level) {
|
|
508
|
-
const session = this.requireSession();
|
|
509
|
-
if (!session.supportsThinking()) {
|
|
510
|
-
return "Current model does not support reasoning/thinking.";
|
|
511
|
-
}
|
|
512
|
-
const available = session.getAvailableThinkingLevels();
|
|
513
|
-
if (!available.includes(level)) {
|
|
514
|
-
return `Invalid thinking level "${level}" for current model. Available: ${available.join(", ")}`;
|
|
515
|
-
}
|
|
516
|
-
session.setThinkingLevel(level);
|
|
517
|
-
return `Thinking level set to "${level}".`;
|
|
518
|
-
}
|
|
519
551
|
getSessionDir() {
|
|
520
552
|
return path.join(this.config.agentDir, "sessions");
|
|
521
553
|
}
|
|
522
554
|
}
|
|
523
|
-
function isSameModel(currentModel, desiredModel) {
|
|
524
|
-
if (!currentModel) {
|
|
525
|
-
return false;
|
|
526
|
-
}
|
|
527
|
-
return currentModel.provider === desiredModel.provider && currentModel.id === desiredModel.id;
|
|
528
|
-
}
|
|
529
555
|
|
|
530
556
|
// src/config.ts
|
|
531
557
|
import path2 from "node:path";
|
|
@@ -547,8 +573,7 @@ function resolveConfig(config) {
|
|
|
547
573
|
shutdownOnSignals: config.shutdownOnSignals ?? true,
|
|
548
574
|
visionModelId: config.visionModelId?.trim() || null,
|
|
549
575
|
discordAllowedForumChannelIds: config.discordAllowedForumChannelIds ?? [],
|
|
550
|
-
discordAllowedUserIds: config.discordAllowedUserIds ?? [discordAllowedUserId]
|
|
551
|
-
sessionIdleTimeoutMs: config.sessionIdleTimeoutMs ?? null
|
|
576
|
+
discordAllowedUserIds: config.discordAllowedUserIds ?? [discordAllowedUserId]
|
|
552
577
|
};
|
|
553
578
|
}
|
|
554
579
|
function loadDiscordGatewayConfigFromEnv(overrides = {}) {
|
|
@@ -568,8 +593,7 @@ function loadDiscordGatewayConfigFromEnv(overrides = {}) {
|
|
|
568
593
|
shutdownOnSignals: overrides.shutdownOnSignals,
|
|
569
594
|
visionModelId: overrides.visionModelId ?? process.env.PI_VISION_MODEL_ID,
|
|
570
595
|
discordAllowedForumChannelIds: overrides.discordAllowedForumChannelIds ?? parseStringArrayFromEnv("DISCORD_FORUM_CHANNEL_IDS") ?? [],
|
|
571
|
-
discordAllowedUserIds: overrides.discordAllowedUserIds ?? parseStringArrayFromEnv("DISCORD_ALLOWED_USER_IDS")
|
|
572
|
-
sessionIdleTimeoutMs: overrides.sessionIdleTimeoutMs ?? parseOptionalIntFromEnv("DISCORD_SESSION_IDLE_TIMEOUT_MS") ?? undefined
|
|
596
|
+
discordAllowedUserIds: overrides.discordAllowedUserIds ?? parseStringArrayFromEnv("DISCORD_ALLOWED_USER_IDS")
|
|
573
597
|
});
|
|
574
598
|
}
|
|
575
599
|
function readRequiredValue(name, value) {
|
|
@@ -618,14 +642,6 @@ function parseStringArrayFromEnv(key) {
|
|
|
618
642
|
}
|
|
619
643
|
return value.split(",").map((id) => id.trim()).filter(Boolean);
|
|
620
644
|
}
|
|
621
|
-
function parseOptionalIntFromEnv(key) {
|
|
622
|
-
const value = process.env[key];
|
|
623
|
-
if (!value) {
|
|
624
|
-
return;
|
|
625
|
-
}
|
|
626
|
-
const parsed = parseInt(value, 10);
|
|
627
|
-
return Number.isNaN(parsed) ? undefined : parsed;
|
|
628
|
-
}
|
|
629
645
|
|
|
630
646
|
// src/discord-gateway-client.ts
|
|
631
647
|
import {
|
|
@@ -665,16 +681,6 @@ function getSessionStatusText(session, promptQueue, extras) {
|
|
|
665
681
|
return lines.join(`
|
|
666
682
|
`);
|
|
667
683
|
}
|
|
668
|
-
function getThinkingInfo(session) {
|
|
669
|
-
if (!session.supportsThinking()) {
|
|
670
|
-
return { current: "off", available: [], supported: false };
|
|
671
|
-
}
|
|
672
|
-
return {
|
|
673
|
-
current: session.thinkingLevel,
|
|
674
|
-
available: session.getAvailableThinkingLevels(),
|
|
675
|
-
supported: true
|
|
676
|
-
};
|
|
677
|
-
}
|
|
678
684
|
function getEffectiveSession(context) {
|
|
679
685
|
return context.session ?? context.agentService.getSession();
|
|
680
686
|
}
|
|
@@ -736,8 +742,8 @@ async function handleStatusCommand(trimmedInput, context) {
|
|
|
736
742
|
return effectiveSession;
|
|
737
743
|
}
|
|
738
744
|
const tools = effectiveSession.session.getAllTools();
|
|
739
|
-
const extensionsSummary = context.agentService.getExtensionsSummary();
|
|
740
|
-
const skillsSummary = context.agentService.getSkillsSummary();
|
|
745
|
+
const extensionsSummary = context.agentService.resources.getExtensionsSummary();
|
|
746
|
+
const skillsSummary = context.agentService.resources.getSkillsSummary();
|
|
741
747
|
return {
|
|
742
748
|
handled: true,
|
|
743
749
|
response: getSessionStatusText(effectiveSession.session, context.promptQueue, {
|
|
@@ -757,7 +763,7 @@ async function handleThinkingCommand(trimmedInput, context) {
|
|
|
757
763
|
}
|
|
758
764
|
const parts = trimmedInput.split(" ");
|
|
759
765
|
if (parts.length === 1) {
|
|
760
|
-
const info =
|
|
766
|
+
const info = context.agentService.models.getThinkingLevel(effectiveSession.session);
|
|
761
767
|
if (!info.supported) {
|
|
762
768
|
return {
|
|
763
769
|
handled: true,
|
|
@@ -775,23 +781,9 @@ async function handleThinkingCommand(trimmedInput, context) {
|
|
|
775
781
|
};
|
|
776
782
|
}
|
|
777
783
|
const requestedLevel = parts[1];
|
|
778
|
-
if (!effectiveSession.session.supportsThinking()) {
|
|
779
|
-
return {
|
|
780
|
-
handled: true,
|
|
781
|
-
response: "Current model does not support reasoning/thinking."
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
const available = effectiveSession.session.getAvailableThinkingLevels();
|
|
785
|
-
if (!available.includes(requestedLevel)) {
|
|
786
|
-
return {
|
|
787
|
-
handled: true,
|
|
788
|
-
response: `Invalid thinking level "${requestedLevel}" for current model. Available: ${available.join(", ")}`
|
|
789
|
-
};
|
|
790
|
-
}
|
|
791
|
-
effectiveSession.session.setThinkingLevel(requestedLevel);
|
|
792
784
|
return {
|
|
793
785
|
handled: true,
|
|
794
|
-
response:
|
|
786
|
+
response: context.agentService.models.setThinkingLevel(effectiveSession.session, requestedLevel)
|
|
795
787
|
};
|
|
796
788
|
}
|
|
797
789
|
async function handleModelCommand(trimmedInput, context) {
|
|
@@ -804,8 +796,8 @@ async function handleModelCommand(trimmedInput, context) {
|
|
|
804
796
|
}
|
|
805
797
|
const parts = trimmedInput.split(" ");
|
|
806
798
|
if (parts.length === 1) {
|
|
807
|
-
const current = context.agentService.getCurrentModelDisplay(effectiveSession.session);
|
|
808
|
-
const modelList = await context.agentService.listModels(effectiveSession.session);
|
|
799
|
+
const current = context.agentService.models.getCurrentModelDisplay(effectiveSession.session);
|
|
800
|
+
const modelList = await context.agentService.models.listModels(effectiveSession.session);
|
|
809
801
|
return {
|
|
810
802
|
handled: true,
|
|
811
803
|
response: `Current model: ${current}
|
|
@@ -827,7 +819,7 @@ ${modelList}`
|
|
|
827
819
|
const modelId = argument.substring(slashIndex + 1).trim();
|
|
828
820
|
return {
|
|
829
821
|
handled: true,
|
|
830
|
-
response: await context.agentService.switchModel(provider, modelId, effectiveSession.session)
|
|
822
|
+
response: await context.agentService.models.switchModel(provider, modelId, effectiveSession.session)
|
|
831
823
|
};
|
|
832
824
|
}
|
|
833
825
|
async function handleCompactCommand(trimmedInput, context) {
|
|
@@ -853,7 +845,7 @@ async function handleReloadCommand(trimmedInput, context) {
|
|
|
853
845
|
return {
|
|
854
846
|
handled: true,
|
|
855
847
|
response: await context.promptQueue.enqueue(async () => {
|
|
856
|
-
return context.agentService.reloadResources();
|
|
848
|
+
return context.agentService.resources.reloadResources();
|
|
857
849
|
})
|
|
858
850
|
};
|
|
859
851
|
}
|
|
@@ -907,7 +899,7 @@ async function executeCommand(input, context) {
|
|
|
907
899
|
}
|
|
908
900
|
|
|
909
901
|
// src/discord-attachments.ts
|
|
910
|
-
var
|
|
902
|
+
var logger6 = createModuleLogger("discord-attachments");
|
|
911
903
|
var TEXT_ATTACHMENT_EXTENSIONS = [
|
|
912
904
|
".txt",
|
|
913
905
|
".md",
|
|
@@ -969,11 +961,11 @@ async function readTextAttachments(message) {
|
|
|
969
961
|
const results = [];
|
|
970
962
|
for (const [, attachment] of attachments) {
|
|
971
963
|
if (!isSupportedTextAttachment(attachment)) {
|
|
972
|
-
|
|
964
|
+
logger6.debug({ messageId: message.id, filename: attachment.name }, "skipping non-text attachment");
|
|
973
965
|
continue;
|
|
974
966
|
}
|
|
975
967
|
if (attachment.size > MAX_TEXT_ATTACHMENT_SIZE_BYTES) {
|
|
976
|
-
|
|
968
|
+
logger6.warn({
|
|
977
969
|
messageId: message.id,
|
|
978
970
|
filename: attachment.name,
|
|
979
971
|
size: attachment.size
|
|
@@ -981,14 +973,14 @@ async function readTextAttachments(message) {
|
|
|
981
973
|
continue;
|
|
982
974
|
}
|
|
983
975
|
try {
|
|
984
|
-
|
|
976
|
+
logger6.info({
|
|
985
977
|
messageId: message.id,
|
|
986
978
|
filename: attachment.name,
|
|
987
979
|
size: attachment.size
|
|
988
980
|
}, "fetching attachment");
|
|
989
981
|
const response = await fetch(attachment.url);
|
|
990
982
|
if (!response.ok) {
|
|
991
|
-
|
|
983
|
+
logger6.warn({
|
|
992
984
|
messageId: message.id,
|
|
993
985
|
filename: attachment.name,
|
|
994
986
|
status: response.status
|
|
@@ -998,7 +990,7 @@ async function readTextAttachments(message) {
|
|
|
998
990
|
const content = await response.text();
|
|
999
991
|
results.push({ filename: attachment.name, content });
|
|
1000
992
|
} catch (error) {
|
|
1001
|
-
|
|
993
|
+
logger6.error({ messageId: message.id, filename: attachment.name, error }, "error fetching attachment");
|
|
1002
994
|
}
|
|
1003
995
|
}
|
|
1004
996
|
return results;
|
|
@@ -1014,7 +1006,7 @@ async function readMediaAttachments(message) {
|
|
|
1014
1006
|
continue;
|
|
1015
1007
|
}
|
|
1016
1008
|
if (attachment.size > MAX_MEDIA_ATTACHMENT_SIZE_BYTES) {
|
|
1017
|
-
|
|
1009
|
+
logger6.warn({
|
|
1018
1010
|
messageId: message.id,
|
|
1019
1011
|
filename: attachment.name,
|
|
1020
1012
|
size: attachment.size
|
|
@@ -1022,14 +1014,14 @@ async function readMediaAttachments(message) {
|
|
|
1022
1014
|
continue;
|
|
1023
1015
|
}
|
|
1024
1016
|
try {
|
|
1025
|
-
|
|
1017
|
+
logger6.info({
|
|
1026
1018
|
messageId: message.id,
|
|
1027
1019
|
filename: attachment.name,
|
|
1028
1020
|
size: attachment.size
|
|
1029
1021
|
}, "fetching media attachment");
|
|
1030
1022
|
const response = await fetch(attachment.url);
|
|
1031
1023
|
if (!response.ok) {
|
|
1032
|
-
|
|
1024
|
+
logger6.warn({
|
|
1033
1025
|
messageId: message.id,
|
|
1034
1026
|
filename: attachment.name,
|
|
1035
1027
|
status: response.status
|
|
@@ -1043,7 +1035,7 @@ async function readMediaAttachments(message) {
|
|
|
1043
1035
|
mimeType: attachment.contentType ?? "application/octet-stream"
|
|
1044
1036
|
});
|
|
1045
1037
|
} catch (error) {
|
|
1046
|
-
|
|
1038
|
+
logger6.error({ messageId: message.id, filename: attachment.name, error }, "error fetching media attachment");
|
|
1047
1039
|
}
|
|
1048
1040
|
}
|
|
1049
1041
|
return results;
|
|
@@ -1113,13 +1105,13 @@ function chunkMessage(text, maxChunkSize = SAFE_MESSAGE_LIMIT) {
|
|
|
1113
1105
|
}
|
|
1114
1106
|
|
|
1115
1107
|
// src/discord-replies.ts
|
|
1116
|
-
var
|
|
1108
|
+
var logger7 = createModuleLogger("discord-replies");
|
|
1117
1109
|
var WORKING_EMOJI = "⚙️";
|
|
1118
1110
|
async function addWorkingReaction(message) {
|
|
1119
1111
|
try {
|
|
1120
1112
|
await message.react(WORKING_EMOJI);
|
|
1121
1113
|
} catch (error) {
|
|
1122
|
-
|
|
1114
|
+
logger7.debug({ messageId: message.id, error }, "failed to add working reaction");
|
|
1123
1115
|
}
|
|
1124
1116
|
}
|
|
1125
1117
|
async function removeWorkingReaction(message) {
|
|
@@ -1129,13 +1121,13 @@ async function removeWorkingReaction(message) {
|
|
|
1129
1121
|
await reaction.users.remove(message.client.user);
|
|
1130
1122
|
}
|
|
1131
1123
|
} catch (error) {
|
|
1132
|
-
|
|
1124
|
+
logger7.debug({ messageId: message.id, error }, "failed to remove working reaction");
|
|
1133
1125
|
}
|
|
1134
1126
|
}
|
|
1135
1127
|
async function sendReply(message, text) {
|
|
1136
1128
|
const channel = message.channel;
|
|
1137
1129
|
if (!channel.isSendable()) {
|
|
1138
|
-
|
|
1130
|
+
logger7.debug({
|
|
1139
1131
|
messageId: message.id
|
|
1140
1132
|
}, "reply skipped, channel not sendable");
|
|
1141
1133
|
return;
|
|
@@ -1151,7 +1143,7 @@ async function sendReply(message, text) {
|
|
|
1151
1143
|
await channel.send(chunk);
|
|
1152
1144
|
}
|
|
1153
1145
|
} catch (error) {
|
|
1154
|
-
|
|
1146
|
+
logger7.error({
|
|
1155
1147
|
messageId: message.id,
|
|
1156
1148
|
error
|
|
1157
1149
|
}, "send reply failed");
|
|
@@ -1159,7 +1151,7 @@ async function sendReply(message, text) {
|
|
|
1159
1151
|
}
|
|
1160
1152
|
|
|
1161
1153
|
// src/image-description.ts
|
|
1162
|
-
var
|
|
1154
|
+
var logger8 = createModuleLogger("image-description");
|
|
1163
1155
|
async function describeMediaAttachment(agentService, imageData, mimeType, userText, visionModel) {
|
|
1164
1156
|
const session = await agentService.createTemporarySession();
|
|
1165
1157
|
await session.setModel(visionModel);
|
|
@@ -1180,7 +1172,7 @@ async function describeMediaAttachment(agentService, imageData, mimeType, userTe
|
|
|
1180
1172
|
await session.prompt(promptText, { images: [imageContent] });
|
|
1181
1173
|
text = extractLastAssistantText(session);
|
|
1182
1174
|
} catch (error) {
|
|
1183
|
-
|
|
1175
|
+
logger8.error({ error, mimeType }, "vision model prompt failed");
|
|
1184
1176
|
text = "(Vision model failed to process the file.)";
|
|
1185
1177
|
} finally {
|
|
1186
1178
|
session.dispose();
|
|
@@ -1188,7 +1180,7 @@ async function describeMediaAttachment(agentService, imageData, mimeType, userTe
|
|
|
1188
1180
|
if (!text) {
|
|
1189
1181
|
return "(Vision model returned no description.)";
|
|
1190
1182
|
}
|
|
1191
|
-
|
|
1183
|
+
logger8.debug({ textLength: text.length, mimeType }, "media described");
|
|
1192
1184
|
return text;
|
|
1193
1185
|
}
|
|
1194
1186
|
function extractLastAssistantText(session) {
|
|
@@ -1224,7 +1216,7 @@ function isAssistantMessage(msg) {
|
|
|
1224
1216
|
}
|
|
1225
1217
|
|
|
1226
1218
|
// src/discord-media-resolution.ts
|
|
1227
|
-
var
|
|
1219
|
+
var logger9 = createModuleLogger("discord-media-resolution");
|
|
1228
1220
|
function parseProviderModelId(value) {
|
|
1229
1221
|
const trimmed = value.trim();
|
|
1230
1222
|
if (!trimmed) {
|
|
@@ -1258,7 +1250,7 @@ async function resolveMediaAttachmentsForPrompt(mediaAttachments, content, curre
|
|
|
1258
1250
|
const modelSupportsVision = currentModel?.input.includes("image") ?? false;
|
|
1259
1251
|
if (modelSupportsVision) {
|
|
1260
1252
|
const names = mediaAttachments.map((media) => media.filename).join(", ");
|
|
1261
|
-
|
|
1253
|
+
logger9.info({
|
|
1262
1254
|
count: mediaAttachments.length,
|
|
1263
1255
|
filenames: names,
|
|
1264
1256
|
model: currentModel ? `${currentModel.provider}/${currentModel.id}` : "none"
|
|
@@ -1274,7 +1266,7 @@ async function resolveMediaAttachmentsForPrompt(mediaAttachments, content, curre
|
|
|
1274
1266
|
}
|
|
1275
1267
|
if (!config.visionModelId) {
|
|
1276
1268
|
const names = mediaAttachments.map((media) => media.filename).join(", ");
|
|
1277
|
-
|
|
1269
|
+
logger9.info({ filenames: names }, "media attachments received but vision model not configured");
|
|
1278
1270
|
const note = `
|
|
1279
1271
|
|
|
1280
1272
|
[User sent media attachment(s): ${names}]
|
|
@@ -1288,9 +1280,9 @@ async function resolveMediaAttachmentsForPrompt(mediaAttachments, content, curre
|
|
|
1288
1280
|
if (!parsedVisionModelId) {
|
|
1289
1281
|
return { content, images: [] };
|
|
1290
1282
|
}
|
|
1291
|
-
const visionModel = agentService.findModel(parsedVisionModelId.provider, parsedVisionModelId.modelId);
|
|
1283
|
+
const visionModel = agentService.models.findModel(parsedVisionModelId.provider, parsedVisionModelId.modelId);
|
|
1292
1284
|
if (!visionModel) {
|
|
1293
|
-
|
|
1285
|
+
logger9.warn({ visionModelId: config.visionModelId }, "vision model not found in registry");
|
|
1294
1286
|
const names = mediaAttachments.map((media) => media.filename).join(", ");
|
|
1295
1287
|
const note = `
|
|
1296
1288
|
|
|
@@ -1301,7 +1293,7 @@ async function resolveMediaAttachmentsForPrompt(mediaAttachments, content, curre
|
|
|
1301
1293
|
images: []
|
|
1302
1294
|
};
|
|
1303
1295
|
}
|
|
1304
|
-
|
|
1296
|
+
logger9.info({
|
|
1305
1297
|
count: mediaAttachments.length,
|
|
1306
1298
|
visionModel: `${visionModel.provider}/${visionModel.id}`
|
|
1307
1299
|
}, "describing media with vision model");
|
|
@@ -1328,7 +1320,7 @@ ${content}` : descriptionPrefix,
|
|
|
1328
1320
|
}
|
|
1329
1321
|
|
|
1330
1322
|
// src/discord-typing.ts
|
|
1331
|
-
var
|
|
1323
|
+
var logger10 = createModuleLogger("discord-typing");
|
|
1332
1324
|
var TYPING_INTERVAL_MS = 9000;
|
|
1333
1325
|
var typingIntervals = new Map;
|
|
1334
1326
|
async function sendTypingSafe(channel, channelKey) {
|
|
@@ -1340,7 +1332,7 @@ async function sendTypingSafe(channel, channelKey) {
|
|
|
1340
1332
|
headers: { Authorization: `Bot ${token}` }
|
|
1341
1333
|
});
|
|
1342
1334
|
if (response.ok) {
|
|
1343
|
-
|
|
1335
|
+
logger10.debug("[TYPING] STATUS UPDATED OK");
|
|
1344
1336
|
return;
|
|
1345
1337
|
}
|
|
1346
1338
|
if (response.status === 429) {
|
|
@@ -1352,28 +1344,28 @@ async function sendTypingSafe(channel, channelKey) {
|
|
|
1352
1344
|
retryMs = parsed.retry_after * 1000 + 500;
|
|
1353
1345
|
}
|
|
1354
1346
|
} catch {}
|
|
1355
|
-
|
|
1347
|
+
logger10.warn({ channelKey, retryMs, response: body }, `[TYPING] 429, retrying after ${retryMs}ms delay`);
|
|
1356
1348
|
await new Promise((resolve) => setTimeout(resolve, retryMs));
|
|
1357
1349
|
await fetch(url, {
|
|
1358
1350
|
method: "POST",
|
|
1359
1351
|
headers: { Authorization: `Bot ${token}` }
|
|
1360
1352
|
});
|
|
1361
|
-
|
|
1353
|
+
logger10.info({ channelKey }, "[TYPING] retry done");
|
|
1362
1354
|
return;
|
|
1363
1355
|
}
|
|
1364
|
-
|
|
1356
|
+
logger10.warn({ channelKey, status: response.status }, "[TYPING] unexpected status");
|
|
1365
1357
|
} catch (error) {
|
|
1366
|
-
|
|
1358
|
+
logger10.warn({ channelKey, error }, "[TYPING] FAILED");
|
|
1367
1359
|
}
|
|
1368
1360
|
}
|
|
1369
1361
|
function startTypingForChannel(channel, channelKey) {
|
|
1370
1362
|
const existing = typingIntervals.get(channelKey);
|
|
1371
1363
|
if (existing) {
|
|
1372
1364
|
existing.refs += 1;
|
|
1373
|
-
|
|
1365
|
+
logger10.debug({ channelKey, refs: existing.refs }, "[TYPING] ref++ (reusing existing interval)");
|
|
1374
1366
|
return;
|
|
1375
1367
|
}
|
|
1376
|
-
|
|
1368
|
+
logger10.debug("[TYPING] started new interval");
|
|
1377
1369
|
sendTypingSafe(channel, channelKey);
|
|
1378
1370
|
const interval = setInterval(() => {
|
|
1379
1371
|
sendTypingSafe(channel, channelKey);
|
|
@@ -1383,17 +1375,17 @@ function startTypingForChannel(channel, channelKey) {
|
|
|
1383
1375
|
function stopTypingForChannel(channelKey) {
|
|
1384
1376
|
const entry = typingIntervals.get(channelKey);
|
|
1385
1377
|
if (!entry) {
|
|
1386
|
-
|
|
1378
|
+
logger10.debug({ channelKey }, "[TYPING] stop called but no entry found");
|
|
1387
1379
|
return;
|
|
1388
1380
|
}
|
|
1389
1381
|
entry.refs -= 1;
|
|
1390
1382
|
if (entry.refs <= 0) {
|
|
1391
1383
|
clearInterval(entry.interval);
|
|
1392
1384
|
typingIntervals.delete(channelKey);
|
|
1393
|
-
|
|
1385
|
+
logger10.debug("[TYPING] interval cleared (refs hit 0)");
|
|
1394
1386
|
return;
|
|
1395
1387
|
}
|
|
1396
|
-
|
|
1388
|
+
logger10.debug("[TYPING] ref-- (interval still active)");
|
|
1397
1389
|
}
|
|
1398
1390
|
|
|
1399
1391
|
// src/prompt-context.ts
|
|
@@ -1445,7 +1437,7 @@ function normalizeContextValue(value) {
|
|
|
1445
1437
|
}
|
|
1446
1438
|
|
|
1447
1439
|
// src/discord-message-handler.ts
|
|
1448
|
-
var
|
|
1440
|
+
var logger11 = createModuleLogger("discord-message-handler");
|
|
1449
1441
|
function buildDiscordPromptContent(message, scope, content, config) {
|
|
1450
1442
|
const isThread = scope.startsWith("thread:") && message.channel.isThread();
|
|
1451
1443
|
return buildDiscordMessageContextPrompt(content, {
|
|
@@ -1465,23 +1457,23 @@ function buildDiscordPromptContent(message, scope, content, config) {
|
|
|
1465
1457
|
}
|
|
1466
1458
|
async function handleDiscordMessage(message, config, agentService, sessionRegistry, authConfig) {
|
|
1467
1459
|
if (message.author.bot) {
|
|
1468
|
-
|
|
1460
|
+
logger11.debug("ignored bot message");
|
|
1469
1461
|
return;
|
|
1470
1462
|
}
|
|
1471
1463
|
if (message.system) {
|
|
1472
|
-
|
|
1464
|
+
logger11.debug({ messageId: message.id }, "ignored system message");
|
|
1473
1465
|
return;
|
|
1474
1466
|
}
|
|
1475
1467
|
const scope = resolveMessageScope(message);
|
|
1476
1468
|
if (scope === null) {
|
|
1477
|
-
|
|
1469
|
+
logger11.debug({
|
|
1478
1470
|
messageId: message.id,
|
|
1479
1471
|
channelType: message.channel.type
|
|
1480
1472
|
}, "unsupported channel type, ignoring");
|
|
1481
1473
|
return;
|
|
1482
1474
|
}
|
|
1483
1475
|
if (!isAuthorizedMessage(message, scope, authConfig)) {
|
|
1484
|
-
|
|
1476
|
+
logger11.debug({
|
|
1485
1477
|
messageId: message.id,
|
|
1486
1478
|
authorId: message.author.id,
|
|
1487
1479
|
scope
|
|
@@ -1501,10 +1493,10 @@ ${attachment.content}`;
|
|
|
1501
1493
|
}
|
|
1502
1494
|
const mediaAttachments = await readMediaAttachments(message);
|
|
1503
1495
|
if (!content && mediaAttachments.length === 0) {
|
|
1504
|
-
|
|
1496
|
+
logger11.debug({ messageId: message.id }, "ignored empty message (no text or images)");
|
|
1505
1497
|
return;
|
|
1506
1498
|
}
|
|
1507
|
-
|
|
1499
|
+
logger11.info({
|
|
1508
1500
|
direction: "IN",
|
|
1509
1501
|
scope,
|
|
1510
1502
|
messageId: message.id,
|
|
@@ -1519,7 +1511,7 @@ ${attachment.content}`;
|
|
|
1519
1511
|
const { entry, created } = await sessionRegistry.getOrCreate(scope);
|
|
1520
1512
|
const { session, promptQueue } = entry;
|
|
1521
1513
|
if (created && scope.startsWith("thread:") && message.channel.isThread()) {
|
|
1522
|
-
|
|
1514
|
+
logger11.info({
|
|
1523
1515
|
scope,
|
|
1524
1516
|
threadName: message.channel.name
|
|
1525
1517
|
}, "new thread session");
|
|
@@ -1532,7 +1524,7 @@ ${attachment.content}`;
|
|
|
1532
1524
|
if (commandResult.handled) {
|
|
1533
1525
|
stopTypingForChannel(channelKey);
|
|
1534
1526
|
if (commandResult.archive && scope.startsWith("thread:")) {
|
|
1535
|
-
|
|
1527
|
+
logger11.info({ scope }, "archiving thread");
|
|
1536
1528
|
const archiveChannel = message.channel;
|
|
1537
1529
|
if (archiveChannel.isSendable()) {
|
|
1538
1530
|
await archiveChannel.send(commandResult.response ?? "Archiving...");
|
|
@@ -1542,12 +1534,12 @@ ${attachment.content}`;
|
|
|
1542
1534
|
await archiveChannel.setArchived(true);
|
|
1543
1535
|
}
|
|
1544
1536
|
} catch (error) {
|
|
1545
|
-
|
|
1537
|
+
logger11.error({ error }, "failed to archive thread");
|
|
1546
1538
|
}
|
|
1547
1539
|
await sessionRegistry.remove(scope);
|
|
1548
1540
|
return;
|
|
1549
1541
|
}
|
|
1550
|
-
|
|
1542
|
+
logger11.info({
|
|
1551
1543
|
messageId: message.id,
|
|
1552
1544
|
command: content,
|
|
1553
1545
|
hasResponse: Boolean(commandResult.response)
|
|
@@ -1559,7 +1551,7 @@ ${attachment.content}`;
|
|
|
1559
1551
|
}
|
|
1560
1552
|
if (!message.channel.isSendable()) {
|
|
1561
1553
|
stopTypingForChannel(channelKey);
|
|
1562
|
-
|
|
1554
|
+
logger11.debug({ messageId: message.id }, "channel not sendable");
|
|
1563
1555
|
return;
|
|
1564
1556
|
}
|
|
1565
1557
|
await addWorkingReaction(message);
|
|
@@ -1582,7 +1574,6 @@ ${attachment.content}`;
|
|
|
1582
1574
|
const wrappedContent = buildDiscordPromptContent(message, scope, promptContent, config);
|
|
1583
1575
|
const transformedPrompt = await config.promptTransform(wrappedContent);
|
|
1584
1576
|
return runPromptAndCollectReply(session, transformedPrompt, {
|
|
1585
|
-
logPrefix: `[agent:${session.sessionId}]`,
|
|
1586
1577
|
images: promptImages
|
|
1587
1578
|
});
|
|
1588
1579
|
});
|
|
@@ -1594,7 +1585,7 @@ ${attachment.content}`;
|
|
|
1594
1585
|
}
|
|
1595
1586
|
|
|
1596
1587
|
// src/discord-gateway-client.ts
|
|
1597
|
-
var
|
|
1588
|
+
var logger12 = createModuleLogger("discord-gateway");
|
|
1598
1589
|
async function startGatewayClient(config, agentService, sessionRegistry, authConfig) {
|
|
1599
1590
|
const client = new Client({
|
|
1600
1591
|
intents: [
|
|
@@ -1606,7 +1597,7 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
|
|
|
1606
1597
|
partials: [Partials.Channel]
|
|
1607
1598
|
});
|
|
1608
1599
|
client.once(Events.ClientReady, async (readyClient) => {
|
|
1609
|
-
|
|
1600
|
+
logger12.info({ userTag: readyClient.user.tag }, "logged in");
|
|
1610
1601
|
if (!authConfig.startupMessage) {
|
|
1611
1602
|
return;
|
|
1612
1603
|
}
|
|
@@ -1614,24 +1605,24 @@ async function startGatewayClient(config, agentService, sessionRegistry, authCon
|
|
|
1614
1605
|
const user = await readyClient.users.fetch(authConfig.discordAllowedUserId);
|
|
1615
1606
|
const dmChannel = await user.createDM();
|
|
1616
1607
|
await dmChannel.send(authConfig.startupMessage);
|
|
1617
|
-
|
|
1608
|
+
logger12.info({
|
|
1618
1609
|
userId: authConfig.discordAllowedUserId
|
|
1619
1610
|
}, "sent startup dm");
|
|
1620
1611
|
} catch (error) {
|
|
1621
|
-
|
|
1612
|
+
logger12.error({ error }, "failed to send startup dm");
|
|
1622
1613
|
}
|
|
1623
1614
|
});
|
|
1624
1615
|
client.on(Events.MessageCreate, async (message) => {
|
|
1625
1616
|
try {
|
|
1626
1617
|
await handleDiscordMessage(message, config, agentService, sessionRegistry, authConfig);
|
|
1627
1618
|
} catch (error) {
|
|
1628
|
-
|
|
1619
|
+
logger12.error({ error, direction: "IN" }, "message handling failed");
|
|
1629
1620
|
await sendReply(message, "The bot hit an error while handling that message.");
|
|
1630
1621
|
}
|
|
1631
1622
|
});
|
|
1632
1623
|
client.on(Events.ThreadDelete, async (thread) => {
|
|
1633
1624
|
const scope = `thread:${thread.id}`;
|
|
1634
|
-
|
|
1625
|
+
logger12.info({ threadId: thread.id, scope }, "thread deleted");
|
|
1635
1626
|
await sessionRegistry.remove(scope);
|
|
1636
1627
|
});
|
|
1637
1628
|
await client.login(config.discordBotToken);
|
|
@@ -1690,7 +1681,7 @@ function sessionDirForScope(agentDir, scope) {
|
|
|
1690
1681
|
}
|
|
1691
1682
|
throw new Error(`Unknown session scope: ${scope}`);
|
|
1692
1683
|
}
|
|
1693
|
-
var
|
|
1684
|
+
var logger13 = createModuleLogger("session-registry");
|
|
1694
1685
|
|
|
1695
1686
|
class SessionRegistry {
|
|
1696
1687
|
scopes = new Map;
|
|
@@ -1712,7 +1703,7 @@ class SessionRegistry {
|
|
|
1712
1703
|
createdAt: new Date
|
|
1713
1704
|
};
|
|
1714
1705
|
this.scopes.set(scope, entry);
|
|
1715
|
-
|
|
1706
|
+
logger13.debug({
|
|
1716
1707
|
scope,
|
|
1717
1708
|
sessionDir,
|
|
1718
1709
|
sessionId: session.sessionId
|
|
@@ -1724,7 +1715,7 @@ class SessionRegistry {
|
|
|
1724
1715
|
if (!entry) {
|
|
1725
1716
|
return;
|
|
1726
1717
|
}
|
|
1727
|
-
|
|
1718
|
+
logger13.debug({ scope }, "removing scope");
|
|
1728
1719
|
await entry.session.abort();
|
|
1729
1720
|
entry.session.dispose();
|
|
1730
1721
|
this.scopes.delete(scope);
|
|
@@ -1736,7 +1727,7 @@ class SessionRegistry {
|
|
|
1736
1727
|
return Array.from(this.scopes.keys());
|
|
1737
1728
|
}
|
|
1738
1729
|
async shutdownAll() {
|
|
1739
|
-
|
|
1730
|
+
logger13.info({ count: this.scopes.size }, "shutting down all scopes");
|
|
1740
1731
|
const scopes = Array.from(this.scopes.keys());
|
|
1741
1732
|
for (const scope of scopes) {
|
|
1742
1733
|
await this.remove(scope);
|
|
@@ -1745,13 +1736,13 @@ class SessionRegistry {
|
|
|
1745
1736
|
}
|
|
1746
1737
|
|
|
1747
1738
|
// src/index.ts
|
|
1748
|
-
var
|
|
1739
|
+
var logger14 = createModuleLogger("index");
|
|
1749
1740
|
async function startDiscordGateway(config) {
|
|
1750
1741
|
const resolvedConfig = resolveConfig(config);
|
|
1751
1742
|
const agentService = new AgentService(resolvedConfig);
|
|
1752
|
-
|
|
1743
|
+
logger14.info("initializing agent service");
|
|
1753
1744
|
await agentService.initialize();
|
|
1754
|
-
|
|
1745
|
+
logger14.info(agentService.getStatus(), "agent ready");
|
|
1755
1746
|
const authConfig = {
|
|
1756
1747
|
discordAllowedUserId: resolvedConfig.discordAllowedUserId,
|
|
1757
1748
|
discordAllowedForumChannelIds: resolvedConfig.discordAllowedForumChannelIds,
|
|
@@ -1779,7 +1770,7 @@ function createGatewayStopHandler(client, agentService, sessionRegistry, config)
|
|
|
1779
1770
|
return;
|
|
1780
1771
|
}
|
|
1781
1772
|
stopped = true;
|
|
1782
|
-
|
|
1773
|
+
logger14.info({
|
|
1783
1774
|
cwd: config.cwd,
|
|
1784
1775
|
agentDir: config.agentDir
|
|
1785
1776
|
}, "stopping discord gateway");
|
|
@@ -1790,9 +1781,9 @@ function createGatewayStopHandler(client, agentService, sessionRegistry, config)
|
|
|
1790
1781
|
}
|
|
1791
1782
|
function registerSignalHandlers(stop) {
|
|
1792
1783
|
const handleSignal = (signal) => {
|
|
1793
|
-
|
|
1784
|
+
logger14.info({ signal }, "received signal");
|
|
1794
1785
|
stop().finally(() => {
|
|
1795
|
-
|
|
1786
|
+
logger14.info("done");
|
|
1796
1787
|
process.exit(0);
|
|
1797
1788
|
});
|
|
1798
1789
|
};
|
package/dist/reply-buffer.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import type { ImageContent } from "@earendil-works/pi-ai";
|
|
3
3
|
type CollectReplyOptions = {
|
|
4
|
-
logPrefix?: string;
|
|
5
4
|
images?: ImageContent[];
|
|
6
5
|
};
|
|
7
6
|
export declare function runPromptAndCollectReply(session: AgentSession, prompt: string, options?: CollectReplyOptions): Promise<string>;
|
package/dist/types.d.ts
CHANGED
|
@@ -25,8 +25,6 @@ export type DiscordGatewayConfig = {
|
|
|
25
25
|
discordAllowedForumChannelIds?: string[];
|
|
26
26
|
/** Which users can interact in forum threads (defaults to [discordAllowedUserId]). */
|
|
27
27
|
discordAllowedUserIds?: string[];
|
|
28
|
-
/** Auto-shutdown idle thread sessions after this many ms. */
|
|
29
|
-
sessionIdleTimeoutMs?: number;
|
|
30
28
|
};
|
|
31
29
|
export type ResolvedDiscordGatewayConfig = {
|
|
32
30
|
discordBotToken: string;
|
|
@@ -45,7 +43,6 @@ export type ResolvedDiscordGatewayConfig = {
|
|
|
45
43
|
visionModelId: string | null;
|
|
46
44
|
discordAllowedForumChannelIds: string[];
|
|
47
45
|
discordAllowedUserIds: string[];
|
|
48
|
-
sessionIdleTimeoutMs: number | null;
|
|
49
46
|
};
|
|
50
47
|
export type ContextUsageStatus = {
|
|
51
48
|
tokens: number | null;
|