@phi-code-admin/phi-code 0.57.2 โ 0.57.4
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/extensions/phi/skill-loader.ts +4 -5
- package/extensions/phi/smart-router.ts +172 -190
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Automatic skill discovery at startup
|
|
11
11
|
* - Keyword-based skill detection and loading
|
|
12
12
|
* - /skills command to list available skills
|
|
13
|
-
* - Contextual skill injection via
|
|
13
|
+
* - Contextual skill injection via ui notifications
|
|
14
14
|
*
|
|
15
15
|
* Usage:
|
|
16
16
|
* 1. Copy to packages/coding-agent/extensions/phi/skill-loader.ts
|
|
@@ -241,10 +241,9 @@ ${skill.content}
|
|
|
241
241
|
---
|
|
242
242
|
This skill was automatically loaded based on your request. Use this knowledge to assist with the task.`;
|
|
243
243
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}, { source: "extension" });
|
|
244
|
+
// Notify the model about the relevant skill content
|
|
245
|
+
// The model can then use the `read` tool to load the full SKILL.md
|
|
246
|
+
ctx.ui.notify(`๐ Relevant skill loaded: **${skill.name}**\n${skill.description}\nUse \`read ${skill.path}\` for full content.`, "info");
|
|
248
247
|
}
|
|
249
248
|
|
|
250
249
|
// Notify user which skills were loaded
|
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Smart Router Extension - Intelligent model routing for different task types
|
|
3
3
|
*
|
|
4
|
-
* Analyzes user input
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
4
|
+
* Analyzes user input keywords and suggests the optimal model:
|
|
5
|
+
* - code: implement, create, build, refactor โ qwen3-coder-plus
|
|
6
|
+
* - debug: fix, bug, error, crash โ qwen3-max-2026-01-23
|
|
7
|
+
* - explore: read, analyze, explain, understand โ kimi-k2.5
|
|
8
|
+
* - plan: plan, design, architect, spec โ qwen3-max-2026-01-23
|
|
9
|
+
* - test: test, verify, validate, check โ kimi-k2.5
|
|
10
|
+
* - review: review, audit, quality, security โ qwen3.5-plus
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* Features:
|
|
14
|
-
* - Input analysis and model recommendations
|
|
15
|
-
* - Configurable routing rules
|
|
16
|
-
* - User notifications via ctx.ui.notify
|
|
17
|
-
*
|
|
18
|
-
* Usage:
|
|
19
|
-
* 1. Copy to packages/coding-agent/extensions/phi/smart-router.ts
|
|
20
|
-
* 2. Optionally configure via ~/.phi/agent/routing.json
|
|
12
|
+
* Configuration: ~/.phi/agent/routing.json (same format as config/routing.json)
|
|
13
|
+
* Command: /routing โ show config, enable/disable, test, reload
|
|
21
14
|
*/
|
|
22
15
|
|
|
23
16
|
import type { ExtensionAPI } from "phi-code";
|
|
@@ -25,266 +18,255 @@ import { readFile, mkdir, writeFile, access } from "node:fs/promises";
|
|
|
25
18
|
import { join } from "node:path";
|
|
26
19
|
import { homedir } from "node:os";
|
|
27
20
|
|
|
21
|
+
// โโโ Types โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
22
|
+
|
|
23
|
+
interface RouteEntry {
|
|
24
|
+
description: string;
|
|
25
|
+
keywords: string[];
|
|
26
|
+
preferredModel: string;
|
|
27
|
+
fallback: string;
|
|
28
|
+
agent: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
28
31
|
interface RoutingConfig {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
models: {
|
|
36
|
-
coder: string;
|
|
37
|
-
reasoning: string;
|
|
38
|
-
fast: string;
|
|
39
|
-
};
|
|
32
|
+
routes: Record<string, RouteEntry>;
|
|
33
|
+
default: { model: string; agent: string | null };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface FullConfig {
|
|
37
|
+
routing: RoutingConfig;
|
|
40
38
|
enabled: boolean;
|
|
41
39
|
notifyOnRecommendation: boolean;
|
|
42
40
|
}
|
|
43
41
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
42
|
+
// โโโ Defaults (aligned with config/routing.json and default-models.json) โโ
|
|
43
|
+
|
|
44
|
+
const DEFAULT_ROUTING: RoutingConfig = {
|
|
45
|
+
routes: {
|
|
46
|
+
code: {
|
|
47
|
+
description: "Code generation, implementation, refactoring",
|
|
48
|
+
keywords: ["implement", "create", "build", "refactor", "write", "add", "modify", "update", "generate", "code", "develop", "function", "class"],
|
|
49
|
+
preferredModel: "qwen3-coder-plus",
|
|
50
|
+
fallback: "qwen3.5-plus",
|
|
51
|
+
agent: "code",
|
|
52
|
+
},
|
|
53
|
+
debug: {
|
|
54
|
+
description: "Debugging, fixing, error resolution",
|
|
55
|
+
keywords: ["fix", "bug", "error", "debug", "crash", "broken", "failing", "issue", "troubleshoot", "repair", "solve"],
|
|
56
|
+
preferredModel: "qwen3-max-2026-01-23",
|
|
57
|
+
fallback: "qwen3.5-plus",
|
|
58
|
+
agent: "code",
|
|
59
|
+
},
|
|
60
|
+
explore: {
|
|
61
|
+
description: "Code reading, analysis, understanding",
|
|
62
|
+
keywords: ["read", "analyze", "explain", "understand", "find", "search", "look", "show", "what", "how", "explore", "examine"],
|
|
63
|
+
preferredModel: "kimi-k2.5",
|
|
64
|
+
fallback: "glm-4.7",
|
|
65
|
+
agent: "explore",
|
|
66
|
+
},
|
|
67
|
+
plan: {
|
|
68
|
+
description: "Architecture, design, planning",
|
|
69
|
+
keywords: ["plan", "design", "architect", "spec", "structure", "organize", "strategy", "approach", "roadmap"],
|
|
70
|
+
preferredModel: "qwen3-max-2026-01-23",
|
|
71
|
+
fallback: "qwen3.5-plus",
|
|
72
|
+
agent: "plan",
|
|
73
|
+
},
|
|
74
|
+
test: {
|
|
75
|
+
description: "Testing, validation, verification",
|
|
76
|
+
keywords: ["test", "verify", "validate", "check", "assert", "coverage", "unit", "integration", "e2e"],
|
|
77
|
+
preferredModel: "kimi-k2.5",
|
|
78
|
+
fallback: "glm-4.7",
|
|
79
|
+
agent: "test",
|
|
80
|
+
},
|
|
81
|
+
review: {
|
|
82
|
+
description: "Code review, quality assessment",
|
|
83
|
+
keywords: ["review", "audit", "quality", "security", "improve", "optimize", "refine", "critique"],
|
|
84
|
+
preferredModel: "qwen3.5-plus",
|
|
85
|
+
fallback: "qwen3-max-2026-01-23",
|
|
86
|
+
agent: "review",
|
|
87
|
+
},
|
|
50
88
|
},
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
fast: "anthropic/claude-haiku"
|
|
89
|
+
default: {
|
|
90
|
+
model: "qwen3.5-plus",
|
|
91
|
+
agent: null,
|
|
55
92
|
},
|
|
56
|
-
enabled: true,
|
|
57
|
-
notifyOnRecommendation: true
|
|
58
93
|
};
|
|
59
94
|
|
|
95
|
+
// โโโ Extension โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
96
|
+
|
|
60
97
|
export default function smartRouterExtension(pi: ExtensionAPI) {
|
|
61
|
-
let config: RoutingConfig = DEFAULT_CONFIG;
|
|
62
98
|
const configDir = join(homedir(), ".phi", "agent");
|
|
63
99
|
const configPath = join(configDir, "routing.json");
|
|
64
100
|
|
|
101
|
+
let config: FullConfig = {
|
|
102
|
+
routing: DEFAULT_ROUTING,
|
|
103
|
+
enabled: true,
|
|
104
|
+
notifyOnRecommendation: true,
|
|
105
|
+
};
|
|
106
|
+
|
|
65
107
|
/**
|
|
66
|
-
* Load routing
|
|
108
|
+
* Load routing config from ~/.phi/agent/routing.json
|
|
67
109
|
*/
|
|
68
110
|
async function loadConfig() {
|
|
69
111
|
try {
|
|
70
112
|
await access(configPath);
|
|
71
|
-
const
|
|
72
|
-
const userConfig = JSON.parse(
|
|
73
|
-
|
|
74
|
-
// Merge with defaults
|
|
75
|
-
config = {
|
|
76
|
-
...DEFAULT_CONFIG,
|
|
77
|
-
...userConfig,
|
|
78
|
-
patterns: { ...DEFAULT_CONFIG.patterns, ...userConfig.patterns },
|
|
79
|
-
models: { ...DEFAULT_CONFIG.models, ...userConfig.models }
|
|
80
|
-
};
|
|
81
|
-
} catch (error) {
|
|
82
|
-
// Config doesn't exist or is invalid, use defaults
|
|
83
|
-
console.log("Using default routing configuration");
|
|
84
|
-
await saveDefaultConfig();
|
|
85
|
-
}
|
|
86
|
-
}
|
|
113
|
+
const text = await readFile(configPath, "utf-8");
|
|
114
|
+
const userConfig = JSON.parse(text);
|
|
87
115
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
116
|
+
// Support both flat format (routes at top level) and wrapped format
|
|
117
|
+
if (userConfig.routes) {
|
|
118
|
+
config.routing = {
|
|
119
|
+
routes: { ...DEFAULT_ROUTING.routes, ...userConfig.routes },
|
|
120
|
+
default: userConfig.default || DEFAULT_ROUTING.default,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (typeof userConfig.enabled === "boolean") config.enabled = userConfig.enabled;
|
|
125
|
+
if (typeof userConfig.notifyOnRecommendation === "boolean") config.notifyOnRecommendation = userConfig.notifyOnRecommendation;
|
|
126
|
+
} catch {
|
|
127
|
+
// No config file โ use defaults, and save them for reference
|
|
128
|
+
try {
|
|
129
|
+
await mkdir(configDir, { recursive: true });
|
|
130
|
+
await writeFile(configPath, JSON.stringify(DEFAULT_ROUTING, null, 2), "utf-8");
|
|
131
|
+
} catch {
|
|
132
|
+
// Can't write, that's fine
|
|
133
|
+
}
|
|
98
134
|
}
|
|
99
135
|
}
|
|
100
136
|
|
|
101
137
|
/**
|
|
102
|
-
* Analyze input text to
|
|
138
|
+
* Analyze input text to classify task type
|
|
103
139
|
*/
|
|
104
|
-
function
|
|
105
|
-
const
|
|
106
|
-
const results: Array<{
|
|
140
|
+
function classifyTask(text: string): { category: string | null; confidence: number; matches: string[]; route: RouteEntry | null } {
|
|
141
|
+
const lower = text.toLowerCase();
|
|
142
|
+
const results: Array<{ category: string; confidence: number; matches: string[]; route: RouteEntry }> = [];
|
|
107
143
|
|
|
108
|
-
|
|
109
|
-
for (const [category, patterns] of Object.entries(config.patterns)) {
|
|
144
|
+
for (const [category, route] of Object.entries(config.routing.routes)) {
|
|
110
145
|
const matches: string[] = [];
|
|
111
|
-
let matchCount = 0;
|
|
112
146
|
|
|
113
|
-
for (const
|
|
114
|
-
if (
|
|
115
|
-
matches.push(
|
|
116
|
-
matchCount++;
|
|
147
|
+
for (const keyword of route.keywords) {
|
|
148
|
+
if (lower.includes(keyword.toLowerCase())) {
|
|
149
|
+
matches.push(keyword);
|
|
117
150
|
}
|
|
118
151
|
}
|
|
119
152
|
|
|
120
|
-
if (
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
matches
|
|
127
|
-
});
|
|
153
|
+
if (matches.length > 0) {
|
|
154
|
+
// Confidence = weighted match ratio
|
|
155
|
+
// More matches = higher confidence, but cap at 95%
|
|
156
|
+
const ratio = matches.length / route.keywords.length;
|
|
157
|
+
const confidence = Math.min(95, Math.round(ratio * 100 + matches.length * 5));
|
|
158
|
+
results.push({ category, confidence, matches, route });
|
|
128
159
|
}
|
|
129
160
|
}
|
|
130
161
|
|
|
131
|
-
// Return the highest confidence match
|
|
132
162
|
if (results.length === 0) {
|
|
133
|
-
return {
|
|
163
|
+
return { category: null, confidence: 0, matches: [], route: null };
|
|
134
164
|
}
|
|
135
165
|
|
|
166
|
+
// Highest confidence wins
|
|
136
167
|
results.sort((a, b) => b.confidence - a.confidence);
|
|
137
168
|
return results[0];
|
|
138
169
|
}
|
|
139
170
|
|
|
140
|
-
|
|
141
|
-
* Get recommended model for task type
|
|
142
|
-
*/
|
|
143
|
-
function getRecommendedModel(taskType: keyof RoutingConfig['patterns'] | null): string | null {
|
|
144
|
-
if (!taskType) return null;
|
|
145
|
-
|
|
146
|
-
switch (taskType) {
|
|
147
|
-
case 'code':
|
|
148
|
-
return config.models.coder;
|
|
149
|
-
case 'debug':
|
|
150
|
-
case 'planning':
|
|
151
|
-
return config.models.reasoning;
|
|
152
|
-
case 'exploration':
|
|
153
|
-
return config.models.fast;
|
|
154
|
-
default:
|
|
155
|
-
return null;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
171
|
+
// โโโ Input Event โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
158
172
|
|
|
159
|
-
/**
|
|
160
|
-
* Get task type description
|
|
161
|
-
*/
|
|
162
|
-
function getTaskDescription(taskType: keyof RoutingConfig['patterns'] | null): string {
|
|
163
|
-
switch (taskType) {
|
|
164
|
-
case 'code':
|
|
165
|
-
return 'Code Implementation';
|
|
166
|
-
case 'debug':
|
|
167
|
-
return 'Debugging & Problem Solving';
|
|
168
|
-
case 'exploration':
|
|
169
|
-
return 'Analysis & Understanding';
|
|
170
|
-
case 'planning':
|
|
171
|
-
return 'Planning & Design';
|
|
172
|
-
default:
|
|
173
|
-
return 'General Task';
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Input interceptor for smart routing
|
|
179
|
-
*/
|
|
180
173
|
pi.on("input", async (event, ctx) => {
|
|
181
|
-
// Skip if routing is disabled or this is an extension-generated message
|
|
182
174
|
if (!config.enabled || event.source === "extension") {
|
|
183
175
|
return { action: "continue" };
|
|
184
176
|
}
|
|
185
177
|
|
|
186
|
-
|
|
187
|
-
const analysis = analyzeTaskType(event.text);
|
|
188
|
-
|
|
189
|
-
// Only recommend if we have good confidence (>= 30%)
|
|
190
|
-
if (analysis.type && analysis.confidence >= 30) {
|
|
191
|
-
const recommendedModel = getRecommendedModel(analysis.type);
|
|
192
|
-
const taskDescription = getTaskDescription(analysis.type);
|
|
193
|
-
|
|
194
|
-
if (recommendedModel && config.notifyOnRecommendation) {
|
|
195
|
-
const message = `๐ก Detected: ${taskDescription} (${analysis.confidence.toFixed(0)}% confidence)
|
|
196
|
-
Recommended model: ${recommendedModel}
|
|
197
|
-
Matched patterns: ${analysis.matches.join(", ")}`;
|
|
178
|
+
const result = classifyTask(event.text);
|
|
198
179
|
|
|
199
|
-
|
|
180
|
+
if (result.category && result.confidence >= 25 && result.route) {
|
|
181
|
+
if (config.notifyOnRecommendation) {
|
|
182
|
+
ctx.ui.notify(
|
|
183
|
+
`๐ ${result.route.description} โ \`${result.route.preferredModel}\` (${result.confidence}% | ${result.matches.join(", ")})`,
|
|
184
|
+
"info"
|
|
185
|
+
);
|
|
200
186
|
}
|
|
201
187
|
}
|
|
202
188
|
|
|
203
189
|
return { action: "continue" };
|
|
204
190
|
});
|
|
205
191
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
*/
|
|
192
|
+
// โโโ /routing Command โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
193
|
+
|
|
209
194
|
pi.registerCommand("routing", {
|
|
210
|
-
description: "Show or configure smart routing
|
|
195
|
+
description: "Show or configure smart routing (enable/disable/test/reload)",
|
|
211
196
|
handler: async (args, ctx) => {
|
|
212
|
-
|
|
213
|
-
// Show current configuration
|
|
214
|
-
const statusMessage = `Smart Router Configuration:
|
|
215
|
-
|
|
216
|
-
**Status:** ${config.enabled ? "Enabled" : "Disabled"}
|
|
217
|
-
**Notifications:** ${config.notifyOnRecommendation ? "Enabled" : "Disabled"}
|
|
197
|
+
const arg = args.trim().toLowerCase();
|
|
218
198
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
199
|
+
if (!arg) {
|
|
200
|
+
// Show current config
|
|
201
|
+
let output = `**๐ Smart Router**\n\n`;
|
|
202
|
+
output += `Status: ${config.enabled ? "โ
Enabled" : "โ Disabled"}\n`;
|
|
203
|
+
output += `Notifications: ${config.notifyOnRecommendation ? "On" : "Off"}\n\n`;
|
|
224
204
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
205
|
+
output += `**Routes:**\n`;
|
|
206
|
+
for (const [cat, route] of Object.entries(config.routing.routes)) {
|
|
207
|
+
output += ` **${cat}** โ \`${route.preferredModel}\` (fallback: \`${route.fallback}\`) [agent: ${route.agent}]\n`;
|
|
208
|
+
output += ` Keywords: ${route.keywords.slice(0, 6).join(", ")}${route.keywords.length > 6 ? "..." : ""}\n`;
|
|
209
|
+
}
|
|
210
|
+
output += `\n **default** โ \`${config.routing.default.model}\`\n`;
|
|
230
211
|
|
|
231
|
-
|
|
212
|
+
output += `\nConfig: \`${configPath}\``;
|
|
213
|
+
output += `\nCommands: \`/routing enable|disable|notify-on|notify-off|reload|test\``;
|
|
232
214
|
|
|
233
|
-
ctx.ui.notify(
|
|
215
|
+
ctx.ui.notify(output, "info");
|
|
234
216
|
return;
|
|
235
217
|
}
|
|
236
218
|
|
|
237
|
-
const arg = args.trim().toLowerCase();
|
|
238
|
-
|
|
239
219
|
switch (arg) {
|
|
240
220
|
case "enable":
|
|
241
221
|
config.enabled = true;
|
|
242
|
-
ctx.ui.notify("Smart routing enabled", "info");
|
|
222
|
+
ctx.ui.notify("โ
Smart routing enabled.", "info");
|
|
243
223
|
break;
|
|
244
224
|
case "disable":
|
|
245
225
|
config.enabled = false;
|
|
246
|
-
ctx.ui.notify("Smart routing disabled", "info");
|
|
226
|
+
ctx.ui.notify("โ Smart routing disabled.", "info");
|
|
247
227
|
break;
|
|
248
228
|
case "notify-on":
|
|
249
229
|
config.notifyOnRecommendation = true;
|
|
250
|
-
ctx.ui.notify("Routing notifications enabled", "info");
|
|
230
|
+
ctx.ui.notify("๐ Routing notifications enabled.", "info");
|
|
251
231
|
break;
|
|
252
232
|
case "notify-off":
|
|
253
233
|
config.notifyOnRecommendation = false;
|
|
254
|
-
ctx.ui.notify("Routing notifications disabled", "info");
|
|
234
|
+
ctx.ui.notify("๐ Routing notifications disabled.", "info");
|
|
255
235
|
break;
|
|
256
236
|
case "reload":
|
|
257
237
|
await loadConfig();
|
|
258
|
-
ctx.ui.notify("Routing
|
|
238
|
+
ctx.ui.notify("๐ Routing config reloaded from disk.", "info");
|
|
259
239
|
break;
|
|
260
|
-
case "test":
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
"
|
|
264
|
-
"
|
|
265
|
-
"
|
|
266
|
-
"
|
|
240
|
+
case "test": {
|
|
241
|
+
const tests = [
|
|
242
|
+
"implement a new user authentication system",
|
|
243
|
+
"fix the crash when uploading files larger than 10MB",
|
|
244
|
+
"explain how the middleware chain works",
|
|
245
|
+
"plan the migration from REST to GraphQL",
|
|
246
|
+
"run all unit tests and check coverage",
|
|
247
|
+
"review the PR for security vulnerabilities",
|
|
248
|
+
"what time is it",
|
|
267
249
|
];
|
|
268
|
-
|
|
269
|
-
let
|
|
270
|
-
for (const input of
|
|
271
|
-
const
|
|
272
|
-
const model =
|
|
273
|
-
|
|
250
|
+
|
|
251
|
+
let output = "**๐งช Routing Test:**\n\n";
|
|
252
|
+
for (const input of tests) {
|
|
253
|
+
const result = classifyTask(input);
|
|
254
|
+
const model = result.route?.preferredModel || config.routing.default.model;
|
|
255
|
+
const tag = result.category || "default";
|
|
256
|
+
output += `"${input}"\n โ **${tag}** (${result.confidence}%) โ \`${model}\`\n\n`;
|
|
274
257
|
}
|
|
275
|
-
|
|
276
|
-
ctx.ui.notify(testResults, "info");
|
|
258
|
+
ctx.ui.notify(output, "info");
|
|
277
259
|
break;
|
|
260
|
+
}
|
|
278
261
|
default:
|
|
279
|
-
ctx.ui.notify("Usage:
|
|
262
|
+
ctx.ui.notify("Usage: `/routing [enable|disable|notify-on|notify-off|reload|test]`", "warning");
|
|
280
263
|
}
|
|
281
264
|
},
|
|
282
265
|
});
|
|
283
266
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
*/
|
|
267
|
+
// โโโ Session Start โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
268
|
+
|
|
287
269
|
pi.on("session_start", async (_event, _ctx) => {
|
|
288
270
|
await loadConfig();
|
|
289
271
|
});
|
|
290
|
-
}
|
|
272
|
+
}
|