@intent-systems/nexus 2026.1.5-3 → 2026.1.5-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.
Files changed (39) hide show
  1. package/dist/capabilities/detector.js +214 -0
  2. package/dist/capabilities/registry.js +98 -0
  3. package/dist/channels/location.js +44 -0
  4. package/dist/channels/web/index.js +2 -0
  5. package/dist/control-plane/broker/broker.js +969 -0
  6. package/dist/control-plane/compaction.js +284 -0
  7. package/dist/control-plane/factory.js +31 -0
  8. package/dist/control-plane/index.js +10 -0
  9. package/dist/control-plane/odu/agents.js +187 -0
  10. package/dist/control-plane/odu/interaction-tools.js +196 -0
  11. package/dist/control-plane/odu/prompt-loader.js +95 -0
  12. package/dist/control-plane/odu/runtime.js +467 -0
  13. package/dist/control-plane/odu/types.js +6 -0
  14. package/dist/control-plane/odu-control-plane.js +314 -0
  15. package/dist/control-plane/single-agent.js +249 -0
  16. package/dist/control-plane/types.js +11 -0
  17. package/dist/credentials/store.js +323 -0
  18. package/dist/logging/redact.js +109 -0
  19. package/dist/markdown/fences.js +58 -0
  20. package/dist/memory/embeddings.js +146 -0
  21. package/dist/memory/index.js +382 -0
  22. package/dist/memory/internal.js +163 -0
  23. package/dist/pairing/pairing-store.js +194 -0
  24. package/dist/plugins/cli.js +42 -0
  25. package/dist/plugins/discovery.js +253 -0
  26. package/dist/plugins/install.js +181 -0
  27. package/dist/plugins/loader.js +290 -0
  28. package/dist/plugins/registry.js +105 -0
  29. package/dist/plugins/status.js +29 -0
  30. package/dist/plugins/tools.js +39 -0
  31. package/dist/plugins/types.js +1 -0
  32. package/dist/routing/resolve-route.js +144 -0
  33. package/dist/routing/session-key.js +63 -0
  34. package/dist/utils/provider-utils.js +28 -0
  35. package/package.json +4 -29
  36. package/patches/@mariozechner__pi-ai.patch +215 -0
  37. package/patches/playwright-core@1.57.0.patch +13 -0
  38. package/patches/qrcode-terminal.patch +12 -0
  39. package/scripts/postinstall.js +202 -0
@@ -0,0 +1,290 @@
1
+ import { createJiti } from "jiti";
2
+ import { createSubsystemLogger } from "../logging.js";
3
+ import { resolveUserPath } from "../utils.js";
4
+ import { discoverNexusPlugins } from "./discovery.js";
5
+ import { createPluginRegistry, } from "./registry.js";
6
+ const registryCache = new Map();
7
+ const defaultLogger = () => createSubsystemLogger("plugins");
8
+ const normalizeList = (value) => {
9
+ if (!Array.isArray(value))
10
+ return [];
11
+ return value
12
+ .map((entry) => (typeof entry === "string" ? entry.trim() : ""))
13
+ .filter(Boolean);
14
+ };
15
+ const normalizePluginEntries = (entries) => {
16
+ if (!entries || typeof entries !== "object" || Array.isArray(entries)) {
17
+ return {};
18
+ }
19
+ const normalized = {};
20
+ for (const [key, value] of Object.entries(entries)) {
21
+ if (!key.trim())
22
+ continue;
23
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
24
+ normalized[key] = {};
25
+ continue;
26
+ }
27
+ const entry = value;
28
+ normalized[key] = {
29
+ enabled: typeof entry.enabled === "boolean" ? entry.enabled : undefined,
30
+ config: entry.config &&
31
+ typeof entry.config === "object" &&
32
+ !Array.isArray(entry.config)
33
+ ? entry.config
34
+ : undefined,
35
+ };
36
+ }
37
+ return normalized;
38
+ };
39
+ const normalizePluginsConfig = (config) => {
40
+ return {
41
+ enabled: config?.enabled !== false,
42
+ allow: normalizeList(config?.allow),
43
+ deny: normalizeList(config?.deny),
44
+ loadPaths: normalizeList(config?.load?.paths),
45
+ entries: normalizePluginEntries(config?.entries),
46
+ };
47
+ };
48
+ function buildCacheKey(params) {
49
+ const workspaceKey = params.workspaceDir
50
+ ? resolveUserPath(params.workspaceDir)
51
+ : "";
52
+ return `${workspaceKey}::${JSON.stringify(params.plugins)}`;
53
+ }
54
+ function resolveEnableState(id, config) {
55
+ if (!config.enabled) {
56
+ return { enabled: false, reason: "plugins disabled" };
57
+ }
58
+ if (config.deny.includes(id)) {
59
+ return { enabled: false, reason: "blocked by denylist" };
60
+ }
61
+ if (config.allow.length > 0 && !config.allow.includes(id)) {
62
+ return { enabled: false, reason: "not in allowlist" };
63
+ }
64
+ const entry = config.entries[id];
65
+ if (entry?.enabled === false) {
66
+ return { enabled: false, reason: "disabled in config" };
67
+ }
68
+ return { enabled: true };
69
+ }
70
+ function validatePluginConfig(params) {
71
+ const schema = params.schema;
72
+ if (!schema)
73
+ return { ok: true, value: params.value };
74
+ if (typeof schema.validate === "function") {
75
+ const result = schema.validate(params.value);
76
+ if (result.ok) {
77
+ return { ok: true, value: result.value };
78
+ }
79
+ return { ok: false, errors: result.errors };
80
+ }
81
+ if (typeof schema.safeParse === "function") {
82
+ const result = schema.safeParse(params.value);
83
+ if (result.success) {
84
+ return { ok: true, value: result.data };
85
+ }
86
+ const issues = result.error?.issues ?? [];
87
+ const errors = issues.map((issue) => {
88
+ const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
89
+ return `${path}: ${issue.message}`;
90
+ });
91
+ return { ok: false, errors };
92
+ }
93
+ if (typeof schema.parse === "function") {
94
+ try {
95
+ const parsed = schema.parse(params.value);
96
+ return { ok: true, value: parsed };
97
+ }
98
+ catch (err) {
99
+ return { ok: false, errors: [String(err)] };
100
+ }
101
+ }
102
+ return { ok: true, value: params.value };
103
+ }
104
+ function resolvePluginModuleExport(moduleExport) {
105
+ const resolved = moduleExport &&
106
+ typeof moduleExport === "object" &&
107
+ "default" in moduleExport
108
+ ? moduleExport.default
109
+ : moduleExport;
110
+ if (typeof resolved === "function") {
111
+ return {
112
+ register: resolved,
113
+ };
114
+ }
115
+ if (resolved && typeof resolved === "object") {
116
+ const def = resolved;
117
+ const register = def.register ?? def.activate;
118
+ return { definition: def, register };
119
+ }
120
+ return {};
121
+ }
122
+ function createPluginRecord(params) {
123
+ return {
124
+ id: params.id,
125
+ name: params.name ?? params.id,
126
+ description: params.description,
127
+ version: params.version,
128
+ source: params.source,
129
+ origin: params.origin,
130
+ workspaceDir: params.workspaceDir,
131
+ enabled: params.enabled,
132
+ status: params.enabled ? "loaded" : "disabled",
133
+ toolNames: [],
134
+ gatewayMethods: [],
135
+ cliCommands: [],
136
+ services: [],
137
+ configSchema: params.configSchema,
138
+ configUiHints: undefined,
139
+ };
140
+ }
141
+ function pushDiagnostics(diagnostics, append) {
142
+ diagnostics.push(...append);
143
+ }
144
+ export function loadNexusPlugins(options = {}) {
145
+ const cfg = options.config ?? {};
146
+ const logger = options.logger ?? defaultLogger();
147
+ const normalized = normalizePluginsConfig(cfg.plugins);
148
+ const cacheKey = buildCacheKey({
149
+ workspaceDir: options.workspaceDir,
150
+ plugins: normalized,
151
+ });
152
+ const cacheEnabled = options.cache !== false;
153
+ if (cacheEnabled) {
154
+ const cached = registryCache.get(cacheKey);
155
+ if (cached)
156
+ return cached;
157
+ }
158
+ const { registry, createApi } = createPluginRegistry({
159
+ logger,
160
+ coreGatewayHandlers: options.coreGatewayHandlers,
161
+ });
162
+ const discovery = discoverNexusPlugins({
163
+ workspaceDir: options.workspaceDir,
164
+ extraPaths: normalized.loadPaths,
165
+ });
166
+ pushDiagnostics(registry.diagnostics, discovery.diagnostics);
167
+ const jiti = createJiti(import.meta.url, {
168
+ interopDefault: true,
169
+ });
170
+ for (const candidate of discovery.candidates) {
171
+ const enableState = resolveEnableState(candidate.idHint, normalized);
172
+ const entry = normalized.entries[candidate.idHint];
173
+ const record = createPluginRecord({
174
+ id: candidate.idHint,
175
+ name: candidate.packageName ?? candidate.idHint,
176
+ description: candidate.packageDescription,
177
+ version: candidate.packageVersion,
178
+ source: candidate.source,
179
+ origin: candidate.origin,
180
+ workspaceDir: candidate.workspaceDir,
181
+ enabled: enableState.enabled,
182
+ configSchema: false,
183
+ });
184
+ if (!enableState.enabled) {
185
+ record.status = "disabled";
186
+ record.error = enableState.reason;
187
+ registry.plugins.push(record);
188
+ continue;
189
+ }
190
+ let mod = null;
191
+ try {
192
+ mod = jiti(candidate.source);
193
+ }
194
+ catch (err) {
195
+ record.status = "error";
196
+ record.error = String(err);
197
+ registry.plugins.push(record);
198
+ registry.diagnostics.push({
199
+ level: "error",
200
+ pluginId: record.id,
201
+ source: record.source,
202
+ message: `failed to load plugin: ${String(err)}`,
203
+ });
204
+ continue;
205
+ }
206
+ const resolved = resolvePluginModuleExport(mod);
207
+ const definition = resolved.definition;
208
+ const register = resolved.register;
209
+ if (definition?.id && definition.id !== record.id) {
210
+ registry.diagnostics.push({
211
+ level: "warn",
212
+ pluginId: record.id,
213
+ source: record.source,
214
+ message: `plugin id mismatch (config uses "${record.id}", export uses "${definition.id}")`,
215
+ });
216
+ }
217
+ record.name = definition?.name ?? record.name;
218
+ record.description = definition?.description ?? record.description;
219
+ record.version = definition?.version ?? record.version;
220
+ record.configSchema = Boolean(definition?.configSchema);
221
+ record.configUiHints =
222
+ definition?.configSchema &&
223
+ typeof definition.configSchema === "object" &&
224
+ definition.configSchema.uiHints &&
225
+ typeof definition.configSchema.uiHints ===
226
+ "object" &&
227
+ !Array.isArray(definition.configSchema.uiHints)
228
+ ? definition.configSchema.uiHints
229
+ : undefined;
230
+ const validatedConfig = validatePluginConfig({
231
+ schema: definition?.configSchema,
232
+ value: entry?.config,
233
+ });
234
+ if (!validatedConfig.ok) {
235
+ record.status = "error";
236
+ record.error = `invalid config: ${validatedConfig.errors?.join(", ")}`;
237
+ registry.plugins.push(record);
238
+ registry.diagnostics.push({
239
+ level: "error",
240
+ pluginId: record.id,
241
+ source: record.source,
242
+ message: record.error,
243
+ });
244
+ continue;
245
+ }
246
+ if (typeof register !== "function") {
247
+ record.status = "error";
248
+ record.error = "plugin export missing register/activate";
249
+ registry.plugins.push(record);
250
+ registry.diagnostics.push({
251
+ level: "error",
252
+ pluginId: record.id,
253
+ source: record.source,
254
+ message: record.error,
255
+ });
256
+ continue;
257
+ }
258
+ const api = createApi(record, {
259
+ config: cfg,
260
+ pluginConfig: validatedConfig.value,
261
+ });
262
+ try {
263
+ const result = register(api);
264
+ if (result && typeof result.then === "function") {
265
+ registry.diagnostics.push({
266
+ level: "warn",
267
+ pluginId: record.id,
268
+ source: record.source,
269
+ message: "plugin register returned a promise; async registration is ignored",
270
+ });
271
+ }
272
+ registry.plugins.push(record);
273
+ }
274
+ catch (err) {
275
+ record.status = "error";
276
+ record.error = String(err);
277
+ registry.plugins.push(record);
278
+ registry.diagnostics.push({
279
+ level: "error",
280
+ pluginId: record.id,
281
+ source: record.source,
282
+ message: `plugin failed during register: ${String(err)}`,
283
+ });
284
+ }
285
+ }
286
+ if (cacheEnabled) {
287
+ registryCache.set(cacheKey, registry);
288
+ }
289
+ return registry;
290
+ }
@@ -0,0 +1,105 @@
1
+ import { resolveUserPath } from "../utils.js";
2
+ export function createPluginRegistry(registryParams) {
3
+ const registry = {
4
+ plugins: [],
5
+ tools: [],
6
+ gatewayHandlers: {},
7
+ cliRegistrars: [],
8
+ services: [],
9
+ diagnostics: [],
10
+ };
11
+ const coreGatewayMethods = new Set(Object.keys(registryParams.coreGatewayHandlers ?? {}));
12
+ const pushDiagnostic = (diag) => {
13
+ registry.diagnostics.push(diag);
14
+ };
15
+ const registerTool = (record, tool, opts) => {
16
+ const names = opts?.names ?? (opts?.name ? [opts.name] : []);
17
+ const factory = typeof tool === "function"
18
+ ? tool
19
+ : (_ctx) => tool;
20
+ if (typeof tool !== "function") {
21
+ names.push(tool.name);
22
+ }
23
+ const normalized = names.map((name) => name.trim()).filter(Boolean);
24
+ if (normalized.length > 0) {
25
+ record.toolNames.push(...normalized);
26
+ }
27
+ registry.tools.push({
28
+ pluginId: record.id,
29
+ factory,
30
+ names: normalized,
31
+ source: record.source,
32
+ });
33
+ };
34
+ const registerGatewayMethod = (record, method, handler) => {
35
+ const trimmed = method.trim();
36
+ if (!trimmed)
37
+ return;
38
+ if (coreGatewayMethods.has(trimmed) || registry.gatewayHandlers[trimmed]) {
39
+ pushDiagnostic({
40
+ level: "error",
41
+ pluginId: record.id,
42
+ source: record.source,
43
+ message: `gateway method already registered: ${trimmed}`,
44
+ });
45
+ return;
46
+ }
47
+ registry.gatewayHandlers[trimmed] = handler;
48
+ record.gatewayMethods.push(trimmed);
49
+ };
50
+ const registerCli = (record, registrar, opts) => {
51
+ const commands = (opts?.commands ?? [])
52
+ .map((cmd) => cmd.trim())
53
+ .filter(Boolean);
54
+ record.cliCommands.push(...commands);
55
+ registry.cliRegistrars.push({
56
+ pluginId: record.id,
57
+ register: registrar,
58
+ commands,
59
+ source: record.source,
60
+ });
61
+ };
62
+ const registerService = (record, service) => {
63
+ const id = service.id.trim();
64
+ if (!id)
65
+ return;
66
+ record.services.push(id);
67
+ registry.services.push({
68
+ pluginId: record.id,
69
+ service,
70
+ source: record.source,
71
+ });
72
+ };
73
+ const normalizeLogger = (logger) => ({
74
+ info: logger.info,
75
+ warn: logger.warn,
76
+ error: logger.error,
77
+ debug: logger.debug,
78
+ });
79
+ const createApi = (record, params) => {
80
+ return {
81
+ id: record.id,
82
+ name: record.name,
83
+ version: record.version,
84
+ description: record.description,
85
+ source: record.source,
86
+ config: params.config,
87
+ pluginConfig: params.pluginConfig,
88
+ logger: normalizeLogger(registryParams.logger),
89
+ registerTool: (tool, opts) => registerTool(record, tool, opts),
90
+ registerGatewayMethod: (method, handler) => registerGatewayMethod(record, method, handler),
91
+ registerCli: (registrar, opts) => registerCli(record, registrar, opts),
92
+ registerService: (service) => registerService(record, service),
93
+ resolvePath: (input) => resolveUserPath(input),
94
+ };
95
+ };
96
+ return {
97
+ registry,
98
+ createApi,
99
+ pushDiagnostic,
100
+ registerTool,
101
+ registerGatewayMethod,
102
+ registerCli,
103
+ registerService,
104
+ };
105
+ }
@@ -0,0 +1,29 @@
1
+ import { resolveAgentWorkspaceDir } from "../agents/agent-scope.js";
2
+ import { loadConfig } from "../config/config.js";
3
+ import { createSubsystemLogger } from "../logging.js";
4
+ import { DEFAULT_AGENT_ID, normalizeAgentId } from "../routing/session-key.js";
5
+ import { loadNexusPlugins } from "./loader.js";
6
+ const log = createSubsystemLogger("plugins");
7
+ function resolveDefaultAgentId(cfg) {
8
+ return normalizeAgentId(cfg.routing?.defaultAgentId ?? DEFAULT_AGENT_ID);
9
+ }
10
+ export function buildPluginStatusReport(params) {
11
+ const config = params?.config ?? loadConfig();
12
+ const workspaceDir = params?.workspaceDir
13
+ ? params.workspaceDir
14
+ : resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
15
+ const registry = loadNexusPlugins({
16
+ config,
17
+ workspaceDir,
18
+ logger: {
19
+ info: (msg) => log.info(msg),
20
+ warn: (msg) => log.warn(msg),
21
+ error: (msg) => log.error(msg),
22
+ debug: (msg) => log.debug(msg),
23
+ },
24
+ });
25
+ return {
26
+ workspaceDir,
27
+ ...registry,
28
+ };
29
+ }
@@ -0,0 +1,39 @@
1
+ import { createSubsystemLogger } from "../logging.js";
2
+ import { loadNexusPlugins } from "./loader.js";
3
+ const log = createSubsystemLogger("plugins");
4
+ export function resolvePluginTools(params) {
5
+ const registry = loadNexusPlugins({
6
+ config: params.context.config,
7
+ workspaceDir: params.context.workspaceDir,
8
+ logger: {
9
+ info: (msg) => log.info(msg),
10
+ warn: (msg) => log.warn(msg),
11
+ error: (msg) => log.error(msg),
12
+ debug: (msg) => log.debug(msg),
13
+ },
14
+ });
15
+ const tools = [];
16
+ const existing = params.existingToolNames ?? new Set();
17
+ for (const entry of registry.tools) {
18
+ let resolved = null;
19
+ try {
20
+ resolved = entry.factory(params.context);
21
+ }
22
+ catch (err) {
23
+ log.error(`plugin tool failed (${entry.pluginId}): ${String(err)}`);
24
+ continue;
25
+ }
26
+ if (!resolved)
27
+ continue;
28
+ const list = Array.isArray(resolved) ? resolved : [resolved];
29
+ for (const tool of list) {
30
+ if (existing.has(tool.name)) {
31
+ log.warn(`plugin tool name conflict (${entry.pluginId}): ${tool.name}`);
32
+ continue;
33
+ }
34
+ existing.add(tool.name);
35
+ tools.push(tool);
36
+ }
37
+ }
38
+ return tools;
39
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,144 @@
1
+ import { buildAgentMainSessionKey, buildAgentPeerSessionKey, DEFAULT_ACCOUNT_ID, DEFAULT_AGENT_ID, DEFAULT_MAIN_KEY, normalizeAgentId, } from "./session-key.js";
2
+ export { DEFAULT_ACCOUNT_ID, DEFAULT_AGENT_ID } from "./session-key.js";
3
+ function normalizeToken(value) {
4
+ return (value ?? "").trim().toLowerCase();
5
+ }
6
+ function normalizeId(value) {
7
+ return (value ?? "").trim();
8
+ }
9
+ function normalizeAccountId(value) {
10
+ const trimmed = (value ?? "").trim();
11
+ return trimmed ? trimmed : DEFAULT_ACCOUNT_ID;
12
+ }
13
+ function matchesAccountId(match, actual) {
14
+ const trimmed = (match ?? "").trim();
15
+ if (!trimmed)
16
+ return actual === DEFAULT_ACCOUNT_ID;
17
+ if (trimmed === "*")
18
+ return true;
19
+ return trimmed === actual;
20
+ }
21
+ export function buildAgentSessionKey(params) {
22
+ const provider = normalizeToken(params.provider) || "unknown";
23
+ const peer = params.peer;
24
+ return buildAgentPeerSessionKey({
25
+ agentId: params.agentId,
26
+ mainKey: DEFAULT_MAIN_KEY,
27
+ provider,
28
+ peerKind: peer?.kind ?? "dm",
29
+ peerId: peer ? normalizeId(peer.id) || "unknown" : null,
30
+ });
31
+ }
32
+ function listBindings(cfg) {
33
+ const bindings = cfg.routing?.bindings;
34
+ return Array.isArray(bindings) ? bindings : [];
35
+ }
36
+ function listAgents(cfg) {
37
+ const agents = cfg.routing?.agents;
38
+ return agents && typeof agents === "object" ? agents : undefined;
39
+ }
40
+ function resolveDefaultAgentId(cfg) {
41
+ const explicit = cfg.routing?.defaultAgentId?.trim();
42
+ if (explicit)
43
+ return explicit;
44
+ return DEFAULT_AGENT_ID;
45
+ }
46
+ function pickFirstExistingAgentId(cfg, agentId) {
47
+ const normalized = normalizeAgentId(agentId);
48
+ const agents = listAgents(cfg);
49
+ if (!agents)
50
+ return normalized;
51
+ if (Object.hasOwn(agents, normalized))
52
+ return normalized;
53
+ return normalizeAgentId(resolveDefaultAgentId(cfg));
54
+ }
55
+ function matchesProvider(match, provider) {
56
+ const key = normalizeToken(match?.provider);
57
+ if (!key)
58
+ return false;
59
+ return key === provider;
60
+ }
61
+ function matchesPeer(match, peer) {
62
+ const m = match?.peer;
63
+ if (!m)
64
+ return false;
65
+ const kind = normalizeToken(m.kind);
66
+ const id = normalizeId(m.id);
67
+ if (!kind || !id)
68
+ return false;
69
+ return kind === peer.kind && id === peer.id;
70
+ }
71
+ function matchesGuild(match, guildId) {
72
+ const id = normalizeId(match?.guildId);
73
+ if (!id)
74
+ return false;
75
+ return id === guildId;
76
+ }
77
+ function matchesTeam(match, teamId) {
78
+ const id = normalizeId(match?.teamId);
79
+ if (!id)
80
+ return false;
81
+ return id === teamId;
82
+ }
83
+ export function resolveAgentRoute(input) {
84
+ const provider = normalizeToken(input.provider ?? input.channel);
85
+ const accountId = normalizeAccountId(input.accountId);
86
+ const peer = input.peer
87
+ ? { kind: input.peer.kind, id: normalizeId(input.peer.id) }
88
+ : null;
89
+ const guildId = normalizeId(input.guildId);
90
+ const teamId = normalizeId(input.teamId);
91
+ const bindings = listBindings(input.cfg).filter((binding) => {
92
+ if (!binding || typeof binding !== "object")
93
+ return false;
94
+ if (!matchesProvider(binding.match, provider))
95
+ return false;
96
+ return matchesAccountId(binding.match?.accountId, accountId);
97
+ });
98
+ const choose = (agentId, matchedBy) => {
99
+ const resolvedAgentId = pickFirstExistingAgentId(input.cfg, agentId);
100
+ return {
101
+ agentId: resolvedAgentId,
102
+ provider,
103
+ accountId,
104
+ sessionKey: buildAgentSessionKey({
105
+ agentId: resolvedAgentId,
106
+ provider,
107
+ peer,
108
+ }),
109
+ mainSessionKey: buildAgentMainSessionKey({
110
+ agentId: resolvedAgentId,
111
+ mainKey: DEFAULT_MAIN_KEY,
112
+ }),
113
+ matchedBy,
114
+ };
115
+ };
116
+ if (peer) {
117
+ const peerMatch = bindings.find((b) => matchesPeer(b.match, peer));
118
+ if (peerMatch)
119
+ return choose(peerMatch.agentId, "binding.peer");
120
+ }
121
+ if (guildId) {
122
+ const guildMatch = bindings.find((b) => matchesGuild(b.match, guildId));
123
+ if (guildMatch)
124
+ return choose(guildMatch.agentId, "binding.guild");
125
+ }
126
+ if (teamId) {
127
+ const teamMatch = bindings.find((b) => matchesTeam(b.match, teamId));
128
+ if (teamMatch)
129
+ return choose(teamMatch.agentId, "binding.team");
130
+ }
131
+ const accountMatch = bindings.find((b) => b.match?.accountId?.trim() !== "*" &&
132
+ !b.match?.peer &&
133
+ !b.match?.guildId &&
134
+ !b.match?.teamId);
135
+ if (accountMatch)
136
+ return choose(accountMatch.agentId, "binding.account");
137
+ const anyAccountMatch = bindings.find((b) => b.match?.accountId?.trim() === "*" &&
138
+ !b.match?.peer &&
139
+ !b.match?.guildId &&
140
+ !b.match?.teamId);
141
+ if (anyAccountMatch)
142
+ return choose(anyAccountMatch.agentId, "binding.provider");
143
+ return choose(resolveDefaultAgentId(input.cfg), "default");
144
+ }
@@ -0,0 +1,63 @@
1
+ export const DEFAULT_AGENT_ID = "default";
2
+ export const DEFAULT_MAIN_KEY = "main";
3
+ export const DEFAULT_ACCOUNT_ID = "default";
4
+ export function normalizeAgentId(value) {
5
+ const trimmed = (value ?? "").trim();
6
+ if (!trimmed)
7
+ return DEFAULT_AGENT_ID;
8
+ // Keep it path-safe + shell-friendly.
9
+ if (/^[a-z0-9][a-z0-9_-]{0,63}$/i.test(trimmed))
10
+ return trimmed;
11
+ // Best-effort fallback: collapse invalid characters to "-"
12
+ return (trimmed
13
+ .toLowerCase()
14
+ .replace(/[^a-z0-9_-]+/g, "-")
15
+ .replace(/^-+/, "")
16
+ .replace(/-+$/, "")
17
+ .slice(0, 64) || DEFAULT_AGENT_ID);
18
+ }
19
+ export function parseAgentSessionKey(sessionKey) {
20
+ const raw = (sessionKey ?? "").trim();
21
+ if (!raw)
22
+ return null;
23
+ const parts = raw.split(":").filter(Boolean);
24
+ if (parts.length < 3)
25
+ return null;
26
+ if (parts[0] !== "agent")
27
+ return null;
28
+ const agentId = parts[1]?.trim();
29
+ const rest = parts.slice(2).join(":");
30
+ if (!agentId || !rest)
31
+ return null;
32
+ return { agentId, rest };
33
+ }
34
+ export function resolveAgentIdFromSessionKey(sessionKey) {
35
+ const parsed = parseAgentSessionKey(sessionKey);
36
+ return normalizeAgentId(parsed?.agentId ?? DEFAULT_AGENT_ID);
37
+ }
38
+ export function isSubagentSessionKey(sessionKey) {
39
+ const raw = (sessionKey ?? "").trim();
40
+ if (!raw)
41
+ return false;
42
+ if (raw.toLowerCase().startsWith("subagent:"))
43
+ return true;
44
+ const parsed = parseAgentSessionKey(raw);
45
+ return Boolean((parsed?.rest ?? "").toLowerCase().startsWith("subagent:"));
46
+ }
47
+ export function buildAgentMainSessionKey(params) {
48
+ const agentId = normalizeAgentId(params.agentId);
49
+ const mainKey = (params.mainKey ?? DEFAULT_MAIN_KEY).trim() || DEFAULT_MAIN_KEY;
50
+ return `agent:${agentId}:${mainKey}`;
51
+ }
52
+ export function buildAgentPeerSessionKey(params) {
53
+ const peerKind = params.peerKind ?? "dm";
54
+ if (peerKind === "dm") {
55
+ return buildAgentMainSessionKey({
56
+ agentId: params.agentId,
57
+ mainKey: params.mainKey,
58
+ });
59
+ }
60
+ const provider = (params.provider ?? "").trim().toLowerCase() || "unknown";
61
+ const peerId = (params.peerId ?? "").trim() || "unknown";
62
+ return `agent:${normalizeAgentId(params.agentId)}:${provider}:${peerKind}:${peerId}`;
63
+ }