@rubytech/create-maxy-code 0.1.439 → 0.1.440
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/plugins/admin/skills/platform-architecture/SKILL.md +14 -6
- package/payload/platform/plugins/docs/references/admin-identity-gate.md +32 -17
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/internals.md +2 -2
- package/payload/platform/plugins/docs/references/outlook-guide.md +2 -2
- package/payload/platform/plugins/docs/references/troubleshooting.md +8 -0
- package/payload/platform/plugins/graph/.claude-plugin/plugin.json +19 -0
- package/payload/platform/plugins/graph/PLUGIN.md +10 -4
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.d.ts +2 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.d.ts.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.js +55 -0
- package/payload/platform/plugins/outlook/mcp/dist/__tests__/mail-list.test.js.map +1 -0
- package/payload/platform/plugins/outlook/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts +17 -0
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.d.ts.map +1 -1
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js +84 -21
- package/payload/platform/plugins/outlook/mcp/dist/tools/mail-list.js.map +1 -1
- package/payload/platform/plugins/outlook/references/graph-surfaces.md +15 -1
- package/payload/platform/scripts/__tests__/account-schema-owned-dirs.test.sh +100 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.py +207 -0
- package/payload/platform/scripts/lib/account-schema-owned-dirs.sh +33 -0
- package/payload/platform/scripts/lib/provision-account-dir.sh +7 -0
- package/payload/platform/scripts/setup-account.sh +10 -0
- package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/http-server.js +6 -4
- package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.d.ts +7 -6
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.d.ts.map +1 -1
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.js +56 -7
- package/payload/platform/services/claude-session-manager/dist/tool-registration-drift.js.map +1 -1
- package/payload/server/{chunk-UGPQCVIW.js → chunk-WC4WJR6X.js} +0 -6
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/assets/{AdminLoginScreens-DeR71H29.js → AdminLoginScreens-CukwQufW.js} +1 -1
- package/payload/server/public/assets/AdminShell-BwEBH6tT.js +2 -0
- package/payload/server/public/assets/{admin-C15h-NL0.js → admin-CdFRCUUZ.js} +1 -1
- package/payload/server/public/assets/{browser-B7xQLIRK.js → browser-BWR59IkB.js} +1 -1
- package/payload/server/public/assets/{calendar-A-vGm6pL.js → calendar-kuTcxX1t.js} +1 -1
- package/payload/server/public/assets/chat-ejnq2rck.js +1 -0
- package/payload/server/public/assets/data-dZbOyLdT.js +1 -0
- package/payload/server/public/assets/{graph-D0orIRAh.js → graph-NgQDY0Pb.js} +1 -1
- package/payload/server/public/assets/{operator-DZzuotDB.js → operator-oSaB1mya.js} +1 -1
- package/payload/server/public/assets/{page-Ck_pLhAk.js → page-DEkI-Gl1.js} +1 -1
- package/payload/server/public/assets/{page-3g0A19Rv.js → page-DMeJHVfA.js} +5 -5
- package/payload/server/public/assets/{public-WFUQw9AD.js → public-C54tl6V4.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-CSk9KjhY.js → useCopyFeedback-DHoYHiK9.js} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-DCyJQkFK.js → useVoiceRecorder-b7HzTFs0.js} +1 -1
- package/payload/server/public/browser.html +2 -2
- package/payload/server/public/calendar.html +2 -2
- package/payload/server/public/chat.html +5 -5
- package/payload/server/public/data.html +3 -3
- package/payload/server/public/graph.html +3 -3
- package/payload/server/public/index.html +5 -5
- package/payload/server/public/operator.html +6 -6
- package/payload/server/public/public.html +5 -5
- package/payload/server/server.js +186 -112
- package/payload/server/public/assets/AdminShell-DlAVQUda.js +0 -2
- package/payload/server/public/assets/chat-DAae6Z51.js +0 -1
- package/payload/server/public/assets/data-BNWpKx1d.js +0 -1
package/payload/server/server.js
CHANGED
|
@@ -89,7 +89,6 @@ import {
|
|
|
89
89
|
resolveCallback,
|
|
90
90
|
resolveClientIp,
|
|
91
91
|
resolveDefaultAgentSlug,
|
|
92
|
-
retagAccountForUser,
|
|
93
92
|
runAdminUserSelfHeal,
|
|
94
93
|
runUserProfileReconcile,
|
|
95
94
|
safeJson,
|
|
@@ -111,7 +110,7 @@ import {
|
|
|
111
110
|
vncLog,
|
|
112
111
|
walkPremiumBundles,
|
|
113
112
|
writeAdminUserAndPerson
|
|
114
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-WC4WJR6X.js";
|
|
115
114
|
import {
|
|
116
115
|
__commonJS,
|
|
117
116
|
__toESM
|
|
@@ -126,7 +125,7 @@ var require_dist = __commonJS({
|
|
|
126
125
|
exports.cosine = cosine;
|
|
127
126
|
exports.deconflictBySubject = deconflictBySubject;
|
|
128
127
|
exports.formatStandingRulesBlock = formatStandingRulesBlock2;
|
|
129
|
-
exports.resolveOwnerUserId =
|
|
128
|
+
exports.resolveOwnerUserId = resolveOwnerUserId2;
|
|
130
129
|
exports.queryActiveRuleCandidates = queryActiveRuleCandidates;
|
|
131
130
|
exports.resolveActiveRules = resolveActiveRules2;
|
|
132
131
|
exports.activeRuleSet = activeRuleSet;
|
|
@@ -166,7 +165,7 @@ var require_dist = __commonJS({
|
|
|
166
165
|
return `## Standing rules
|
|
167
166
|
${lines.join("\n")}`;
|
|
168
167
|
}
|
|
169
|
-
async function
|
|
168
|
+
async function resolveOwnerUserId2(session, accountId) {
|
|
170
169
|
const res = await session.run(`
|
|
171
170
|
MATCH (au:AdminUser {accountId: $accountId, role: 'owner'})
|
|
172
171
|
RETURN au.userId AS userId
|
|
@@ -202,7 +201,7 @@ ${lines.join("\n")}`;
|
|
|
202
201
|
async function resolveCandidates(session, accountId) {
|
|
203
202
|
let ownerUserId;
|
|
204
203
|
try {
|
|
205
|
-
ownerUserId = await
|
|
204
|
+
ownerUserId = await resolveOwnerUserId2(session, accountId);
|
|
206
205
|
} catch {
|
|
207
206
|
return { candidates: [], ownerUserId: null, source: "owner-error" };
|
|
208
207
|
}
|
|
@@ -4756,7 +4755,10 @@ function buildSelfChatDispatch(input) {
|
|
|
4756
4755
|
}),
|
|
4757
4756
|
media: [],
|
|
4758
4757
|
// Self-chat is the owner: the session scopes to the house account (Task 1383).
|
|
4759
|
-
effectiveAccountId: input.accountId
|
|
4758
|
+
effectiveAccountId: input.accountId,
|
|
4759
|
+
// Task 1575 — self-chat is unambiguously the owner, so attribute the spawn
|
|
4760
|
+
// to the owner userId even when the owner phone is unbound in users.json.
|
|
4761
|
+
ownerSelfPhone: true
|
|
4760
4762
|
};
|
|
4761
4763
|
}
|
|
4762
4764
|
async function init(opts) {
|
|
@@ -5566,7 +5568,11 @@ async function handleInboundMessage(conn, msg) {
|
|
|
5566
5568
|
replyContext: extracted.quotedMessage,
|
|
5567
5569
|
// Task 1383 — the gate's single resolution feeds the spawn (house or the
|
|
5568
5570
|
// bound sub-account); the router never re-resolves.
|
|
5569
|
-
effectiveAccountId: accessResult.effectiveAccountId
|
|
5571
|
+
effectiveAccountId: accessResult.effectiveAccountId,
|
|
5572
|
+
// Task 1575 — the gate's owner-self verdict. Only reason:'self-phone' is the
|
|
5573
|
+
// owner's own number; adminPhones/account-manager admits are other admins
|
|
5574
|
+
// and must not inherit the owner userId.
|
|
5575
|
+
ownerSelfPhone: accessResult.reason === "self-phone"
|
|
5570
5576
|
};
|
|
5571
5577
|
if (accessResult.agentType === "public") {
|
|
5572
5578
|
const hoursResult = await isBusinessOpen(conn.accountId);
|
|
@@ -8199,8 +8205,8 @@ import { readFileSync as readFileSync15, watch, statSync as statSync5, openSync,
|
|
|
8199
8205
|
import { basename as basename4, dirname as dirname4, isAbsolute, join as join15, relative as relative2, resolve as resolve12, sep as sep3 } from "path";
|
|
8200
8206
|
|
|
8201
8207
|
// server/routes/admin/sidebar-sessions.ts
|
|
8202
|
-
import { readdirSync as
|
|
8203
|
-
import { dirname as dirname3, join as
|
|
8208
|
+
import { readdirSync as readdirSync7, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
|
|
8209
|
+
import { dirname as dirname3, join as join13, resolve as resolve11 } from "path";
|
|
8204
8210
|
|
|
8205
8211
|
// ../services/claude-session-manager/src/jsonl-path.ts
|
|
8206
8212
|
import { existsSync as existsSync8, readdirSync as readdirSync5 } from "fs";
|
|
@@ -8225,6 +8231,96 @@ function findExistingJsonlForSessionId(claudeConfigDir2, sessionId) {
|
|
|
8225
8231
|
return null;
|
|
8226
8232
|
}
|
|
8227
8233
|
|
|
8234
|
+
// app/lib/admin-identity/pin-validator.ts
|
|
8235
|
+
import { createHash as createHash3 } from "crypto";
|
|
8236
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
|
|
8237
|
+
import { join as join12 } from "path";
|
|
8238
|
+
function hashPin(pin) {
|
|
8239
|
+
return createHash3("sha256").update(pin).digest("hex");
|
|
8240
|
+
}
|
|
8241
|
+
function readUsersFile(usersFilePath) {
|
|
8242
|
+
if (!existsSync9(usersFilePath)) return null;
|
|
8243
|
+
const raw = readFileSync12(usersFilePath, "utf-8").trim();
|
|
8244
|
+
if (!raw) return [];
|
|
8245
|
+
return JSON.parse(raw);
|
|
8246
|
+
}
|
|
8247
|
+
function validatePin(pin, usersFilePath) {
|
|
8248
|
+
let users;
|
|
8249
|
+
try {
|
|
8250
|
+
users = readUsersFile(usersFilePath);
|
|
8251
|
+
} catch (err) {
|
|
8252
|
+
return { kind: "corrupt", message: err instanceof Error ? err.message : String(err) };
|
|
8253
|
+
}
|
|
8254
|
+
if (users === null || users.length === 0) return { kind: "unavailable" };
|
|
8255
|
+
const inputHash = hashPin(pin);
|
|
8256
|
+
const matched = users.find((u) => u.pin === inputHash);
|
|
8257
|
+
return matched ? { kind: "match", userId: matched.userId } : { kind: "miss" };
|
|
8258
|
+
}
|
|
8259
|
+
|
|
8260
|
+
// app/lib/whatsapp/inbound/resolve-admin-userid.ts
|
|
8261
|
+
function classifyAdminUserId(users, admins, senderPhone) {
|
|
8262
|
+
const matched = users.find((u) => typeof u.phone === "string" && phonesMatch(u.phone, senderPhone));
|
|
8263
|
+
if (!matched) return { kind: "no-users-binding" };
|
|
8264
|
+
return admins.some((a) => a.userId === matched.userId) ? { kind: "resolved", userId: matched.userId } : { kind: "userid-not-admin", userId: matched.userId };
|
|
8265
|
+
}
|
|
8266
|
+
function pickAdminUserId(users, admins, senderPhone) {
|
|
8267
|
+
const r = classifyAdminUserId(users, admins, senderPhone);
|
|
8268
|
+
return r.kind === "resolved" ? r.userId : null;
|
|
8269
|
+
}
|
|
8270
|
+
function pickOwnerUserId(admins) {
|
|
8271
|
+
return admins.find((a) => a.role === "owner")?.userId ?? null;
|
|
8272
|
+
}
|
|
8273
|
+
function resolveOwnerUserId(accountId) {
|
|
8274
|
+
try {
|
|
8275
|
+
const account = listValidAccounts().find((a) => a.accountId === accountId);
|
|
8276
|
+
return pickOwnerUserId(account?.config.admins ?? []);
|
|
8277
|
+
} catch {
|
|
8278
|
+
return null;
|
|
8279
|
+
}
|
|
8280
|
+
}
|
|
8281
|
+
function resolveAdminUserId(params) {
|
|
8282
|
+
try {
|
|
8283
|
+
const users = readUsersFile(USERS_FILE);
|
|
8284
|
+
if (!users) return null;
|
|
8285
|
+
const account = listValidAccounts().find((a) => a.accountId === params.accountId);
|
|
8286
|
+
const admins = account?.config.admins ?? [];
|
|
8287
|
+
const direct = pickAdminUserId(users, admins, params.senderPhone);
|
|
8288
|
+
if (direct) return direct;
|
|
8289
|
+
const mapped = resolveViaLidMap(readLidMap() ?? {}, params.senderPhone);
|
|
8290
|
+
if (!mapped) return null;
|
|
8291
|
+
const viaLid = pickAdminUserId(users, admins, mapped);
|
|
8292
|
+
if (viaLid) {
|
|
8293
|
+
console.error(`[lid-map] resolved-via-cache lid=${params.senderPhone} userId=${viaLid.slice(0, 8)}`);
|
|
8294
|
+
}
|
|
8295
|
+
return viaLid;
|
|
8296
|
+
} catch {
|
|
8297
|
+
return null;
|
|
8298
|
+
}
|
|
8299
|
+
}
|
|
8300
|
+
|
|
8301
|
+
// app/lib/whatsapp-reader/resolve-operator-name.ts
|
|
8302
|
+
function resolveOperatorDisplay(userIdRes, nameRes) {
|
|
8303
|
+
if (userIdRes.kind === "no-users-binding") return { name: null, reason: "no-binding" };
|
|
8304
|
+
if (userIdRes.kind === "userid-not-admin") return { name: null, reason: "not-admin" };
|
|
8305
|
+
if (nameRes && nameRes.source === "neo4j") return { name: nameRes.joined, reason: null };
|
|
8306
|
+
if (nameRes && nameRes.reason === "neo4j-unreachable") return { name: null, reason: "neo4j-unreachable" };
|
|
8307
|
+
return { name: null, reason: "no-person-name" };
|
|
8308
|
+
}
|
|
8309
|
+
|
|
8310
|
+
// app/lib/whatsapp-reader/resolve-operator-names.ts
|
|
8311
|
+
async function resolveOperatorNamesBySender(users, admins, senderIds) {
|
|
8312
|
+
const out = /* @__PURE__ */ new Map();
|
|
8313
|
+
const distinct = [...new Set([...senderIds].filter((s) => !!s))];
|
|
8314
|
+
await Promise.all(
|
|
8315
|
+
distinct.map(async (senderId) => {
|
|
8316
|
+
const idRes = classifyAdminUserId(users, admins, senderId);
|
|
8317
|
+
const nameRes = idRes.kind === "resolved" ? await loadAdminUserName(idRes.userId) : null;
|
|
8318
|
+
out.set(senderId, resolveOperatorDisplay(idRes, nameRes));
|
|
8319
|
+
})
|
|
8320
|
+
);
|
|
8321
|
+
return out;
|
|
8322
|
+
}
|
|
8323
|
+
|
|
8228
8324
|
// server/routes/admin/sidebar-sessions.ts
|
|
8229
8325
|
var SESSION_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
8230
8326
|
function lastAssistantModel(body) {
|
|
@@ -8261,16 +8357,16 @@ function enumerateJsonls(projectsRoot) {
|
|
|
8261
8357
|
const out = [];
|
|
8262
8358
|
let slugs;
|
|
8263
8359
|
try {
|
|
8264
|
-
slugs =
|
|
8360
|
+
slugs = readdirSync7(projectsRoot, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => d.name);
|
|
8265
8361
|
} catch (err) {
|
|
8266
8362
|
if (err.code === "ENOENT") return out;
|
|
8267
8363
|
throw err;
|
|
8268
8364
|
}
|
|
8269
8365
|
for (const slug of slugs) {
|
|
8270
|
-
const slugDir =
|
|
8366
|
+
const slugDir = join13(projectsRoot, slug);
|
|
8271
8367
|
let entries;
|
|
8272
8368
|
try {
|
|
8273
|
-
entries =
|
|
8369
|
+
entries = readdirSync7(slugDir, { withFileTypes: true });
|
|
8274
8370
|
} catch (err) {
|
|
8275
8371
|
const code = err.code ?? "unknown";
|
|
8276
8372
|
console.error(`[admin-sessions-list] slug-skipped slug=${slug} code=${code}`);
|
|
@@ -8278,12 +8374,12 @@ function enumerateJsonls(projectsRoot) {
|
|
|
8278
8374
|
}
|
|
8279
8375
|
for (const entry of entries) {
|
|
8280
8376
|
if (entry.isFile() && SESSION_ID_RE.test(entry.name)) {
|
|
8281
|
-
out.push({ path:
|
|
8377
|
+
out.push({ path: join13(slugDir, entry.name), isSubagent: false, archived: false });
|
|
8282
8378
|
} else if (entry.isDirectory() && entry.name === "subagents") {
|
|
8283
|
-
const subDir =
|
|
8379
|
+
const subDir = join13(slugDir, entry.name);
|
|
8284
8380
|
let subEntries;
|
|
8285
8381
|
try {
|
|
8286
|
-
subEntries =
|
|
8382
|
+
subEntries = readdirSync7(subDir, { withFileTypes: true });
|
|
8287
8383
|
} catch (err) {
|
|
8288
8384
|
const code = err.code ?? "unknown";
|
|
8289
8385
|
console.error(`[admin-sessions-list] subagents-skipped slug=${slug} code=${code}`);
|
|
@@ -8291,14 +8387,14 @@ function enumerateJsonls(projectsRoot) {
|
|
|
8291
8387
|
}
|
|
8292
8388
|
for (const sub of subEntries) {
|
|
8293
8389
|
if (sub.isFile() && SESSION_ID_RE.test(sub.name)) {
|
|
8294
|
-
out.push({ path:
|
|
8390
|
+
out.push({ path: join13(subDir, sub.name), isSubagent: true, archived: false });
|
|
8295
8391
|
}
|
|
8296
8392
|
}
|
|
8297
8393
|
} else if (entry.isDirectory() && entry.name === "archive") {
|
|
8298
|
-
const archiveDir =
|
|
8394
|
+
const archiveDir = join13(slugDir, entry.name);
|
|
8299
8395
|
let archiveEntries;
|
|
8300
8396
|
try {
|
|
8301
|
-
archiveEntries =
|
|
8397
|
+
archiveEntries = readdirSync7(archiveDir, { withFileTypes: true });
|
|
8302
8398
|
} catch (err) {
|
|
8303
8399
|
const code = err.code ?? "unknown";
|
|
8304
8400
|
console.error(`[admin-sessions-list] archive-skipped slug=${slug} code=${code}`);
|
|
@@ -8306,7 +8402,7 @@ function enumerateJsonls(projectsRoot) {
|
|
|
8306
8402
|
}
|
|
8307
8403
|
for (const arc of archiveEntries) {
|
|
8308
8404
|
if (arc.isFile() && SESSION_ID_RE.test(arc.name)) {
|
|
8309
|
-
out.push({ path:
|
|
8405
|
+
out.push({ path: join13(archiveDir, arc.name), isSubagent: false, archived: true });
|
|
8310
8406
|
}
|
|
8311
8407
|
}
|
|
8312
8408
|
}
|
|
@@ -8339,10 +8435,10 @@ async function fetchLiveSessions() {
|
|
|
8339
8435
|
function loadUserTitles(accountDir) {
|
|
8340
8436
|
const out = /* @__PURE__ */ new Map();
|
|
8341
8437
|
if (!accountDir) return out;
|
|
8342
|
-
const path2 =
|
|
8438
|
+
const path2 = join13(accountDir, "session-titles.json");
|
|
8343
8439
|
let raw;
|
|
8344
8440
|
try {
|
|
8345
|
-
raw =
|
|
8441
|
+
raw = readFileSync13(path2, "utf8");
|
|
8346
8442
|
} catch {
|
|
8347
8443
|
return out;
|
|
8348
8444
|
}
|
|
@@ -8368,7 +8464,7 @@ function readSidecarMeta(metaPath) {
|
|
|
8368
8464
|
const empty = { bridgeIds: [], role: null, channel: null, senderId: null, startedAt: null, personId: null, adminUserId: null, visitorId: null, agentSlug: null, accountId: null };
|
|
8369
8465
|
let raw;
|
|
8370
8466
|
try {
|
|
8371
|
-
raw =
|
|
8467
|
+
raw = readFileSync13(metaPath, "utf8");
|
|
8372
8468
|
} catch {
|
|
8373
8469
|
return empty;
|
|
8374
8470
|
}
|
|
@@ -8466,11 +8562,22 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8466
8562
|
console.error("[admin-sessions-list] CLAUDE_CONFIG_DIR not set; returning empty list");
|
|
8467
8563
|
return c.json({ sessions: [], accountId: scopeAccountId });
|
|
8468
8564
|
}
|
|
8469
|
-
const projectsRoot =
|
|
8565
|
+
const projectsRoot = join13(configDir2, "projects");
|
|
8470
8566
|
const jsonls = enumerateJsonls(projectsRoot);
|
|
8471
8567
|
const liveSessions = await fetchLiveSessions();
|
|
8472
8568
|
const accountDir = scopeAccountId ? resolve11(ACCOUNTS_DIR, scopeAccountId) : null;
|
|
8473
8569
|
const userTitles = loadUserTitles(accountDir);
|
|
8570
|
+
const account = multiAccount ? validAccounts.find((a) => a.accountId === scopeAccountId) ?? null : resolveAccount();
|
|
8571
|
+
let channelUsers = [];
|
|
8572
|
+
if (account) {
|
|
8573
|
+
try {
|
|
8574
|
+
channelUsers = readUsersFile(USERS_FILE) ?? [];
|
|
8575
|
+
} catch (err) {
|
|
8576
|
+
console.error(`[admin-sessions-list] op=users-file-unreadable reason=${err instanceof Error ? err.message : String(err)}`);
|
|
8577
|
+
channelUsers = [];
|
|
8578
|
+
}
|
|
8579
|
+
}
|
|
8580
|
+
const channelAdmins = account?.config.admins ?? [];
|
|
8474
8581
|
const rows = [];
|
|
8475
8582
|
const seenIds = /* @__PURE__ */ new Set();
|
|
8476
8583
|
let liveCount = 0;
|
|
@@ -8484,6 +8591,8 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8484
8591
|
const titleSourceCounts = { user: 0, ai: 0, "first-message": 0, prefix: 0 };
|
|
8485
8592
|
const personIdBySession = /* @__PURE__ */ new Map();
|
|
8486
8593
|
const adminUserIdBySession = /* @__PURE__ */ new Map();
|
|
8594
|
+
const channelSenderBySession = /* @__PURE__ */ new Map();
|
|
8595
|
+
const channelMissNoSender = [];
|
|
8487
8596
|
for (const { path: path2, isSubagent, archived } of jsonls) {
|
|
8488
8597
|
const lastSlash = path2.lastIndexOf("/");
|
|
8489
8598
|
const base = path2.slice(lastSlash + 1);
|
|
@@ -8497,8 +8606,8 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8497
8606
|
let body;
|
|
8498
8607
|
let mtimeMs;
|
|
8499
8608
|
try {
|
|
8500
|
-
body =
|
|
8501
|
-
mtimeMs =
|
|
8609
|
+
body = readFileSync13(path2, "utf8");
|
|
8610
|
+
mtimeMs = statSync4(path2).mtimeMs;
|
|
8502
8611
|
} catch {
|
|
8503
8612
|
continue;
|
|
8504
8613
|
}
|
|
@@ -8506,8 +8615,8 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8506
8615
|
const pid = live ? liveSessions.get(sessionId) ?? null : null;
|
|
8507
8616
|
const resolved = resolveTitle(body, sessionId, userTitles);
|
|
8508
8617
|
titleSourceCounts[resolved.source] += 1;
|
|
8509
|
-
const metaPath =
|
|
8510
|
-
const { bridgeIds, role, channel: sidecarChannel, personId, adminUserId, accountId: rowAccountId } = readSidecarMeta(metaPath);
|
|
8618
|
+
const metaPath = join13(projectDir, `${sessionId}.meta.json`);
|
|
8619
|
+
const { bridgeIds, role, channel: sidecarChannel, senderId, personId, adminUserId, accountId: rowAccountId } = readSidecarMeta(metaPath);
|
|
8511
8620
|
if (multiAccount) {
|
|
8512
8621
|
if (rowAccountId === null) untaggedCount += 1;
|
|
8513
8622
|
if (rowAccountId !== scopeAccountId) continue;
|
|
@@ -8539,8 +8648,13 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8539
8648
|
channel: sidecarChannel,
|
|
8540
8649
|
personName: null,
|
|
8541
8650
|
model: lastAssistantModel(body),
|
|
8542
|
-
accountId: rowAccountId
|
|
8651
|
+
accountId: rowAccountId,
|
|
8652
|
+
senderId
|
|
8543
8653
|
});
|
|
8654
|
+
if ((sidecarChannel === "whatsapp" || sidecarChannel === "telegram") && resolved.source === "prefix") {
|
|
8655
|
+
if (senderId) channelSenderBySession.set(sessionId, senderId);
|
|
8656
|
+
else channelMissNoSender.push({ sessionId, channel: sidecarChannel });
|
|
8657
|
+
}
|
|
8544
8658
|
if (sidecarChannel === "whatsapp") channelInList.whatsapp += 1;
|
|
8545
8659
|
else if (sidecarChannel === "telegram") channelInList.telegram += 1;
|
|
8546
8660
|
else if (sidecarChannel === "webchat") channelInList.webchat += 1;
|
|
@@ -8575,9 +8689,29 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8575
8689
|
})
|
|
8576
8690
|
);
|
|
8577
8691
|
}
|
|
8692
|
+
let channelNamesResolved = 0;
|
|
8693
|
+
const channelNameCandidates = channelSenderBySession.size + channelMissNoSender.length;
|
|
8694
|
+
if (account && channelSenderBySession.size > 0) {
|
|
8695
|
+
const resolvedNames = await resolveOperatorNamesBySender(channelUsers, channelAdmins, channelSenderBySession.values());
|
|
8696
|
+
for (const row of rows) {
|
|
8697
|
+
const rowSenderId = channelSenderBySession.get(row.sessionId);
|
|
8698
|
+
if (!rowSenderId) continue;
|
|
8699
|
+
const r = resolvedNames.get(rowSenderId) ?? { name: null, reason: "no-binding" };
|
|
8700
|
+
if (r.name) {
|
|
8701
|
+
row.personName = r.name;
|
|
8702
|
+
channelNamesResolved += 1;
|
|
8703
|
+
} else {
|
|
8704
|
+
const reason = r.reason === "neo4j-unreachable" ? "neo4j-down" : "classify-miss";
|
|
8705
|
+
console.error(`[admin-sessions-list] op=channel-name-unresolved sessionId=${row.sessionId.slice(0, 8)} channel=${row.channel} senderId=${rowSenderId} reason=${reason}`);
|
|
8706
|
+
}
|
|
8707
|
+
}
|
|
8708
|
+
}
|
|
8709
|
+
for (const m of channelMissNoSender) {
|
|
8710
|
+
console.error(`[admin-sessions-list] op=channel-name-unresolved sessionId=${m.sessionId.slice(0, 8)} channel=${m.channel} senderId= reason=no-senderId`);
|
|
8711
|
+
}
|
|
8578
8712
|
rows.sort((a, b) => a.startedAt < b.startedAt ? 1 : -1);
|
|
8579
8713
|
console.log(
|
|
8580
|
-
`[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} channelInList=whatsapp:${channelInList.whatsapp},telegram:${channelInList.telegram},webchat:${channelInList.webchat} unknownChannel=${unknownChannelCount} excludedPublicWebchat=${excludedPublicWebchatCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} personNames=${personNameCount} adminNames=${adminNameCount} accountId=${scopeAccountId ? scopeAccountId.slice(0, 8) : "unset"}`
|
|
8714
|
+
`[admin-sessions-list] rows=${rows.length} live=${liveCount} subagents=${subagentCount} archived=${archivedCount} channelInList=whatsapp:${channelInList.whatsapp},telegram:${channelInList.telegram},webchat:${channelInList.webchat} channelNameCandidates=${channelNameCandidates} channelNamesResolved=${channelNamesResolved} unknownChannel=${unknownChannelCount} excludedPublicWebchat=${excludedPublicWebchatCount} titles user=${titleSourceCounts.user} ai=${titleSourceCounts.ai} first=${titleSourceCounts["first-message"]} prefix=${titleSourceCounts.prefix} personNames=${personNameCount} adminNames=${adminNameCount} accountId=${scopeAccountId ? scopeAccountId.slice(0, 8) : "unset"}`
|
|
8581
8715
|
);
|
|
8582
8716
|
console.log(
|
|
8583
8717
|
`[admin-sessions-list] accountId=${scopeAccountId ? scopeAccountId.slice(0, 8) : "unset"} scope=${multiAccount ? "scoped" : "all"} rows=${rows.length}`
|
|
@@ -8589,71 +8723,6 @@ app3.get("/", requireAdminSession, async (c) => {
|
|
|
8589
8723
|
});
|
|
8590
8724
|
var sidebar_sessions_default = app3;
|
|
8591
8725
|
|
|
8592
|
-
// app/lib/admin-identity/pin-validator.ts
|
|
8593
|
-
import { createHash as createHash3 } from "crypto";
|
|
8594
|
-
import { existsSync as existsSync9, readFileSync as readFileSync13, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
|
|
8595
|
-
import { join as join13 } from "path";
|
|
8596
|
-
function hashPin(pin) {
|
|
8597
|
-
return createHash3("sha256").update(pin).digest("hex");
|
|
8598
|
-
}
|
|
8599
|
-
function readUsersFile(usersFilePath) {
|
|
8600
|
-
if (!existsSync9(usersFilePath)) return null;
|
|
8601
|
-
const raw = readFileSync13(usersFilePath, "utf-8").trim();
|
|
8602
|
-
if (!raw) return [];
|
|
8603
|
-
return JSON.parse(raw);
|
|
8604
|
-
}
|
|
8605
|
-
function validatePin(pin, usersFilePath) {
|
|
8606
|
-
let users;
|
|
8607
|
-
try {
|
|
8608
|
-
users = readUsersFile(usersFilePath);
|
|
8609
|
-
} catch (err) {
|
|
8610
|
-
return { kind: "corrupt", message: err instanceof Error ? err.message : String(err) };
|
|
8611
|
-
}
|
|
8612
|
-
if (users === null || users.length === 0) return { kind: "unavailable" };
|
|
8613
|
-
const inputHash = hashPin(pin);
|
|
8614
|
-
const matched = users.find((u) => u.pin === inputHash);
|
|
8615
|
-
return matched ? { kind: "match", userId: matched.userId } : { kind: "miss" };
|
|
8616
|
-
}
|
|
8617
|
-
|
|
8618
|
-
// app/lib/whatsapp/inbound/resolve-admin-userid.ts
|
|
8619
|
-
function classifyAdminUserId(users, admins, senderPhone) {
|
|
8620
|
-
const matched = users.find((u) => typeof u.phone === "string" && phonesMatch(u.phone, senderPhone));
|
|
8621
|
-
if (!matched) return { kind: "no-users-binding" };
|
|
8622
|
-
return admins.some((a) => a.userId === matched.userId) ? { kind: "resolved", userId: matched.userId } : { kind: "userid-not-admin", userId: matched.userId };
|
|
8623
|
-
}
|
|
8624
|
-
function pickAdminUserId(users, admins, senderPhone) {
|
|
8625
|
-
const r = classifyAdminUserId(users, admins, senderPhone);
|
|
8626
|
-
return r.kind === "resolved" ? r.userId : null;
|
|
8627
|
-
}
|
|
8628
|
-
function resolveAdminUserId(params) {
|
|
8629
|
-
try {
|
|
8630
|
-
const users = readUsersFile(USERS_FILE);
|
|
8631
|
-
if (!users) return null;
|
|
8632
|
-
const account = listValidAccounts().find((a) => a.accountId === params.accountId);
|
|
8633
|
-
const admins = account?.config.admins ?? [];
|
|
8634
|
-
const direct = pickAdminUserId(users, admins, params.senderPhone);
|
|
8635
|
-
if (direct) return direct;
|
|
8636
|
-
const mapped = resolveViaLidMap(readLidMap() ?? {}, params.senderPhone);
|
|
8637
|
-
if (!mapped) return null;
|
|
8638
|
-
const viaLid = pickAdminUserId(users, admins, mapped);
|
|
8639
|
-
if (viaLid) {
|
|
8640
|
-
console.error(`[lid-map] resolved-via-cache lid=${params.senderPhone} userId=${viaLid.slice(0, 8)}`);
|
|
8641
|
-
}
|
|
8642
|
-
return viaLid;
|
|
8643
|
-
} catch {
|
|
8644
|
-
return null;
|
|
8645
|
-
}
|
|
8646
|
-
}
|
|
8647
|
-
|
|
8648
|
-
// app/lib/whatsapp-reader/resolve-operator-name.ts
|
|
8649
|
-
function resolveOperatorDisplay(userIdRes, nameRes) {
|
|
8650
|
-
if (userIdRes.kind === "no-users-binding") return { name: null, reason: "no-binding" };
|
|
8651
|
-
if (userIdRes.kind === "userid-not-admin") return { name: null, reason: "not-admin" };
|
|
8652
|
-
if (nameRes && nameRes.source === "neo4j") return { name: nameRes.joined, reason: null };
|
|
8653
|
-
if (nameRes && nameRes.reason === "neo4j-unreachable") return { name: null, reason: "neo4j-unreachable" };
|
|
8654
|
-
return { name: null, reason: "no-person-name" };
|
|
8655
|
-
}
|
|
8656
|
-
|
|
8657
8726
|
// app/lib/agent-errors.ts
|
|
8658
8727
|
function classifyAgentError(err) {
|
|
8659
8728
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -9063,19 +9132,16 @@ app4.get("/conversations", requireAdminSession, async (c) => {
|
|
|
9063
9132
|
const admins = account?.config.admins ?? [];
|
|
9064
9133
|
const nameBySender = /* @__PURE__ */ new Map();
|
|
9065
9134
|
const distinct = [...new Set(sessionRows.map((r) => r.senderId).filter((s) => !!s))];
|
|
9066
|
-
|
|
9067
|
-
|
|
9068
|
-
|
|
9069
|
-
|
|
9070
|
-
return;
|
|
9071
|
-
}
|
|
9072
|
-
const idRes = classifyAdminUserId(users, admins, senderId);
|
|
9073
|
-
const nameRes = idRes.kind === "resolved" ? await loadAdminUserName(idRes.userId) : null;
|
|
9074
|
-
const { name, reason } = resolveOperatorDisplay(idRes, nameRes);
|
|
9135
|
+
if (account) {
|
|
9136
|
+
const resolved = await resolveOperatorNamesBySender(users, admins, distinct);
|
|
9137
|
+
for (const senderId of distinct) {
|
|
9138
|
+
const { name, reason } = resolved.get(senderId) ?? { name: null, reason: "no-binding" };
|
|
9075
9139
|
if (reason) console.error(`[wa-reader] op=operator-unresolved senderId=${senderId} reason=${reason}`);
|
|
9076
9140
|
nameBySender.set(senderId, name);
|
|
9077
|
-
}
|
|
9078
|
-
|
|
9141
|
+
}
|
|
9142
|
+
} else {
|
|
9143
|
+
for (const senderId of distinct) nameBySender.set(senderId, null);
|
|
9144
|
+
}
|
|
9079
9145
|
const sessionConversations = sessionRows.map((r) => ({
|
|
9080
9146
|
...r,
|
|
9081
9147
|
operatorName: r.senderId ? nameBySender.get(r.senderId) ?? null : null
|
|
@@ -12399,8 +12465,8 @@ app11.post("/switch-account", requireAdminSession, async (c) => {
|
|
|
12399
12465
|
const { userName, avatar } = await resolveUserIdentity(target.accountId, userId);
|
|
12400
12466
|
const payload = await createAdminSession(target.accountId, target.config.thinkingView, userId, userName, operatorRoleFor(target.config, userId), avatar);
|
|
12401
12467
|
unregisterSession(oldCacheKey);
|
|
12402
|
-
retagAccountForUser(userId, target.accountId);
|
|
12403
12468
|
console.log(`[sub-account] op=switch userId=${userId.slice(0, 8)} from=${oldCacheKey.slice(0, 8)} to=${target.accountId.slice(0, 8)}`);
|
|
12469
|
+
console.log(`[sub-account] op=rebind cacheKey=${payload.session_key.slice(0, 8)} account=${target.accountId.slice(0, 8)} siblingsTouched=0`);
|
|
12404
12470
|
return c.json(payload);
|
|
12405
12471
|
});
|
|
12406
12472
|
var session_default = app11;
|
|
@@ -22627,7 +22693,8 @@ var WaGateway = class {
|
|
|
22627
22693
|
serverPath: this.deps.serverPath,
|
|
22628
22694
|
passive: true,
|
|
22629
22695
|
text: input.text,
|
|
22630
|
-
msgId
|
|
22696
|
+
msgId,
|
|
22697
|
+
ownerSelfPhone: input.ownerSelfPhone
|
|
22631
22698
|
});
|
|
22632
22699
|
return;
|
|
22633
22700
|
}
|
|
@@ -22682,7 +22749,10 @@ var WaGateway = class {
|
|
|
22682
22749
|
gatewayUrl: this.deps.gatewayUrl,
|
|
22683
22750
|
serverPath: this.deps.serverPath,
|
|
22684
22751
|
// Task 1526 — a scheduled firing's per-firing seat id; absent on real inbound.
|
|
22685
|
-
sessionIdOverride: input.scheduledSessionId
|
|
22752
|
+
sessionIdOverride: input.scheduledSessionId,
|
|
22753
|
+
// Task 1575 — the gate's owner-self verdict; drives owner attribution
|
|
22754
|
+
// for an unbound owner self-phone at the spawn site.
|
|
22755
|
+
ownerSelfPhone: input.ownerSelfPhone
|
|
22686
22756
|
});
|
|
22687
22757
|
} finally {
|
|
22688
22758
|
this.spawning.delete(input.senderId);
|
|
@@ -24846,7 +24916,7 @@ var waGateway = new WaGateway({
|
|
|
24846
24916
|
});
|
|
24847
24917
|
return result.ok ? { ok: true, messageId: result.messageId } : { ok: false, error: result.error };
|
|
24848
24918
|
},
|
|
24849
|
-
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, sessionIdOverride }) => {
|
|
24919
|
+
ensureChannelSession: async ({ accountId, effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, passive, text, msgId, sessionIdOverride, ownerSelfPhone }) => {
|
|
24850
24920
|
if (passive) {
|
|
24851
24921
|
const phoneHash = createHash7("sha256").update(senderId).digest("hex").slice(0, 12);
|
|
24852
24922
|
const prompt = (text ?? "").trim();
|
|
@@ -24895,9 +24965,10 @@ var waGateway = new WaGateway({
|
|
|
24895
24965
|
console.error(`[whatsapp-native] op=account-manager-route senderId=${senderId} managesAccount=${managesAccount} effectiveAccount=${effectiveAccountId} source=gate`);
|
|
24896
24966
|
}
|
|
24897
24967
|
const req = buildWaSpawnRequest({ accountId: effectiveAccountId, senderId, role, personId, gatewayUrl, serverPath, sessionIdOverride });
|
|
24898
|
-
const userId = role === "admin" ? resolveAdminUserId({ accountId: effectiveAccountId, senderPhone: senderId }) ?? void 0 : void 0;
|
|
24968
|
+
const userId = role === "admin" ? resolveAdminUserId({ accountId: effectiveAccountId, senderPhone: senderId }) ?? (ownerSelfPhone ? resolveOwnerUserId(effectiveAccountId) ?? void 0 : void 0) : void 0;
|
|
24899
24969
|
if (role === "admin") {
|
|
24900
|
-
|
|
24970
|
+
const attribution = userId ?? (ownerSelfPhone ? "owner-self-unresolved" : "unbound-empty");
|
|
24971
|
+
console.error(`[whatsapp-native] op=admin-identity senderId=${senderId} userId=${attribution}`);
|
|
24901
24972
|
}
|
|
24902
24973
|
const result = await managerRcSpawn({ ...req, userId });
|
|
24903
24974
|
if ("error" in result) {
|
|
@@ -26159,7 +26230,10 @@ init({
|
|
|
26159
26230
|
passive: decision.passive,
|
|
26160
26231
|
text: msg.text,
|
|
26161
26232
|
reply: msg.reply,
|
|
26162
|
-
media: msg.media
|
|
26233
|
+
media: msg.media,
|
|
26234
|
+
// Task 1575 — carry the gate's owner-self verdict to the spawn site so an
|
|
26235
|
+
// unbound owner self-phone attributes to the owner userId.
|
|
26236
|
+
ownerSelfPhone: msg.ownerSelfPhone
|
|
26163
26237
|
});
|
|
26164
26238
|
} catch (err) {
|
|
26165
26239
|
console.error(
|