@rubytech/create-maxy 1.0.477 → 1.0.479
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/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +34 -6
- package/payload/platform/plugins/admin/PLUGIN.md +4 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js +221 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/anthropic/PLUGIN.md +3 -2
- package/payload/platform/plugins/anthropic/references/console-api.md +186 -0
- package/payload/platform/plugins/anthropic/references/setup-guide.md +3 -3
- package/payload/platform/plugins/anthropic/skills/get-api-key/SKILL.md +89 -20
- package/payload/platform/plugins/memory/mcp/dist/index.js +13 -2
- package/payload/platform/plugins/memory/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js +3 -3
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-delete.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js +3 -2
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-read.js.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts +1 -0
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.d.ts.map +1 -1
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js +14 -11
- package/payload/platform/plugins/memory/mcp/dist/tools/profile-update.js.map +1 -1
- package/payload/platform/scripts/seed-neo4j.sh +101 -0
- package/payload/platform/templates/account.json +2 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +10 -0
- package/payload/server/public/assets/ChatInput-D_Am-NZI.css +1 -0
- package/payload/server/public/assets/{admin-DpmnCxNk.js → admin-ZfkvBfx_.js} +60 -60
- package/payload/server/public/assets/{public-BBxDqQvQ.js → public-C0LOoku3.js} +1 -1
- package/payload/server/public/index.html +3 -3
- package/payload/server/public/public.html +3 -3
- package/payload/server/server.js +252 -126
- package/payload/server/public/assets/ChatInput-sDYraTun.css +0 -1
- /package/payload/server/public/assets/{ChatInput-DZ0j0Gdp.js → ChatInput-B2MUVSm4.js} +0 -0
package/payload/server/server.js
CHANGED
|
@@ -2877,6 +2877,8 @@ if (platformRoot) {
|
|
|
2877
2877
|
}
|
|
2878
2878
|
var MAXY_DIR = resolve(homedir(), configDirName);
|
|
2879
2879
|
var PIN_FILE = resolve(MAXY_DIR, ".admin-pin");
|
|
2880
|
+
var PLATFORM_ROOT = process.env.MAXY_PLATFORM_ROOT ?? resolve(process.cwd(), "..");
|
|
2881
|
+
var USERS_FILE = resolve(PLATFORM_ROOT, "config", "users.json");
|
|
2880
2882
|
var LOG_DIR = resolve(MAXY_DIR, "logs");
|
|
2881
2883
|
var BIN_DIR = resolve(MAXY_DIR, "bin");
|
|
2882
2884
|
var API_KEY_FILE = resolve(MAXY_DIR, ".anthropic-api-key");
|
|
@@ -3144,8 +3146,8 @@ import { spawnSync, execFileSync } from "child_process";
|
|
|
3144
3146
|
import { createConnection as createConnection2 } from "net";
|
|
3145
3147
|
import { mkdirSync, writeFileSync as writeFileSync2 } from "fs";
|
|
3146
3148
|
import { resolve as resolve2 } from "path";
|
|
3147
|
-
var
|
|
3148
|
-
var VNC_SCRIPT = resolve2(
|
|
3149
|
+
var PLATFORM_ROOT2 = process.env.MAXY_PLATFORM_ROOT ?? resolve2(process.cwd(), "..");
|
|
3150
|
+
var VNC_SCRIPT = resolve2(PLATFORM_ROOT2, "scripts/vnc.sh");
|
|
3149
3151
|
function sleep(ms) {
|
|
3150
3152
|
return new Promise((r) => setTimeout(r, ms));
|
|
3151
3153
|
}
|
|
@@ -3277,11 +3279,11 @@ import { randomUUID } from "crypto";
|
|
|
3277
3279
|
import { spawn } from "child_process";
|
|
3278
3280
|
import { readFileSync as readFileSync4 } from "fs";
|
|
3279
3281
|
import { resolve as resolve3 } from "path";
|
|
3280
|
-
var
|
|
3282
|
+
var PLATFORM_ROOT3 = process.env.MAXY_PLATFORM_ROOT ?? resolve3(process.cwd(), "..");
|
|
3281
3283
|
var driver = null;
|
|
3282
3284
|
function readPassword() {
|
|
3283
3285
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
3284
|
-
const passwordFile = resolve3(
|
|
3286
|
+
const passwordFile = resolve3(PLATFORM_ROOT3, "config/.neo4j-password");
|
|
3285
3287
|
try {
|
|
3286
3288
|
return readFileSync4(passwordFile, "utf-8").trim();
|
|
3287
3289
|
} catch {
|
|
@@ -3340,7 +3342,7 @@ function cacheConversationId(sessionKey, conversationId) {
|
|
|
3340
3342
|
}
|
|
3341
3343
|
}
|
|
3342
3344
|
var GREETING_DIRECTIVE = "[New session. Greet the visitor.]";
|
|
3343
|
-
async function ensureConversation(accountId, agentType, sessionKey, visitorId, agentSlug) {
|
|
3345
|
+
async function ensureConversation(accountId, agentType, sessionKey, visitorId, agentSlug, userId) {
|
|
3344
3346
|
const cached2 = getCachedConversationId(sessionKey);
|
|
3345
3347
|
if (cached2) return cached2;
|
|
3346
3348
|
const conversationId = randomUUID();
|
|
@@ -3354,6 +3356,7 @@ async function ensureConversation(accountId, agentType, sessionKey, visitorId, a
|
|
|
3354
3356
|
c.agentType = $agentType,
|
|
3355
3357
|
${visitorId ? "c.visitorId = $visitorId," : ""}
|
|
3356
3358
|
${agentSlug ? "c.agentSlug = $agentSlug," : ""}
|
|
3359
|
+
${userId ? "c.userId = $userId," : ""}
|
|
3357
3360
|
c.createdAt = datetime(),
|
|
3358
3361
|
c.updatedAt = datetime()
|
|
3359
3362
|
ON MATCH SET
|
|
@@ -3370,13 +3373,14 @@ async function ensureConversation(accountId, agentType, sessionKey, visitorId, a
|
|
|
3370
3373
|
accountId,
|
|
3371
3374
|
agentType,
|
|
3372
3375
|
...visitorId ? { visitorId } : {},
|
|
3373
|
-
...agentSlug ? { agentSlug } : {}
|
|
3376
|
+
...agentSlug ? { agentSlug } : {},
|
|
3377
|
+
...userId ? { userId } : {}
|
|
3374
3378
|
}
|
|
3375
3379
|
);
|
|
3376
3380
|
const id = result.records[0]?.get("conversationId");
|
|
3377
3381
|
if (id) {
|
|
3378
3382
|
cacheConversationId(sessionKey, id);
|
|
3379
|
-
console.error(`[
|
|
3383
|
+
console.error(`[session] conversation attributed: conversationId=${id.slice(0, 8)}\u2026 userId=${userId ?? "none"} ${agentType}/${accountId.slice(0, 8)}\u2026`);
|
|
3380
3384
|
}
|
|
3381
3385
|
return id ?? null;
|
|
3382
3386
|
} catch (err) {
|
|
@@ -3582,17 +3586,18 @@ async function searchMessages(accountId, queryEmbedding, limit = 10) {
|
|
|
3582
3586
|
await session.close();
|
|
3583
3587
|
}
|
|
3584
3588
|
}
|
|
3585
|
-
async function listAdminSessions(accountId, limit = 20) {
|
|
3589
|
+
async function listAdminSessions(accountId, userId, limit = 20) {
|
|
3586
3590
|
const session = getSession();
|
|
3587
3591
|
try {
|
|
3588
3592
|
const result = await session.run(
|
|
3589
3593
|
`MATCH (c:Conversation {accountId: $accountId, agentType: 'admin'})
|
|
3594
|
+
WHERE c.userId = $userId OR c.userId IS NULL
|
|
3590
3595
|
RETURN c.conversationId AS conversationId,
|
|
3591
3596
|
c.name AS name,
|
|
3592
3597
|
c.updatedAt AS updatedAt
|
|
3593
3598
|
ORDER BY c.updatedAt DESC
|
|
3594
3599
|
LIMIT $limit`,
|
|
3595
|
-
{ accountId, limit: neo4j.int(limit) }
|
|
3600
|
+
{ accountId, userId, limit: neo4j.int(limit) }
|
|
3596
3601
|
);
|
|
3597
3602
|
return result.records.map((r) => ({
|
|
3598
3603
|
conversationId: r.get("conversationId"),
|
|
@@ -3800,14 +3805,13 @@ var VALID_CATEGORIES = /* @__PURE__ */ new Set([
|
|
|
3800
3805
|
"content",
|
|
3801
3806
|
"interaction"
|
|
3802
3807
|
]);
|
|
3803
|
-
async function getUserTimezone(accountId) {
|
|
3808
|
+
async function getUserTimezone(accountId, userId) {
|
|
3804
3809
|
const session = getSession();
|
|
3805
3810
|
try {
|
|
3806
|
-
const
|
|
3807
|
-
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
);
|
|
3811
|
+
const query = userId ? `MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
3812
|
+
RETURN up.timezone AS timezone` : `MATCH (up:UserProfile {accountId: $accountId})
|
|
3813
|
+
RETURN up.timezone AS timezone LIMIT 1`;
|
|
3814
|
+
const result = await session.run(query, { accountId, userId: userId ?? "" });
|
|
3811
3815
|
if (result.records.length === 0) return null;
|
|
3812
3816
|
const tz = result.records[0].get("timezone");
|
|
3813
3817
|
return tz && tz.trim().length > 0 ? tz : null;
|
|
@@ -3818,11 +3822,11 @@ async function getUserTimezone(accountId) {
|
|
|
3818
3822
|
await session.close();
|
|
3819
3823
|
}
|
|
3820
3824
|
}
|
|
3821
|
-
async function loadUserProfile(accountId) {
|
|
3825
|
+
async function loadUserProfile(accountId, userId) {
|
|
3822
3826
|
const session = getSession();
|
|
3823
3827
|
try {
|
|
3824
|
-
await session.run(
|
|
3825
|
-
`MERGE (up:UserProfile {accountId: $accountId})
|
|
3828
|
+
const mergeResult = await session.run(
|
|
3829
|
+
`MERGE (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
3826
3830
|
ON CREATE SET
|
|
3827
3831
|
up.createdAt = $now,
|
|
3828
3832
|
up.updatedAt = $now,
|
|
@@ -3832,18 +3836,23 @@ async function loadUserProfile(accountId) {
|
|
|
3832
3836
|
OPTIONAL MATCH (b:LocalBusiness {accountId: $accountId})
|
|
3833
3837
|
FOREACH (_ IN CASE WHEN b IS NOT NULL THEN [1] ELSE [] END |
|
|
3834
3838
|
MERGE (up)-[:BELONGS_TO]->(b)
|
|
3835
|
-
)
|
|
3836
|
-
|
|
3839
|
+
)
|
|
3840
|
+
RETURN up.createdAt = $now AS isNew`,
|
|
3841
|
+
{ accountId, userId, now: (/* @__PURE__ */ new Date()).toISOString() }
|
|
3837
3842
|
);
|
|
3843
|
+
const isNew = mergeResult.records[0]?.get("isNew");
|
|
3844
|
+
if (isNew) {
|
|
3845
|
+
console.error(`[profile] created new profile: userId=${userId} accountId=${accountId.slice(0, 8)}\u2026`);
|
|
3846
|
+
}
|
|
3838
3847
|
const nowMs = Date.now();
|
|
3839
3848
|
const thresholdMs = DECAY_THRESHOLD_DAYS * 24 * 60 * 60 * 1e3;
|
|
3840
3849
|
const staleResult = await session.run(
|
|
3841
|
-
`MATCH (up:UserProfile {accountId: $accountId})-[:HAS_PREFERENCE]->(pref:Preference)
|
|
3850
|
+
`MATCH (up:UserProfile {accountId: $accountId, userId: $userId})-[:HAS_PREFERENCE]->(pref:Preference)
|
|
3842
3851
|
WHERE pref.observedAt IS NOT NULL
|
|
3843
3852
|
RETURN pref.preferenceId AS preferenceId,
|
|
3844
3853
|
pref.observedAt AS observedAt,
|
|
3845
3854
|
pref.confidence AS confidence`,
|
|
3846
|
-
{ accountId }
|
|
3855
|
+
{ accountId, userId }
|
|
3847
3856
|
);
|
|
3848
3857
|
let decayCount = 0;
|
|
3849
3858
|
for (const record2 of staleResult.records) {
|
|
@@ -3869,7 +3878,7 @@ async function loadUserProfile(accountId) {
|
|
|
3869
3878
|
}
|
|
3870
3879
|
}
|
|
3871
3880
|
const result = await session.run(
|
|
3872
|
-
`MATCH (up:UserProfile {accountId: $accountId})
|
|
3881
|
+
`MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
3873
3882
|
OPTIONAL MATCH (up)-[:HAS_PREFERENCE]->(pref:Preference)
|
|
3874
3883
|
WHERE pref.confidence >= $threshold
|
|
3875
3884
|
WITH up, pref
|
|
@@ -3877,7 +3886,7 @@ async function loadUserProfile(accountId) {
|
|
|
3877
3886
|
WITH up, collect(pref) AS allPrefs
|
|
3878
3887
|
WITH up, allPrefs[0..$limit] AS prefs
|
|
3879
3888
|
RETURN up, prefs`,
|
|
3880
|
-
{ accountId, threshold: INJECTION_THRESHOLD, limit: MAX_SUMMARY_PREFERENCES }
|
|
3889
|
+
{ accountId, userId, threshold: INJECTION_THRESHOLD, limit: MAX_SUMMARY_PREFERENCES }
|
|
3881
3890
|
);
|
|
3882
3891
|
if (result.records.length === 0 || !result.records[0].get("up")) {
|
|
3883
3892
|
return null;
|
|
@@ -3896,7 +3905,7 @@ async function loadUserProfile(accountId) {
|
|
|
3896
3905
|
}));
|
|
3897
3906
|
const summary = formatProfileSummary(profileProps, preferences);
|
|
3898
3907
|
console.error(
|
|
3899
|
-
`[profile]
|
|
3908
|
+
`[profile] loaded for userId=${userId} accountId=${accountId.slice(0, 8)}\u2026 preferences=${preferences.length} (decay: ${decayCount} updated)`
|
|
3900
3909
|
);
|
|
3901
3910
|
return summary;
|
|
3902
3911
|
} catch (err) {
|
|
@@ -4083,7 +4092,7 @@ async function loadOnboardingStep(accountId) {
|
|
|
4083
4092
|
await session.close();
|
|
4084
4093
|
}
|
|
4085
4094
|
}
|
|
4086
|
-
async function writeReflectionPreferences(accountId, conversationId, updates) {
|
|
4095
|
+
async function writeReflectionPreferences(accountId, userId, conversationId, updates) {
|
|
4087
4096
|
if (updates.length === 0) return 0;
|
|
4088
4097
|
const session = getSession();
|
|
4089
4098
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -4135,6 +4144,7 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
|
|
|
4135
4144
|
const props = {
|
|
4136
4145
|
preferenceId,
|
|
4137
4146
|
accountId,
|
|
4147
|
+
userId,
|
|
4138
4148
|
category: update.category,
|
|
4139
4149
|
key: update.key,
|
|
4140
4150
|
value: update.value,
|
|
@@ -4147,10 +4157,10 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
|
|
|
4147
4157
|
};
|
|
4148
4158
|
if (embedding2) props.embedding = embedding2;
|
|
4149
4159
|
await txc.run(
|
|
4150
|
-
`MATCH (up:UserProfile {accountId: $accountId})
|
|
4160
|
+
`MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
4151
4161
|
CREATE (pref:Preference $props)
|
|
4152
4162
|
CREATE (up)-[:HAS_PREFERENCE]->(pref)`,
|
|
4153
|
-
{ accountId, props }
|
|
4163
|
+
{ accountId, userId, props }
|
|
4154
4164
|
);
|
|
4155
4165
|
for (const convId of allConvIds) {
|
|
4156
4166
|
await txc.run(
|
|
@@ -4183,6 +4193,7 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
|
|
|
4183
4193
|
const mode = update.mode || "reinforce";
|
|
4184
4194
|
const mergeParams = {
|
|
4185
4195
|
accountId,
|
|
4196
|
+
userId,
|
|
4186
4197
|
category: update.category,
|
|
4187
4198
|
key: update.key,
|
|
4188
4199
|
newPrefId,
|
|
@@ -4196,8 +4207,8 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
|
|
|
4196
4207
|
};
|
|
4197
4208
|
if (embedding) mergeParams.embedding = embedding;
|
|
4198
4209
|
const mergeResult = await session.run(
|
|
4199
|
-
`MATCH (up:UserProfile {accountId: $accountId})
|
|
4200
|
-
MERGE (up)-[:HAS_PREFERENCE]->(pref:Preference {accountId: $accountId, category: $category, key: $key})
|
|
4210
|
+
`MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
4211
|
+
MERGE (up)-[:HAS_PREFERENCE]->(pref:Preference {accountId: $accountId, userId: $userId, category: $category, key: $key})
|
|
4201
4212
|
ON CREATE SET
|
|
4202
4213
|
pref.preferenceId = $newPrefId,
|
|
4203
4214
|
pref.value = $value,
|
|
@@ -4237,13 +4248,13 @@ async function writeReflectionPreferences(accountId, conversationId, updates) {
|
|
|
4237
4248
|
}
|
|
4238
4249
|
if (written > 0) {
|
|
4239
4250
|
await session.run(
|
|
4240
|
-
`MATCH (up:UserProfile {accountId: $accountId})
|
|
4251
|
+
`MATCH (up:UserProfile {accountId: $accountId, userId: $userId})
|
|
4241
4252
|
SET up.profileVersion = coalesce(up.profileVersion, 0) + 1,
|
|
4242
4253
|
up.updatedAt = $now`,
|
|
4243
|
-
{ accountId, now }
|
|
4254
|
+
{ accountId, userId, now }
|
|
4244
4255
|
);
|
|
4245
4256
|
}
|
|
4246
|
-
console.error(`[profile-reflection] Wrote ${written}/${updates.length} preference updates for
|
|
4257
|
+
console.error(`[profile-reflection] Wrote ${written}/${updates.length} preference updates for userId=${userId} accountId=${accountId.slice(0, 8)}\u2026`);
|
|
4247
4258
|
return written;
|
|
4248
4259
|
} catch (err) {
|
|
4249
4260
|
console.error(`[profile-reflection] writeReflectionPreferences failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -4270,11 +4281,11 @@ function agentLogStream(name, accountDir) {
|
|
|
4270
4281
|
}
|
|
4271
4282
|
return createWriteStream(resolve4(logDir, `${name}-${date5}.log`), { flags: "a" });
|
|
4272
4283
|
}
|
|
4273
|
-
var
|
|
4274
|
-
var ACCOUNTS_DIR = resolve4(
|
|
4275
|
-
if (!existsSync5(
|
|
4284
|
+
var PLATFORM_ROOT4 = process.env.MAXY_PLATFORM_ROOT ?? resolve4(process.cwd(), "..");
|
|
4285
|
+
var ACCOUNTS_DIR = resolve4(PLATFORM_ROOT4, "..", "data/accounts");
|
|
4286
|
+
if (!existsSync5(PLATFORM_ROOT4)) {
|
|
4276
4287
|
throw new Error(
|
|
4277
|
-
`PLATFORM_ROOT does not exist: ${
|
|
4288
|
+
`PLATFORM_ROOT does not exist: ${PLATFORM_ROOT4}
|
|
4278
4289
|
Set the MAXY_PLATFORM_ROOT environment variable to the absolute path of the platform directory.`
|
|
4279
4290
|
);
|
|
4280
4291
|
}
|
|
@@ -4452,7 +4463,7 @@ function resolveAgentConfig(accountDir, agentName) {
|
|
|
4452
4463
|
return { model, plugins, status, displayName, image, imageShape, showAgentName, knowledge, knowledgeBaked, liveMemory, knowledgeKeywords, budget, accessMode };
|
|
4453
4464
|
}
|
|
4454
4465
|
function parsePluginFrontmatter(pluginDir) {
|
|
4455
|
-
const pluginPath = resolve4(
|
|
4466
|
+
const pluginPath = resolve4(PLATFORM_ROOT4, "plugins", pluginDir, "PLUGIN.md");
|
|
4456
4467
|
if (!existsSync5(pluginPath)) return null;
|
|
4457
4468
|
let raw2;
|
|
4458
4469
|
try {
|
|
@@ -4513,7 +4524,7 @@ function parsePluginFrontmatter(pluginDir) {
|
|
|
4513
4524
|
};
|
|
4514
4525
|
}
|
|
4515
4526
|
function assemblePublicPluginContent(pluginDir) {
|
|
4516
|
-
const pluginRoot = resolve4(
|
|
4527
|
+
const pluginRoot = resolve4(PLATFORM_ROOT4, "plugins", pluginDir);
|
|
4517
4528
|
const pluginPath = resolve4(pluginRoot, "PLUGIN.md");
|
|
4518
4529
|
let raw2;
|
|
4519
4530
|
try {
|
|
@@ -4607,7 +4618,7 @@ function assemblePublicPluginContent(pluginDir) {
|
|
|
4607
4618
|
return { body: parts.join("\n"), skillCount, refCount };
|
|
4608
4619
|
}
|
|
4609
4620
|
function loadEmbeddedPlugins(agentType, selectedPlugins, enabledPlugins) {
|
|
4610
|
-
const pluginsDir = resolve4(
|
|
4621
|
+
const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
|
|
4611
4622
|
let dirs;
|
|
4612
4623
|
try {
|
|
4613
4624
|
dirs = readdirSync(pluginsDir);
|
|
@@ -4687,14 +4698,14 @@ var mcpToolsCache = /* @__PURE__ */ new Map();
|
|
|
4687
4698
|
function fetchMcpToolsList(pluginDir) {
|
|
4688
4699
|
const cached2 = mcpToolsCache.get(pluginDir);
|
|
4689
4700
|
if (cached2) return Promise.resolve(cached2);
|
|
4690
|
-
const serverPath = resolve4(
|
|
4701
|
+
const serverPath = resolve4(PLATFORM_ROOT4, "plugins", pluginDir, "mcp/dist/index.js");
|
|
4691
4702
|
if (!existsSync5(serverPath)) return Promise.resolve([]);
|
|
4692
4703
|
const startMs = Date.now();
|
|
4693
4704
|
return new Promise((resolvePromise) => {
|
|
4694
4705
|
const proc = spawn2(process.execPath, [serverPath], {
|
|
4695
4706
|
env: {
|
|
4696
4707
|
...process.env,
|
|
4697
|
-
PLATFORM_ROOT:
|
|
4708
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
4698
4709
|
ACCOUNT_ID: "__toolslist__",
|
|
4699
4710
|
PLATFORM_PORT: process.env.PORT ?? "19200"
|
|
4700
4711
|
}
|
|
@@ -4778,7 +4789,7 @@ function fetchMcpToolsList(pluginDir) {
|
|
|
4778
4789
|
});
|
|
4779
4790
|
}
|
|
4780
4791
|
async function buildPluginManifest(enabledPlugins) {
|
|
4781
|
-
const pluginsDir = resolve4(
|
|
4792
|
+
const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
|
|
4782
4793
|
let dirs;
|
|
4783
4794
|
try {
|
|
4784
4795
|
dirs = readdirSync(pluginsDir);
|
|
@@ -4849,7 +4860,7 @@ async function buildPluginManifest(enabledPlugins) {
|
|
|
4849
4860
|
toolLines.push(desc ? ` ${tool.name} \u2014 ${desc}` : ` ${tool.name}`);
|
|
4850
4861
|
}
|
|
4851
4862
|
} else if (parsed.tools.length > 0) {
|
|
4852
|
-
const serverPath = resolve4(
|
|
4863
|
+
const serverPath = resolve4(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
|
|
4853
4864
|
if (existsSync5(serverPath)) {
|
|
4854
4865
|
fallbackSourced++;
|
|
4855
4866
|
console.error(`[plugin-manifest] ${dir}: tools/list empty \u2014 fallback to frontmatter (${parsed.tools.length} tools)`);
|
|
@@ -4924,10 +4935,37 @@ function validateComponentData(componentName, data) {
|
|
|
4924
4935
|
function getDefaultAccountId() {
|
|
4925
4936
|
return resolveAccount()?.accountId ?? null;
|
|
4926
4937
|
}
|
|
4938
|
+
function resolveUserAccounts(userId) {
|
|
4939
|
+
if (!existsSync5(ACCOUNTS_DIR)) return [];
|
|
4940
|
+
const results = [];
|
|
4941
|
+
const entries = readdirSync(ACCOUNTS_DIR, { withFileTypes: true });
|
|
4942
|
+
for (const entry of entries) {
|
|
4943
|
+
if (!entry.isDirectory()) continue;
|
|
4944
|
+
const configPath2 = resolve4(ACCOUNTS_DIR, entry.name, "account.json");
|
|
4945
|
+
if (!existsSync5(configPath2)) continue;
|
|
4946
|
+
let config2;
|
|
4947
|
+
try {
|
|
4948
|
+
config2 = JSON.parse(readFileSync5(configPath2, "utf-8"));
|
|
4949
|
+
} catch {
|
|
4950
|
+
console.error(`[session] account.json corrupt at ${configPath2} \u2014 skipping`);
|
|
4951
|
+
continue;
|
|
4952
|
+
}
|
|
4953
|
+
const adminEntry = config2.admins?.find((a) => a.userId === userId);
|
|
4954
|
+
if (adminEntry) {
|
|
4955
|
+
results.push({
|
|
4956
|
+
accountId: config2.accountId,
|
|
4957
|
+
accountDir: resolve4(ACCOUNTS_DIR, entry.name),
|
|
4958
|
+
config: config2,
|
|
4959
|
+
role: adminEntry.role
|
|
4960
|
+
});
|
|
4961
|
+
}
|
|
4962
|
+
}
|
|
4963
|
+
return results;
|
|
4964
|
+
}
|
|
4927
4965
|
var sessionStore = /* @__PURE__ */ new Map();
|
|
4928
4966
|
setSessionStoreRef(sessionStore);
|
|
4929
|
-
function registerSession(sessionKey, agentType, accountId, agentName) {
|
|
4930
|
-
sessionStore.set(sessionKey, { createdAt: Date.now(), agentType, accountId, agentName });
|
|
4967
|
+
function registerSession(sessionKey, agentType, accountId, agentName, userId, userName) {
|
|
4968
|
+
sessionStore.set(sessionKey, { createdAt: Date.now(), agentType, accountId, agentName, userId, userName });
|
|
4931
4969
|
}
|
|
4932
4970
|
function registerResumedSession(sessionKey, accountId, agentName, conversationId, messages) {
|
|
4933
4971
|
const messageHistory = messages.map((m) => ({
|
|
@@ -4996,6 +5034,12 @@ function consumePendingCompactionSummary(sessionKey) {
|
|
|
4996
5034
|
function getAccountIdForSession(sessionKey) {
|
|
4997
5035
|
return sessionStore.get(sessionKey)?.accountId;
|
|
4998
5036
|
}
|
|
5037
|
+
function getUserIdForSession(sessionKey) {
|
|
5038
|
+
return sessionStore.get(sessionKey)?.userId;
|
|
5039
|
+
}
|
|
5040
|
+
function getUserNameForSession(sessionKey) {
|
|
5041
|
+
return sessionStore.get(sessionKey)?.userName;
|
|
5042
|
+
}
|
|
4999
5043
|
function registerGrantSession(sessionKey, accountId, agentName, opts) {
|
|
5000
5044
|
sessionStore.set(sessionKey, {
|
|
5001
5045
|
createdAt: Date.now(),
|
|
@@ -5072,42 +5116,42 @@ function consumeStalledSubagents(sessionKey) {
|
|
|
5072
5116
|
delete session.stalledSubagents;
|
|
5073
5117
|
return stalls && stalls.length > 0 ? stalls : void 0;
|
|
5074
5118
|
}
|
|
5075
|
-
function getMcpServers(accountId, enabledPlugins) {
|
|
5119
|
+
function getMcpServers(accountId, userId, enabledPlugins) {
|
|
5076
5120
|
const servers = {
|
|
5077
5121
|
"memory": {
|
|
5078
5122
|
command: "node",
|
|
5079
|
-
args: [resolve4(
|
|
5080
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5123
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js")],
|
|
5124
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, ...userId ? { USER_ID: userId } : {} }
|
|
5081
5125
|
},
|
|
5082
5126
|
"contacts": {
|
|
5083
5127
|
command: "node",
|
|
5084
|
-
args: [resolve4(
|
|
5085
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5128
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/contacts/mcp/dist/index.js")],
|
|
5129
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5086
5130
|
},
|
|
5087
5131
|
"whatsapp": {
|
|
5088
5132
|
command: "node",
|
|
5089
|
-
args: [resolve4(
|
|
5090
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5133
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/whatsapp/mcp/dist/index.js")],
|
|
5134
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, PLATFORM_PORT: process.env.PORT ?? "19200" }
|
|
5091
5135
|
},
|
|
5092
5136
|
"admin": {
|
|
5093
5137
|
command: "node",
|
|
5094
|
-
args: [resolve4(
|
|
5095
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5138
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/admin/mcp/dist/index.js")],
|
|
5139
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4, PLATFORM_PORT: process.env.PORT ?? "19200", ...userId ? { USER_ID: userId } : {} }
|
|
5096
5140
|
},
|
|
5097
5141
|
"scheduling": {
|
|
5098
5142
|
command: "node",
|
|
5099
|
-
args: [resolve4(
|
|
5100
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5143
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/scheduling/mcp/dist/index.js")],
|
|
5144
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5101
5145
|
},
|
|
5102
5146
|
"tasks": {
|
|
5103
5147
|
command: "node",
|
|
5104
|
-
args: [resolve4(
|
|
5105
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5148
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/tasks/mcp/dist/index.js")],
|
|
5149
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5106
5150
|
},
|
|
5107
5151
|
"email": {
|
|
5108
5152
|
command: "node",
|
|
5109
|
-
args: [resolve4(
|
|
5110
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5153
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/email/mcp/dist/index.js")],
|
|
5154
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5111
5155
|
},
|
|
5112
5156
|
// Playwright MCP server — browser automation for browser-specialist.
|
|
5113
5157
|
// Key matches Claude Code's plugin naming: plugin_{plugin}_{server} so tools
|
|
@@ -5123,8 +5167,8 @@ function getMcpServers(accountId, enabledPlugins) {
|
|
|
5123
5167
|
if (process.env.TELEGRAM_PUBLIC_BOT_TOKEN) {
|
|
5124
5168
|
servers["telegram"] = {
|
|
5125
5169
|
command: "node",
|
|
5126
|
-
args: [resolve4(
|
|
5127
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5170
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/telegram/mcp/dist/index.js")],
|
|
5171
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5128
5172
|
};
|
|
5129
5173
|
} else {
|
|
5130
5174
|
console.error("[plugins] telegram MCP: skipped (no TELEGRAM_PUBLIC_BOT_TOKEN)");
|
|
@@ -5141,14 +5185,14 @@ function getMcpServers(accountId, enabledPlugins) {
|
|
|
5141
5185
|
if (!tunnelConfigured) {
|
|
5142
5186
|
servers["cloudflare"] = {
|
|
5143
5187
|
command: "node",
|
|
5144
|
-
args: [resolve4(
|
|
5145
|
-
env: { PLATFORM_ROOT:
|
|
5188
|
+
args: [resolve4(PLATFORM_ROOT4, "plugins/cloudflare/mcp/dist/index.js")],
|
|
5189
|
+
env: { PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5146
5190
|
};
|
|
5147
5191
|
} else {
|
|
5148
5192
|
console.error("[plugins] cloudflare MCP: skipped (tunnel already configured)");
|
|
5149
5193
|
}
|
|
5150
5194
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
5151
|
-
const pluginsDir = resolve4(
|
|
5195
|
+
const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
|
|
5152
5196
|
let dirs;
|
|
5153
5197
|
try {
|
|
5154
5198
|
dirs = readdirSync(pluginsDir);
|
|
@@ -5171,12 +5215,12 @@ function getMcpServers(accountId, enabledPlugins) {
|
|
|
5171
5215
|
continue;
|
|
5172
5216
|
}
|
|
5173
5217
|
}
|
|
5174
|
-
const mcpEntry = resolve4(
|
|
5218
|
+
const mcpEntry = resolve4(PLATFORM_ROOT4, "plugins", dir, "mcp/dist/index.js");
|
|
5175
5219
|
if (!existsSync5(mcpEntry)) continue;
|
|
5176
5220
|
servers[dir] = {
|
|
5177
5221
|
command: "node",
|
|
5178
5222
|
args: [mcpEntry],
|
|
5179
|
-
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT:
|
|
5223
|
+
env: { ACCOUNT_ID: accountId, PLATFORM_ROOT: PLATFORM_ROOT4 }
|
|
5180
5224
|
};
|
|
5181
5225
|
console.log(`[plugins] optional MCP server started: ${dir}`);
|
|
5182
5226
|
}
|
|
@@ -5224,6 +5268,9 @@ var ADMIN_CORE_TOOLS = [
|
|
|
5224
5268
|
"mcp__admin__render-component",
|
|
5225
5269
|
"mcp__admin__session-reset",
|
|
5226
5270
|
"mcp__admin__session-resume",
|
|
5271
|
+
"mcp__admin__admin-add",
|
|
5272
|
+
"mcp__admin__admin-remove",
|
|
5273
|
+
"mcp__admin__admin-list",
|
|
5227
5274
|
"mcp__admin__api-key-store",
|
|
5228
5275
|
"mcp__admin__api-key-verify",
|
|
5229
5276
|
"mcp__admin__file-attach",
|
|
@@ -5265,7 +5312,7 @@ var ADMIN_CORE_TOOLS = [
|
|
|
5265
5312
|
function getAdminAllowedTools(enabledPlugins) {
|
|
5266
5313
|
const tools = [...ADMIN_CORE_TOOLS];
|
|
5267
5314
|
if (Array.isArray(enabledPlugins) && enabledPlugins.length > 0) {
|
|
5268
|
-
const pluginsDir = resolve4(
|
|
5315
|
+
const pluginsDir = resolve4(PLATFORM_ROOT4, "plugins");
|
|
5269
5316
|
let dirs;
|
|
5270
5317
|
try {
|
|
5271
5318
|
dirs = readdirSync(pluginsDir);
|
|
@@ -5358,7 +5405,7 @@ ${message.slice(0, QUERY_CLASSIFIER_MSG_CAP)}`
|
|
|
5358
5405
|
}
|
|
5359
5406
|
}
|
|
5360
5407
|
async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
5361
|
-
const serverPath = resolve4(
|
|
5408
|
+
const serverPath = resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
|
|
5362
5409
|
if (!existsSync5(serverPath)) {
|
|
5363
5410
|
console.error(`[fetchMemoryContext] MCP server not found: ${serverPath}`);
|
|
5364
5411
|
return null;
|
|
@@ -5369,7 +5416,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
5369
5416
|
env: {
|
|
5370
5417
|
...process.env,
|
|
5371
5418
|
ACCOUNT_ID: accountId,
|
|
5372
|
-
PLATFORM_ROOT:
|
|
5419
|
+
PLATFORM_ROOT: PLATFORM_ROOT4,
|
|
5373
5420
|
READ_ONLY: "true",
|
|
5374
5421
|
ALLOWED_SCOPES: "public,shared",
|
|
5375
5422
|
...sessionKey ? { SESSION_ID: sessionKey } : {},
|
|
@@ -5456,7 +5503,7 @@ async function fetchMemoryContext(accountId, query, sessionKey, options) {
|
|
|
5456
5503
|
}
|
|
5457
5504
|
async function compactTrimmedMessages(accountId, trimmedMessages) {
|
|
5458
5505
|
if (trimmedMessages.length === 0) return true;
|
|
5459
|
-
const serverPath = resolve4(
|
|
5506
|
+
const serverPath = resolve4(PLATFORM_ROOT4, "plugins/memory/mcp/dist/index.js");
|
|
5460
5507
|
if (!existsSync5(serverPath)) return false;
|
|
5461
5508
|
const briefing = trimmedMessages.map((m) => `[${m.role.toUpperCase()}] ${m.content}`).join("\n\n");
|
|
5462
5509
|
return new Promise((resolvePromise) => {
|
|
@@ -5657,7 +5704,7 @@ Respond with ONLY a JSON array. Each element:
|
|
|
5657
5704
|
|
|
5658
5705
|
mergeSourceIds is only for mode "merge" \u2014 list the preferenceIds of the sources to combine.
|
|
5659
5706
|
If no preferences are found, respond with an empty array: []`;
|
|
5660
|
-
async function reflectOnSessionProfile(accountId, sessionKey, profileSummary) {
|
|
5707
|
+
async function reflectOnSessionProfile(accountId, userId, sessionKey, profileSummary) {
|
|
5661
5708
|
const history = getMessageHistory(sessionKey);
|
|
5662
5709
|
if (history.length === 0) return 0;
|
|
5663
5710
|
let apiKey = process.env.ANTHROPIC_API_KEY;
|
|
@@ -5739,7 +5786,7 @@ Extract preference updates as JSON array.`
|
|
|
5739
5786
|
}
|
|
5740
5787
|
console.error(`[profile-reflection] Extracted ${sanitized.length} preference updates via Haiku (${updates.length - sanitized.length} filtered)`);
|
|
5741
5788
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
5742
|
-
return await writeReflectionPreferences(accountId, convId, sanitized);
|
|
5789
|
+
return await writeReflectionPreferences(accountId, userId, convId, sanitized);
|
|
5743
5790
|
} catch (err) {
|
|
5744
5791
|
const reason = err instanceof Error ? err.message : String(err);
|
|
5745
5792
|
if (err instanceof Error && err.name === "AbortError") {
|
|
@@ -5858,7 +5905,7 @@ var COMPACTION_PROMPT = `You are about to reach your context limit. Call session
|
|
|
5858
5905
|
Then respond with only: [COMPACTED]`;
|
|
5859
5906
|
var COMPACTION_TIMEOUT_MS = 45e3;
|
|
5860
5907
|
async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSessionId, adminModel, enabledPlugins) {
|
|
5861
|
-
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, enabledPlugins) });
|
|
5908
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, void 0, enabledPlugins) });
|
|
5862
5909
|
const specialistsDir = resolve4(accountDir, "specialists");
|
|
5863
5910
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-compaction-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
5864
5911
|
`);
|
|
@@ -5888,7 +5935,7 @@ async function* runCompactionTurn(accountDir, accountId, systemPrompt, resumeSes
|
|
|
5888
5935
|
const proc = spawn2("claude", args, {
|
|
5889
5936
|
cwd: accountDir,
|
|
5890
5937
|
stdio: ["ignore", "pipe", "pipe"],
|
|
5891
|
-
env: { ...process.env, PLATFORM_ROOT:
|
|
5938
|
+
env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT4, ACCOUNT_DIR: accountDir }
|
|
5892
5939
|
});
|
|
5893
5940
|
const stderrLog = agentLogStream("claude-agent-compaction-stderr", accountDir);
|
|
5894
5941
|
proc.stderr?.pipe(stderrLog);
|
|
@@ -6497,7 +6544,8 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
6497
6544
|
`);
|
|
6498
6545
|
cdpLog.end();
|
|
6499
6546
|
}
|
|
6500
|
-
const
|
|
6547
|
+
const ccUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
6548
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, ccUserId, enabledPlugins) });
|
|
6501
6549
|
const specialistsDir = resolve4(accountDir, "specialists");
|
|
6502
6550
|
if (!existsSync5(specialistsDir)) agentLogStream("claude-agent-stream", accountDir).write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
6503
6551
|
`);
|
|
@@ -6529,7 +6577,7 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
6529
6577
|
const proc = spawn2("claude", args, {
|
|
6530
6578
|
cwd: accountDir,
|
|
6531
6579
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6532
|
-
env: { ...process.env, PLATFORM_ROOT:
|
|
6580
|
+
env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT4, ACCOUNT_DIR: accountDir }
|
|
6533
6581
|
});
|
|
6534
6582
|
const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
|
|
6535
6583
|
proc.stderr?.pipe(stderrLog);
|
|
@@ -6581,10 +6629,13 @@ async function* invokeAdminAgent(message, systemPrompt, accountDir, accountId, a
|
|
|
6581
6629
|
if (step.value.summary) {
|
|
6582
6630
|
storePendingCompactionSummary(sessionKey, step.value.summary);
|
|
6583
6631
|
}
|
|
6584
|
-
const
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6632
|
+
const reflectionUserId = getUserIdForSession(sessionKey);
|
|
6633
|
+
if (reflectionUserId) {
|
|
6634
|
+
const profileForReflection = await loadUserProfile(accountId, reflectionUserId);
|
|
6635
|
+
reflectOnSessionProfile(accountId, reflectionUserId, sessionKey, profileForReflection).catch((err) => {
|
|
6636
|
+
console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
|
|
6637
|
+
});
|
|
6638
|
+
}
|
|
6588
6639
|
clearAgentSessionId(sessionKey);
|
|
6589
6640
|
const convId = sessionStore.get(sessionKey)?.conversationId;
|
|
6590
6641
|
if (convId) {
|
|
@@ -6749,7 +6800,8 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
6749
6800
|
const cdpOk = await ensureCdp();
|
|
6750
6801
|
if (!cdpOk) streamLog.write(`[${isoTs()}] [warn] ensureCdp failed \u2014 browser-specialist degraded
|
|
6751
6802
|
`);
|
|
6752
|
-
const
|
|
6803
|
+
const managedUserId = getUserIdForSession(sessionKey);
|
|
6804
|
+
const mcpConfig = JSON.stringify({ mcpServers: getMcpServers(accountId, managedUserId, enabledPlugins) });
|
|
6753
6805
|
const specialistsDir = resolve4(accountDir, "specialists");
|
|
6754
6806
|
if (!existsSync5(specialistsDir)) streamLog.write(`[${isoTs()}] [warn] specialists plugin dir missing: ${specialistsDir}
|
|
6755
6807
|
`);
|
|
@@ -6778,7 +6830,7 @@ async function* invokeManagedAdminAgent(message, systemPrompt, accountDir, accou
|
|
|
6778
6830
|
const proc = spawn2("claude", args, {
|
|
6779
6831
|
cwd: accountDir,
|
|
6780
6832
|
stdio: ["ignore", "pipe", "pipe"],
|
|
6781
|
-
env: { ...process.env, PLATFORM_ROOT:
|
|
6833
|
+
env: { ...process.env, PLATFORM_ROOT: PLATFORM_ROOT4, ACCOUNT_DIR: accountDir }
|
|
6782
6834
|
});
|
|
6783
6835
|
const stderrLog = agentLogStream("claude-agent-stderr", accountDir);
|
|
6784
6836
|
proc.stderr?.pipe(stderrLog);
|
|
@@ -7216,10 +7268,11 @@ ${EXPLANATORY_STYLE_INSTRUCTIONS}` : baseSystemPrompt;
|
|
|
7216
7268
|
if (step.value.summary) {
|
|
7217
7269
|
storePendingCompactionSummary(sessionKey, step.value.summary);
|
|
7218
7270
|
}
|
|
7219
|
-
const
|
|
7220
|
-
|
|
7221
|
-
|
|
7222
|
-
|
|
7271
|
+
const compactAgentType = sessionStore.get(sessionKey)?.agentType;
|
|
7272
|
+
const compactUserId = getUserIdForSession(sessionKey);
|
|
7273
|
+
if (compactAgentType === "admin" && compactUserId) {
|
|
7274
|
+
const currentProfile = await loadUserProfile(account.accountId, compactUserId);
|
|
7275
|
+
reflectOnSessionProfile(account.accountId, compactUserId, sessionKey, currentProfile).catch((err) => {
|
|
7223
7276
|
console.error(`[profile-reflection] Unhandled error: ${err instanceof Error ? err.message : String(err)}`);
|
|
7224
7277
|
});
|
|
7225
7278
|
}
|
|
@@ -7236,11 +7289,13 @@ async function* invokeAgent(config2, message, sessionKey, attachments = [], user
|
|
|
7236
7289
|
return;
|
|
7237
7290
|
}
|
|
7238
7291
|
const accountId = sessionAccountId ?? account.accountId;
|
|
7292
|
+
const sessionUserId = sessionKey ? getUserIdForSession(sessionKey) : void 0;
|
|
7293
|
+
const sessionUserName = sessionKey ? getUserNameForSession(sessionKey) : void 0;
|
|
7239
7294
|
const resolvedAgentName = agentName ?? agentType;
|
|
7240
7295
|
const identity = readIdentity(account.accountDir, resolvedAgentName);
|
|
7241
7296
|
const rawSoul = readAgentFile(account.accountDir, resolvedAgentName, "SOUL.md");
|
|
7242
7297
|
const soul = rawSoul && hasSoulContent(rawSoul) ? rawSoul : null;
|
|
7243
|
-
console.log(`[invoke-agent] agent=${resolvedAgentName} type=${agentType} session=${sessionKey?.slice(0, 8) ?? "none"} identity=${identity ? `${identity.length}b` : "MISSING"} soul=${rawSoul ? soul ? `${soul.length}b` : "EMPTY(header-only)" : "MISSING"}`);
|
|
7298
|
+
console.log(`[invoke-agent] agent=${resolvedAgentName} type=${agentType} session=${sessionKey?.slice(0, 8) ?? "none"} userId=${sessionUserId ?? "none"} identity=${identity ? `${identity.length}b` : "MISSING"} soul=${rawSoul ? soul ? `${soul.length}b` : "EMPTY(header-only)" : "MISSING"}`);
|
|
7244
7299
|
const defaultSystemPrompt = agentType === "public" ? "You are a public assistant. Answer only from the memory context provided. Never use training data. British English. Be concise." : "You are an admin agent. Full access. Professional, concise. British English. Be proactive \u2014 tell the user what needs doing and do it.";
|
|
7245
7300
|
const identityPrompt = identity ?? defaultSystemPrompt;
|
|
7246
7301
|
let baseSystemPrompt = soul ? `${identityPrompt}
|
|
@@ -7248,7 +7303,7 @@ async function* invokeAgent(config2, message, sessionKey, attachments = [], user
|
|
|
7248
7303
|
${soul}` : identityPrompt;
|
|
7249
7304
|
const now = /* @__PURE__ */ new Date();
|
|
7250
7305
|
const isoTimestamp = now.toISOString();
|
|
7251
|
-
const userTimezone = await getUserTimezone(accountId);
|
|
7306
|
+
const userTimezone = await getUserTimezone(accountId, sessionUserId);
|
|
7252
7307
|
const effectiveTimezone = userTimezone ?? Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
7253
7308
|
if (!userTimezone) {
|
|
7254
7309
|
console.error(`[datetime] getUserTimezone returned null for ${accountId.slice(0, 8)}\u2026, falling back to server timezone: ${effectiveTimezone}`);
|
|
@@ -7272,16 +7327,23 @@ ${isoTimestamp}
|
|
|
7272
7327
|
${humanReadable} (${effectiveTimezone})
|
|
7273
7328
|
</datetime>`;
|
|
7274
7329
|
if (agentType === "admin") {
|
|
7275
|
-
|
|
7276
|
-
loadUserProfile(accountId),
|
|
7277
|
-
loadSessionContext(accountId),
|
|
7278
|
-
loadOnboardingStep(accountId)
|
|
7279
|
-
]);
|
|
7280
|
-
if (profileSummary) {
|
|
7330
|
+
if (sessionUserId) {
|
|
7281
7331
|
baseSystemPrompt += `
|
|
7282
7332
|
|
|
7333
|
+
<admin-identity>
|
|
7334
|
+
You are talking to ${sessionUserName ?? "an admin"}. Their userId is ${sessionUserId}.
|
|
7335
|
+
</admin-identity>`;
|
|
7336
|
+
const profileSummary = await loadUserProfile(accountId, sessionUserId);
|
|
7337
|
+
if (profileSummary) {
|
|
7338
|
+
baseSystemPrompt += `
|
|
7339
|
+
|
|
7283
7340
|
${profileSummary}`;
|
|
7341
|
+
}
|
|
7284
7342
|
}
|
|
7343
|
+
const [sessionContext, onboardingStep] = await Promise.all([
|
|
7344
|
+
loadSessionContext(accountId),
|
|
7345
|
+
loadOnboardingStep(accountId)
|
|
7346
|
+
]);
|
|
7285
7347
|
if (sessionContext) {
|
|
7286
7348
|
baseSystemPrompt += `
|
|
7287
7349
|
|
|
@@ -7376,7 +7438,7 @@ ${stallContext}`;
|
|
|
7376
7438
|
Current session key: ${sessionKey}` : systemPromptBase;
|
|
7377
7439
|
if (sessionKey) {
|
|
7378
7440
|
try {
|
|
7379
|
-
await ensureConversation(accountId, agentType, sessionKey);
|
|
7441
|
+
await ensureConversation(accountId, agentType, sessionKey, void 0, void 0, sessionUserId);
|
|
7380
7442
|
} catch (err) {
|
|
7381
7443
|
console.error(`[persist] ensureConversation failed in invokeAgent: ${err instanceof Error ? err.message : String(err)}`);
|
|
7382
7444
|
}
|
|
@@ -7606,8 +7668,8 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
7606
7668
|
import { mkdir, readFile, stat, writeFile } from "fs/promises";
|
|
7607
7669
|
import { realpathSync } from "fs";
|
|
7608
7670
|
import { resolve as resolve6, extname, basename } from "path";
|
|
7609
|
-
var
|
|
7610
|
-
var ATTACHMENTS_ROOT = resolve6(
|
|
7671
|
+
var PLATFORM_ROOT5 = process.env.MAXY_PLATFORM_ROOT ?? resolve6(process.cwd(), "../platform");
|
|
7672
|
+
var ATTACHMENTS_ROOT = resolve6(PLATFORM_ROOT5, "..", "data/uploads");
|
|
7611
7673
|
var SUPPORTED_MIME_TYPES = /* @__PURE__ */ new Set([
|
|
7612
7674
|
"image/jpeg",
|
|
7613
7675
|
"image/png",
|
|
@@ -8463,11 +8525,11 @@ import neo4j2 from "neo4j-driver";
|
|
|
8463
8525
|
import { readFileSync as readFileSync7 } from "fs";
|
|
8464
8526
|
import { resolve as resolve7 } from "path";
|
|
8465
8527
|
import { randomUUID as randomUUID3, randomInt } from "crypto";
|
|
8466
|
-
var
|
|
8528
|
+
var PLATFORM_ROOT6 = process.env.MAXY_PLATFORM_ROOT ?? resolve7(process.cwd(), "..");
|
|
8467
8529
|
var driver2 = null;
|
|
8468
8530
|
function readPassword2() {
|
|
8469
8531
|
if (process.env.NEO4J_PASSWORD) return process.env.NEO4J_PASSWORD;
|
|
8470
|
-
const passwordFile = resolve7(
|
|
8532
|
+
const passwordFile = resolve7(PLATFORM_ROOT6, "config/.neo4j-password");
|
|
8471
8533
|
try {
|
|
8472
8534
|
return readFileSync7(passwordFile, "utf-8").trim();
|
|
8473
8535
|
} catch {
|
|
@@ -25619,7 +25681,7 @@ import { readFile as readFile2, stat as stat2 } from "fs/promises";
|
|
|
25619
25681
|
import { realpathSync as realpathSync2 } from "fs";
|
|
25620
25682
|
import { resolve as resolve11, basename as basename2 } from "path";
|
|
25621
25683
|
var TAG11 = "[whatsapp:api]";
|
|
25622
|
-
var
|
|
25684
|
+
var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
25623
25685
|
async function POST15(req) {
|
|
25624
25686
|
try {
|
|
25625
25687
|
const body = await req.json();
|
|
@@ -25628,13 +25690,13 @@ async function POST15(req) {
|
|
|
25628
25690
|
if (!to || !filePath) {
|
|
25629
25691
|
return Response.json({ error: "Missing required fields: to, filePath" }, { status: 400 });
|
|
25630
25692
|
}
|
|
25631
|
-
if (!maxyAccountId || !
|
|
25693
|
+
if (!maxyAccountId || !PLATFORM_ROOT7) {
|
|
25632
25694
|
return Response.json(
|
|
25633
25695
|
{ error: "Cannot validate file path: missing account or platform context" },
|
|
25634
25696
|
{ status: 400 }
|
|
25635
25697
|
);
|
|
25636
25698
|
}
|
|
25637
|
-
const accountDir = resolve11(
|
|
25699
|
+
const accountDir = resolve11(PLATFORM_ROOT7, "..", "data/accounts", maxyAccountId);
|
|
25638
25700
|
let resolvedPath;
|
|
25639
25701
|
try {
|
|
25640
25702
|
resolvedPath = realpathSync2(filePath);
|
|
@@ -25877,35 +25939,93 @@ function getStoredPinHash() {
|
|
|
25877
25939
|
}
|
|
25878
25940
|
return null;
|
|
25879
25941
|
}
|
|
25942
|
+
function readUsersFile() {
|
|
25943
|
+
if (!existsSync13(USERS_FILE)) return null;
|
|
25944
|
+
const raw2 = readFileSync13(USERS_FILE, "utf-8").trim();
|
|
25945
|
+
if (!raw2) return [];
|
|
25946
|
+
return JSON.parse(raw2);
|
|
25947
|
+
}
|
|
25880
25948
|
async function POST19(req) {
|
|
25881
|
-
const storedHash = getStoredPinHash();
|
|
25882
|
-
if (!storedHash) {
|
|
25883
|
-
return Response.json(
|
|
25884
|
-
{ error: "PIN not configured. Complete onboarding first." },
|
|
25885
|
-
{ status: 503 }
|
|
25886
|
-
);
|
|
25887
|
-
}
|
|
25888
25949
|
let body;
|
|
25889
25950
|
try {
|
|
25890
25951
|
body = await req.json();
|
|
25891
25952
|
} catch {
|
|
25892
25953
|
return Response.json({ error: "Invalid request" }, { status: 400 });
|
|
25893
25954
|
}
|
|
25955
|
+
if (!body.pin) {
|
|
25956
|
+
return Response.json({ error: "Invalid request" }, { status: 400 });
|
|
25957
|
+
}
|
|
25894
25958
|
const inputHash = hashPin2(body.pin);
|
|
25895
|
-
|
|
25959
|
+
let users = null;
|
|
25960
|
+
try {
|
|
25961
|
+
users = readUsersFile();
|
|
25962
|
+
} catch (err) {
|
|
25963
|
+
console.error(`[session] users.json corrupt: ${err instanceof Error ? err.message : String(err)}`);
|
|
25964
|
+
return Response.json(
|
|
25965
|
+
{ error: "User configuration is corrupt. Re-run the seed script." },
|
|
25966
|
+
{ status: 503 }
|
|
25967
|
+
);
|
|
25968
|
+
}
|
|
25969
|
+
if (users !== null) {
|
|
25970
|
+
const matchedUser = users.find((u) => u.pin === inputHash);
|
|
25971
|
+
if (!matchedUser) {
|
|
25972
|
+
console.log(`[session] PIN auth failed: no matching user`);
|
|
25973
|
+
return Response.json({ error: "Invalid PIN" }, { status: 401 });
|
|
25974
|
+
}
|
|
25975
|
+
const { userId, name: userName } = matchedUser;
|
|
25976
|
+
const accounts = resolveUserAccounts(userId);
|
|
25977
|
+
if (accounts.length === 0) {
|
|
25978
|
+
console.log(`[session] user has no accounts: userId=${userId}`);
|
|
25979
|
+
return Response.json(
|
|
25980
|
+
{ error: "No account access for this user." },
|
|
25981
|
+
{ status: 403 }
|
|
25982
|
+
);
|
|
25983
|
+
}
|
|
25984
|
+
if (accounts.length > 1 && !body.accountId) {
|
|
25985
|
+
console.log(`[session] multi-account user: userId=${userId} accounts=${accounts.length}`);
|
|
25986
|
+
const accountList = await Promise.all(
|
|
25987
|
+
accounts.map(async (a) => {
|
|
25988
|
+
let businessName;
|
|
25989
|
+
try {
|
|
25990
|
+
const branding = await fetchBranding(a.accountId);
|
|
25991
|
+
businessName = branding?.name || void 0;
|
|
25992
|
+
} catch {
|
|
25993
|
+
}
|
|
25994
|
+
return { accountId: a.accountId, businessName, role: a.role };
|
|
25995
|
+
})
|
|
25996
|
+
);
|
|
25997
|
+
return Response.json({ accounts: accountList, userId, userName });
|
|
25998
|
+
}
|
|
25999
|
+
const selected = body.accountId ? accounts.find((a) => a.accountId === body.accountId) : accounts[0];
|
|
26000
|
+
if (!selected) {
|
|
26001
|
+
console.log(`[session] account selection invalid: userId=${userId} requested=${body.accountId}`);
|
|
26002
|
+
return Response.json({ error: "Invalid account selection." }, { status: 403 });
|
|
26003
|
+
}
|
|
26004
|
+
return createAdminSession(selected.accountId, selected.config.thinkingView, userId, userName);
|
|
26005
|
+
}
|
|
26006
|
+
const storedHash = getStoredPinHash();
|
|
26007
|
+
if (!storedHash) {
|
|
26008
|
+
return Response.json(
|
|
26009
|
+
{ error: "PIN not configured. Complete onboarding first." },
|
|
26010
|
+
{ status: 503 }
|
|
26011
|
+
);
|
|
26012
|
+
}
|
|
26013
|
+
if (inputHash !== storedHash) {
|
|
25896
26014
|
console.log(`[session] PIN mismatch \u2014 stored=${storedHash.slice(0, 8)}\u2026 input=${inputHash.slice(0, 8)}\u2026 file=${PIN_FILE}`);
|
|
25897
26015
|
return Response.json({ error: "Invalid PIN" }, { status: 401 });
|
|
25898
26016
|
}
|
|
25899
26017
|
const accountId = getDefaultAccountId() ?? "default";
|
|
25900
|
-
|
|
25901
|
-
|
|
26018
|
+
return createAdminSession(accountId);
|
|
26019
|
+
}
|
|
26020
|
+
async function createAdminSession(accountId, thinkingView, userId, userName) {
|
|
25902
26021
|
const account = resolveAccount();
|
|
25903
|
-
const
|
|
26022
|
+
const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
|
|
26023
|
+
const sessionKey = crypto.randomUUID();
|
|
26024
|
+
registerSession(sessionKey, "admin", accountId, void 0, userId, userName);
|
|
25904
26025
|
let onboardingComplete = true;
|
|
25905
26026
|
try {
|
|
25906
26027
|
const step = await loadOnboardingStep(accountId);
|
|
25907
26028
|
onboardingComplete = step === null || step >= 7;
|
|
25908
|
-
console.log(`[session] accountId=${accountId} onboardingComplete=${onboardingComplete}`);
|
|
25909
26029
|
} catch (err) {
|
|
25910
26030
|
console.error(`[session] onboarding query failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
25911
26031
|
}
|
|
@@ -25915,11 +26035,13 @@ async function POST19(req) {
|
|
|
25915
26035
|
businessName = branding?.name || void 0;
|
|
25916
26036
|
} catch {
|
|
25917
26037
|
}
|
|
25918
|
-
console.log(`[session]
|
|
26038
|
+
console.log(`[session] admin session created: userId=${userId ?? "\u2013"} userName=${userName ?? "\u2013"} accountId=${accountId}`);
|
|
25919
26039
|
return Response.json({
|
|
25920
26040
|
session_key: sessionKey,
|
|
25921
26041
|
agent_id: "admin",
|
|
25922
|
-
|
|
26042
|
+
userId,
|
|
26043
|
+
userName,
|
|
26044
|
+
thinkingView: effectiveThinkingView,
|
|
25923
26045
|
onboardingComplete,
|
|
25924
26046
|
businessName
|
|
25925
26047
|
});
|
|
@@ -26433,10 +26555,10 @@ async function GET8() {
|
|
|
26433
26555
|
// app/api/admin/version/route.ts
|
|
26434
26556
|
import { readFileSync as readFileSync17, existsSync as existsSync17 } from "fs";
|
|
26435
26557
|
import { resolve as resolve16, join as join7 } from "path";
|
|
26436
|
-
var
|
|
26558
|
+
var PLATFORM_ROOT8 = process.env.MAXY_PLATFORM_ROOT ?? resolve16(process.cwd(), "..");
|
|
26437
26559
|
var brandHostname = "maxy";
|
|
26438
26560
|
var brandNpmPackage = "@rubytech/create-maxy";
|
|
26439
|
-
var brandJsonPath = join7(
|
|
26561
|
+
var brandJsonPath = join7(PLATFORM_ROOT8, "config", "brand.json");
|
|
26440
26562
|
if (existsSync17(brandJsonPath)) {
|
|
26441
26563
|
try {
|
|
26442
26564
|
const brand = JSON.parse(readFileSync17(brandJsonPath, "utf-8"));
|
|
@@ -26445,7 +26567,7 @@ if (existsSync17(brandJsonPath)) {
|
|
|
26445
26567
|
} catch {
|
|
26446
26568
|
}
|
|
26447
26569
|
}
|
|
26448
|
-
var VERSION_FILE = resolve16(
|
|
26570
|
+
var VERSION_FILE = resolve16(PLATFORM_ROOT8, `config/.${brandHostname}-version`);
|
|
26449
26571
|
var NPM_PACKAGE = brandNpmPackage;
|
|
26450
26572
|
var REGISTRY_URL = `https://registry.npmjs.org/${NPM_PACKAGE}/latest`;
|
|
26451
26573
|
var CACHE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -26509,10 +26631,10 @@ async function GET9() {
|
|
|
26509
26631
|
import { spawn as spawn4 } from "child_process";
|
|
26510
26632
|
import { existsSync as existsSync18, statSync as statSync4, writeFileSync as writeFileSync10, readFileSync as readFileSync18, openSync as openSync2, closeSync as closeSync2 } from "fs";
|
|
26511
26633
|
import { resolve as resolve17, join as join8 } from "path";
|
|
26512
|
-
var
|
|
26634
|
+
var PLATFORM_ROOT9 = process.env.MAXY_PLATFORM_ROOT ?? resolve17(process.cwd(), "..");
|
|
26513
26635
|
var upgradePkg = "@rubytech/create-maxy";
|
|
26514
26636
|
var upgradeHostname = "maxy";
|
|
26515
|
-
var brandPath = join8(
|
|
26637
|
+
var brandPath = join8(PLATFORM_ROOT9, "config", "brand.json");
|
|
26516
26638
|
if (existsSync18(brandPath)) {
|
|
26517
26639
|
try {
|
|
26518
26640
|
const brand = JSON.parse(readFileSync18(brandPath, "utf-8"));
|
|
@@ -26591,8 +26713,12 @@ async function GET10(req) {
|
|
|
26591
26713
|
if (!accountId) {
|
|
26592
26714
|
return Response.json({ error: "Account not found for session" }, { status: 401 });
|
|
26593
26715
|
}
|
|
26716
|
+
const userId = getUserIdForSession(sessionKey);
|
|
26717
|
+
if (!userId) {
|
|
26718
|
+
return Response.json({ error: "User identity required \u2014 authenticate with users.json PIN" }, { status: 401 });
|
|
26719
|
+
}
|
|
26594
26720
|
try {
|
|
26595
|
-
const sessions = await listAdminSessions(accountId, 20);
|
|
26721
|
+
const sessions = await listAdminSessions(accountId, userId, 20);
|
|
26596
26722
|
return Response.json({ sessions });
|
|
26597
26723
|
} catch (err) {
|
|
26598
26724
|
console.error(`[sessions-list] Failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -26678,8 +26804,8 @@ async function POST23() {
|
|
|
26678
26804
|
}
|
|
26679
26805
|
|
|
26680
26806
|
// server/index.ts
|
|
26681
|
-
var
|
|
26682
|
-
var BRAND_JSON_PATH =
|
|
26807
|
+
var PLATFORM_ROOT10 = process.env.MAXY_PLATFORM_ROOT || "";
|
|
26808
|
+
var BRAND_JSON_PATH = PLATFORM_ROOT10 ? join9(PLATFORM_ROOT10, "config", "brand.json") : "";
|
|
26683
26809
|
var BRAND = { productName: "Maxy", hostname: "maxy", configDir: ".maxy", domain: "getmaxy.com" };
|
|
26684
26810
|
if (BRAND_JSON_PATH && !existsSync19(BRAND_JSON_PATH)) {
|
|
26685
26811
|
console.error(`[brand] WARNING: brand.json not found at ${BRAND_JSON_PATH} \u2014 using Maxy defaults`);
|