@rubytech/create-realagent-code 0.1.248 → 0.1.250

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 (25) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  3. package/payload/platform/plugins/admin/hooks/__tests__/session-end-retrospective.test.sh +3 -3
  4. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +20 -10
  5. package/payload/platform/plugins/docs/PLUGIN.md +2 -2
  6. package/payload/platform/plugins/docs/references/admin-session.md +7 -67
  7. package/payload/platform/plugins/docs/references/admin-ui.md +7 -3
  8. package/payload/platform/plugins/docs/references/deployment.md +1 -1
  9. package/payload/platform/plugins/docs/references/internals.md +8 -2
  10. package/payload/platform/plugins/docs/references/platform.md +3 -3
  11. package/payload/platform/scripts/check-no-legacy-spawn-route.mjs +37 -0
  12. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  13. package/payload/platform/services/claude-session-manager/dist/http-server.js +87 -21
  14. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  15. package/payload/platform/services/claude-session-manager/dist/index.js +1 -0
  16. package/payload/platform/services/claude-session-manager/dist/index.js.map +1 -1
  17. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +8 -0
  18. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  19. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +14 -4
  20. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  21. package/payload/server/{chunk-UEIA5YUG.js → chunk-BE7O7KZ5.js} +4 -0
  22. package/payload/server/maxy-edge.js +1 -1
  23. package/payload/server/server.js +160 -126
  24. package/payload/platform/plugins/admin/hooks/__tests__/turn-completed-graph-write.test.sh +0 -601
  25. package/payload/platform/plugins/admin/hooks/turn-completed-graph-write.sh +0 -441
@@ -52,6 +52,7 @@ import {
52
52
  getUserNameForSession,
53
53
  getUserTimezone,
54
54
  httpLog,
55
+ isActiveAgentSlug,
55
56
  isPasswordValid,
56
57
  isRemoteAuthConfigured,
57
58
  linkConversationTranscript,
@@ -92,7 +93,7 @@ import {
92
93
  vncLog,
93
94
  walkPremiumBundles,
94
95
  writeAdminUserAndPerson
95
- } from "./chunk-UEIA5YUG.js";
96
+ } from "./chunk-BE7O7KZ5.js";
96
97
  import {
97
98
  __commonJS,
98
99
  __toESM
@@ -4201,7 +4202,7 @@ async function managerSpawn(opts) {
4201
4202
  );
4202
4203
  return { error: "claude-auth-dead", status: 503 };
4203
4204
  }
4204
- const res = await fetch(`${managerBase()}/spawn`, {
4205
+ const res = await fetch(`${managerBase()}/public-spawn`, {
4205
4206
  method: "POST",
4206
4207
  headers: { "content-type": "application/json" },
4207
4208
  body: JSON.stringify({
@@ -4216,7 +4217,8 @@ async function managerSpawn(opts) {
4216
4217
  activePlugins: opts.activePlugins,
4217
4218
  specialistDomains: opts.specialistDomains,
4218
4219
  sliceToken: opts.sliceToken,
4219
- personId: opts.personId
4220
+ personId: opts.personId,
4221
+ name: opts.name
4220
4222
  })
4221
4223
  }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
4222
4224
  if ("__throw" in res) {
@@ -4226,6 +4228,32 @@ async function managerSpawn(opts) {
4226
4228
  if (!r.ok) return { error: `manager-status-${r.status}`, status: r.status };
4227
4229
  return await r.json();
4228
4230
  }
4231
+ async function managerRcSpawn(opts) {
4232
+ const auth = await ensureAuth();
4233
+ if (auth.status === "dead" || auth.status === "missing") {
4234
+ console.log(`[channel-pty-bridge] rc-spawn-refused reason=auth-${auth.status} ${opts.logContext}`);
4235
+ return { error: "claude-auth-dead", status: 503 };
4236
+ }
4237
+ const res = await fetch(`${managerBase()}/rc-spawn`, {
4238
+ method: "POST",
4239
+ headers: { "content-type": "application/json" },
4240
+ body: JSON.stringify({
4241
+ sessionId: opts.sessionId,
4242
+ name: opts.name,
4243
+ initialMessage: opts.initialMessage,
4244
+ closeAfterTurn: opts.closeAfterTurn,
4245
+ sliceToken: opts.sliceToken,
4246
+ personId: opts.personId
4247
+ })
4248
+ }).catch((err) => ({ __throw: err instanceof Error ? err.message : String(err) }));
4249
+ if ("__throw" in res) {
4250
+ return { error: res.__throw, status: 0 };
4251
+ }
4252
+ const r = res;
4253
+ if (!r.ok) return { error: `manager-status-${r.status}`, status: r.status };
4254
+ const out = await r.json();
4255
+ return { sessionId: out.sessionId ?? opts.sessionId, pid: out.spawnedPid ?? null };
4256
+ }
4229
4257
  async function managerWriteInput(sessionId, text) {
4230
4258
  const res = await fetch(`${managerBase()}/${sessionId}/input`, {
4231
4259
  method: "POST",
@@ -4247,7 +4275,19 @@ function managerLogFollowUrl(sessionId) {
4247
4275
  return `${managerBase()}/${sessionId}/log?follow=1`;
4248
4276
  }
4249
4277
 
4278
+ // app/lib/channel-pty-bridge/admin-session-id.ts
4279
+ import { createHash } from "crypto";
4280
+ function adminSessionIdFor(accountId, senderId) {
4281
+ const b = createHash("sha256").update(`${accountId}:admin:${senderId}`).digest().subarray(0, 16);
4282
+ const v = Buffer.from(b);
4283
+ v[6] = v[6] & 15 | 64;
4284
+ v[8] = v[8] & 63 | 128;
4285
+ const h = v.toString("hex");
4286
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20, 32)}`;
4287
+ }
4288
+
4250
4289
  // app/lib/channel-pty-bridge/public-session-end-review.ts
4290
+ import { randomUUID as randomUUID4 } from "crypto";
4251
4291
  var TAG14 = "[public-session-review]";
4252
4292
  var CONSUMED_CAP = 500;
4253
4293
  var consumed = /* @__PURE__ */ new Set();
@@ -4335,30 +4375,20 @@ function composeInitialMessage(input) {
4335
4375
  ].join("\n");
4336
4376
  }
4337
4377
  async function dispatchReviewer(input, initialMessage) {
4338
- try {
4339
- const res = await fetch(`${managerBase2()}/spawn`, {
4340
- method: "POST",
4341
- headers: { "content-type": "application/json" },
4342
- body: JSON.stringify({
4343
- senderId: `public-session-reviewer-${input.sessionId}`,
4344
- role: "admin",
4345
- channel: "webchat",
4346
- accountId: input.accountId,
4347
- specialist: "public-session-reviewer",
4348
- hidden: true,
4349
- initialMessage,
4350
- sliceToken: input.sliceToken,
4351
- personId: input.personId ?? void 0
4352
- })
4353
- });
4354
- if (!res.ok) {
4355
- return { ok: false, reason: `spawn-status-${res.status}` };
4356
- }
4357
- const body = await res.json();
4358
- return { ok: true, managerSessionId: body.sessionId ?? "unknown" };
4359
- } catch (err) {
4360
- return { ok: false, reason: `spawn-threw-${err instanceof Error ? err.message : String(err)}` };
4378
+ const sessionId = randomUUID4();
4379
+ console.log(`${TAG14} route target=rc-spawn sessionId=${sessionId.slice(0, 8)} sourceSession=${input.sessionId.slice(0, 8)}`);
4380
+ const spawned = await managerRcSpawn({
4381
+ sessionId,
4382
+ initialMessage,
4383
+ closeAfterTurn: true,
4384
+ sliceToken: input.sliceToken,
4385
+ personId: input.personId ?? void 0,
4386
+ logContext: `public-session-end sourceSession=${input.sessionId.slice(0, 8)}`
4387
+ });
4388
+ if ("error" in spawned) {
4389
+ return { ok: false, reason: `rc-spawn-${spawned.status}-${spawned.error}` };
4361
4390
  }
4391
+ return { ok: true, managerSessionId: spawned.sessionId };
4362
4392
  }
4363
4393
  async function firePublicSessionEndReview(input) {
4364
4394
  const sliceShort = input.sliceToken.slice(0, 8);
@@ -4527,7 +4557,7 @@ function tagFor(channel) {
4527
4557
  return `[${channel}-adaptor]`;
4528
4558
  }
4529
4559
  function publicIdleMs() {
4530
- return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(30 * 6e4));
4560
+ return Number(process.env.CHANNEL_PTY_IDLE_MS ?? String(5 * 6e4));
4531
4561
  }
4532
4562
  var index = /* @__PURE__ */ new Map();
4533
4563
  function indexKey(accountId, agentSlug, senderId, sliceToken = "") {
@@ -4538,6 +4568,7 @@ async function ensureEntry(input) {
4538
4568
  startReaper();
4539
4569
  const sliceTokenValue = input.sliceToken ?? "";
4540
4570
  const personIdValue = input.personId ?? null;
4571
+ const nameValue = input.name ?? null;
4541
4572
  const key = indexKey(input.accountId, input.agentSlug, input.senderId, sliceTokenValue);
4542
4573
  const existing = index.get(key);
4543
4574
  const tag = tagFor(input.channel);
@@ -4554,17 +4585,30 @@ async function ensureEntry(input) {
4554
4585
  }
4555
4586
  return existing;
4556
4587
  }
4557
- const attachmentDir = input.role === "public" ? resolve4(ATTACHMENTS_ROOT, "public", input.senderId) : void 0;
4558
- const spawned = await managerSpawn({
4559
- senderId: input.senderId,
4560
- role: input.role,
4561
- channel: input.channel,
4562
- accountId: input.accountId,
4563
- agentSlug: input.agentSlug,
4564
- attachmentDir,
4565
- sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
4566
- personId: personIdValue ?? void 0
4567
- });
4588
+ let spawned;
4589
+ if (input.role === "admin") {
4590
+ const adminSessionId = adminSessionIdFor(input.accountId, input.senderId);
4591
+ console.error(`${tag} route role=admin target=rc-spawn senderId=${input.senderId}`);
4592
+ spawned = await managerRcSpawn({
4593
+ sessionId: adminSessionId,
4594
+ name: nameValue ?? void 0,
4595
+ logContext: `channel=${input.channel} senderId=${input.senderId}`
4596
+ });
4597
+ } else {
4598
+ console.error(`${tag} route role=${input.role} target=public-spawn senderId=${input.senderId}`);
4599
+ const attachmentDir = resolve4(ATTACHMENTS_ROOT, "public", input.senderId);
4600
+ spawned = await managerSpawn({
4601
+ senderId: input.senderId,
4602
+ role: input.role,
4603
+ channel: input.channel,
4604
+ accountId: input.accountId,
4605
+ agentSlug: input.agentSlug,
4606
+ attachmentDir,
4607
+ sliceToken: sliceTokenValue.length > 0 ? sliceTokenValue : void 0,
4608
+ personId: personIdValue ?? void 0,
4609
+ name: nameValue ?? void 0
4610
+ });
4611
+ }
4568
4612
  if ("error" in spawned) {
4569
4613
  if (spawned.error !== "claude-auth-dead") {
4570
4614
  console.error(
@@ -4587,7 +4631,8 @@ async function ensureEntry(input) {
4587
4631
  followerAbort: null,
4588
4632
  followerRunning: false,
4589
4633
  sliceToken: sliceTokenValue,
4590
- personId: personIdValue
4634
+ personId: personIdValue,
4635
+ name: nameValue
4591
4636
  };
4592
4637
  entry.followerAbort = startFollower({
4593
4638
  entry,
@@ -4632,7 +4677,8 @@ async function writeInput(entry, text) {
4632
4677
  channel: entry.channel,
4633
4678
  agentSlug: entry.agentSlug,
4634
4679
  sliceToken: entry.sliceToken.length > 0 ? entry.sliceToken : void 0,
4635
- personId: entry.personId
4680
+ personId: entry.personId,
4681
+ name: entry.name
4636
4682
  });
4637
4683
  if (!respawned) return false;
4638
4684
  for (const cb of entry.subscribers) respawned.subscribers.add(cb);
@@ -4671,7 +4717,8 @@ async function dispatchOnce(input) {
4671
4717
  channel: input.channel,
4672
4718
  agentSlug: input.agentSlug,
4673
4719
  sliceToken: input.sliceToken,
4674
- personId: input.personId
4720
+ personId: input.personId,
4721
+ name: input.name
4675
4722
  });
4676
4723
  if (!entry) return { error: "spawn-failed" };
4677
4724
  entry.lastInboundAt = Date.now();
@@ -4774,10 +4821,10 @@ function handlePublicSessionExit(sessionId) {
4774
4821
  }
4775
4822
 
4776
4823
  // app/lib/access-session-store.ts
4777
- import { randomUUID as randomUUID4 } from "crypto";
4824
+ import { randomUUID as randomUUID5 } from "crypto";
4778
4825
  var sessions = /* @__PURE__ */ new Map();
4779
4826
  function createAccessSession(input) {
4780
- const sessionId = randomUUID4();
4827
+ const sessionId = randomUUID5();
4781
4828
  const session = {
4782
4829
  ...input,
4783
4830
  sessionId,
@@ -4800,6 +4847,16 @@ function evictAccessSessionsByGrant(grantId) {
4800
4847
  return dropped;
4801
4848
  }
4802
4849
 
4850
+ // app/lib/public-session-title.ts
4851
+ function composePublicSessionTitle(input) {
4852
+ const sid = input.senderId.slice(0, 8);
4853
+ const ts = input.spawnedAt.toISOString().slice(0, 16).replace("T", " ");
4854
+ const segments = ["Web", sid];
4855
+ if (input.personId) segments.push(input.personId);
4856
+ segments.push(ts);
4857
+ return segments.join(" \xB7 ");
4858
+ }
4859
+
4803
4860
  // server/routes/chat.ts
4804
4861
  var WEBCHAT_TAG = "[webchat-adaptor]";
4805
4862
  var SESSION_KEY_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,127}$/;
@@ -4921,7 +4978,7 @@ app2.post("/", async (c) => {
4921
4978
  let agentSlug;
4922
4979
  let resolveSource;
4923
4980
  if (requestedSlug) {
4924
- const active = validateAgentSlug(requestedSlug) && resolveAgentConfig(account.accountDir, requestedSlug).status === "active";
4981
+ const active = validateAgentSlug(requestedSlug) && isActiveAgentSlug(account.accountDir, requestedSlug);
4925
4982
  agentSlug = active ? requestedSlug : null;
4926
4983
  resolveSource = active ? "slug" : "none";
4927
4984
  } else {
@@ -4969,6 +5026,11 @@ app2.post("/", async (c) => {
4969
5026
  const readable = new ReadableStream({
4970
5027
  async start(controller) {
4971
5028
  try {
5029
+ const publicTitle = composePublicSessionTitle({
5030
+ senderId: session_key,
5031
+ personId,
5032
+ spawnedAt: /* @__PURE__ */ new Date()
5033
+ });
4972
5034
  const result = await dispatchOnce({
4973
5035
  accountId: account.accountId,
4974
5036
  senderId: session_key,
@@ -4977,6 +5039,7 @@ app2.post("/", async (c) => {
4977
5039
  agentSlug,
4978
5040
  sliceToken,
4979
5041
  personId,
5042
+ name: publicTitle,
4980
5043
  text: fullMessage,
4981
5044
  timeoutMs: webchatTurnTimeoutMs()
4982
5045
  });
@@ -5028,7 +5091,7 @@ import { readFile, stat as stat2 } from "fs/promises";
5028
5091
  import { realpathSync as realpathSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, existsSync as existsSync4 } from "fs";
5029
5092
 
5030
5093
  // app/lib/whatsapp/login.ts
5031
- import { randomUUID as randomUUID5 } from "crypto";
5094
+ import { randomUUID as randomUUID6 } from "crypto";
5032
5095
  var TAG15 = "[whatsapp:login]";
5033
5096
  var ACTIVE_LOGIN_TTL_MS = 3 * 6e4;
5034
5097
  var activeLogins = /* @__PURE__ */ new Map();
@@ -5167,7 +5230,7 @@ async function startLogin(opts) {
5167
5230
  const login = {
5168
5231
  accountId,
5169
5232
  authDir,
5170
- id: randomUUID5(),
5233
+ id: randomUUID6(),
5171
5234
  sock,
5172
5235
  startedAt: Date.now(),
5173
5236
  connected: false
@@ -6169,7 +6232,7 @@ var whatsapp_default = app3;
6169
6232
  // server/routes/onboarding.ts
6170
6233
  import { spawn, spawnSync as spawnSync2, execFileSync as execFileSync2 } from "child_process";
6171
6234
  import { openSync, closeSync, writeFileSync as writeFileSync5, writeSync, existsSync as existsSync6, readFileSync as readFileSync8, unlinkSync } from "fs";
6172
- import { createHash, randomUUID as randomUUID6 } from "crypto";
6235
+ import { createHash as createHash2, randomUUID as randomUUID7 } from "crypto";
6173
6236
 
6174
6237
  // ../lib/admins-write/src/index.ts
6175
6238
  import { existsSync as existsSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync4, renameSync, mkdirSync as mkdirSync2, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
@@ -6317,7 +6380,7 @@ function checkAdminAuthInvariant(input) {
6317
6380
 
6318
6381
  // server/routes/onboarding.ts
6319
6382
  function hashPin(pin) {
6320
- return createHash("sha256").update(pin).digest("hex");
6383
+ return createHash2("sha256").update(pin).digest("hex");
6321
6384
  }
6322
6385
  function readUsersFile() {
6323
6386
  if (!existsSync6(USERS_FILE)) return null;
@@ -6445,7 +6508,7 @@ app4.post("/set-pin", async (c) => {
6445
6508
  const hash = hashPin(body.pin);
6446
6509
  const account = resolveAccount();
6447
6510
  const existingOwnerUserId = account?.config.admins?.find((a) => a.role === "owner")?.userId;
6448
- const userId = existingOwnerUserId ?? randomUUID6();
6511
+ const userId = existingOwnerUserId ?? randomUUID7();
6449
6512
  if (existingOwnerUserId) {
6450
6513
  console.log(`[set-pin] reusing existing owner userId=${userId.slice(0, 8)}\u2026 (change-PIN preserves identity)`);
6451
6514
  } else {
@@ -6716,14 +6779,14 @@ app5.post("/", async (c) => {
6716
6779
  var client_error_default = app5;
6717
6780
 
6718
6781
  // server/routes/admin/session.ts
6719
- import { randomUUID as randomUUID7 } from "crypto";
6782
+ import { randomUUID as randomUUID8 } from "crypto";
6720
6783
 
6721
6784
  // app/lib/admin-identity/pin-validator.ts
6722
- import { createHash as createHash2 } from "crypto";
6785
+ import { createHash as createHash3 } from "crypto";
6723
6786
  import { existsSync as existsSync8, readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
6724
6787
  import { join as join8 } from "path";
6725
6788
  function hashPin2(pin) {
6726
- return createHash2("sha256").update(pin).digest("hex");
6789
+ return createHash3("sha256").update(pin).digest("hex");
6727
6790
  }
6728
6791
  function readUsersFile2(usersFilePath) {
6729
6792
  if (!existsSync8(usersFilePath)) return null;
@@ -6790,7 +6853,7 @@ async function resolveUserIdentity(accountId, userId) {
6790
6853
  async function createAdminSession(accountId, thinkingView, userId, userName, role, avatar) {
6791
6854
  const account = resolveAccount();
6792
6855
  const effectiveThinkingView = thinkingView ?? account?.config.thinkingView ?? "default";
6793
- const signedSessionToken = randomUUID7();
6856
+ const signedSessionToken = randomUUID8();
6794
6857
  const cacheKey = fingerprintSessionKey(signedSessionToken);
6795
6858
  registerSession(cacheKey, "admin", accountId, void 0, userId, userName, role);
6796
6859
  if (userId) setWantsPriorConversation(cacheKey);
@@ -8283,7 +8346,6 @@ function managerBase3() {
8283
8346
  return `http://127.0.0.1:${port2}`;
8284
8347
  }
8285
8348
  var app12 = new Hono();
8286
- var UUID_PATTERN = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
8287
8349
  async function performSpawnWithInitialMessage(args) {
8288
8350
  const ownerStart = Date.now();
8289
8351
  const aboutOwner = await resolveOwnerProfileBlock(args.senderId, args.userId);
@@ -8323,7 +8385,7 @@ async function performSpawnWithInitialMessage(args) {
8323
8385
  });
8324
8386
  console.log(`${TAG18} forward-spawn-start managerBase=${managerBase3()} bytes=${upstreamPayload.length} hidden=${args.hidden} specialist=${args.specialist ?? "none"}`);
8325
8387
  const forwardStart = Date.now();
8326
- const upstream = await fetch(`${managerBase3()}/spawn`, {
8388
+ const upstream = await fetch(`${managerBase3()}/public-spawn`, {
8327
8389
  method: "POST",
8328
8390
  headers: { "content-type": "application/json" },
8329
8391
  body: upstreamPayload
@@ -8360,16 +8422,7 @@ function mintTranscriptEdge(sessionId, claudeSessionId, accountId) {
8360
8422
  if (!sessionId || !claudeSessionId) return;
8361
8423
  void linkConversationTranscript(sessionId, claudeSessionId, accountId);
8362
8424
  }
8363
- var requireAdminSessionUnlessLoopbackSpawn = async (c, next) => {
8364
- const remoteAddr = c.env?.incoming?.socket?.remoteAddress ?? "";
8365
- const isLoopback = remoteAddr === "127.0.0.1" || remoteAddr === "::1" || remoteAddr === "::ffff:127.0.0.1";
8366
- const hasForwardedFor = !!c.req.header("x-forwarded-for");
8367
- if (isLoopback && !hasForwardedFor && c.req.method === "POST" && c.req.path === "/api/admin/claude-sessions") {
8368
- return next();
8369
- }
8370
- return requireAdminSession(c, next);
8371
- };
8372
- app12.use("*", requireAdminSessionUnlessLoopbackSpawn);
8425
+ app12.use("*", requireAdminSession);
8373
8426
  app12.post("/", async (c) => {
8374
8427
  const routeStart = Date.now();
8375
8428
  const cacheKey = c.get("cacheKey") ?? "";
@@ -8380,46 +8433,18 @@ app12.post("/", async (c) => {
8380
8433
  }
8381
8434
  const refusal = await refuseIfClaudeAuthDead(c, "spawn", null);
8382
8435
  if (refusal) return refusal;
8383
- let senderId;
8384
- let userId;
8385
- let authSurface;
8386
- if (cacheKey) {
8387
- authSurface = "cookie";
8388
- senderId = getAccountIdForSession(cacheKey) ?? "";
8389
- if (!senderId) {
8390
- console.error(`${TAG18} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
8391
- return c.json({ error: "admin-account-not-resolved" }, 500);
8392
- }
8393
- userId = getUserIdForSession(cacheKey) ?? void 0;
8394
- } else {
8395
- authSurface = "loopback";
8396
- if (typeof body.adminSessionId !== "string" || !UUID_PATTERN.test(body.adminSessionId)) {
8397
- return c.json({ error: "admin-session-id-required" }, 400);
8398
- }
8399
- const lookupSessionId = body.adminSessionId;
8400
- const operatorMeta = await fetch(
8401
- `${managerBase3()}/${encodeURIComponent(lookupSessionId)}/meta`
8402
- ).then((r) => r.ok ? r.json() : null).catch((err) => {
8403
- console.error(`${TAG18} fetch-failed op=operator-meta-lookup message=${err instanceof Error ? err.message : String(err)}`);
8404
- return null;
8405
- });
8406
- const metaSenderId = operatorMeta && typeof operatorMeta.senderId === "string" && operatorMeta.senderId.length > 0 ? operatorMeta.senderId : null;
8407
- const bodyAccountId = typeof body.accountId === "string" && UUID_PATTERN.test(body.accountId) ? body.accountId : null;
8408
- const operatorSenderId = metaSenderId ?? bodyAccountId;
8409
- if (!operatorSenderId) {
8410
- console.error(`${TAG18} reject reason=no-sender-id adminSessionId=${lookupSessionId.slice(0, 8)} metaSenderId=${metaSenderId ? "present" : "absent"} bodyAccountId=${bodyAccountId ? "present" : "absent"}`);
8411
- return c.json({ error: "admin-session-not-found" }, 404);
8412
- }
8413
- console.log(`${TAG18} loopback-sender-resolved source=${metaSenderId ? "meta" : "body-accountId"} accountId=${operatorSenderId.slice(0, 8)}`);
8414
- senderId = operatorSenderId;
8415
- userId = void 0;
8436
+ const senderId = getAccountIdForSession(cacheKey) ?? "";
8437
+ if (!senderId) {
8438
+ console.error(`${TAG18} reject reason=no-account-id cacheKey-prefix=${cacheKey.slice(0, 8)}`);
8439
+ return c.json({ error: "admin-account-not-resolved" }, 500);
8416
8440
  }
8441
+ const userId = getUserIdForSession(cacheKey) ?? void 0;
8417
8442
  const channel = typeof body.channel === "string" ? body.channel : "browser";
8418
8443
  const initialMessage = typeof body.initialMessage === "string" && body.initialMessage.trim() ? body.initialMessage : null;
8419
8444
  const permissionMode = typeof body.permissionMode === "string" ? body.permissionMode : void 0;
8420
8445
  const specialist = typeof body.specialist === "string" && /^[A-Za-z0-9_-]{1,64}$/.test(body.specialist) ? body.specialist : void 0;
8421
8446
  const model = typeof body.model === "string" && /^[A-Za-z0-9._-]{1,64}$/.test(body.model) ? body.model : void 0;
8422
- console.log(`${TAG18} spawn-request-in surface=${authSurface} accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
8447
+ console.log(`${TAG18} spawn-request-in surface=cookie accountId=${senderId.slice(0, 8)} userId=${userId ? userId.slice(0, 8) : "absent"} channel=${channel} permissionMode=${permissionMode ?? "default"} specialist=${specialist ?? "none"} model=${model ?? "default"} initialMessage=${initialMessage ? "yes" : "no"}`);
8423
8448
  const conversationNodeId = cacheKey ? getSessionIdForSession(cacheKey) : void 0;
8424
8449
  const { response, claudeSessionId } = await performSpawnWithInitialMessage({
8425
8450
  senderId,
@@ -8438,7 +8463,7 @@ app12.post("/", async (c) => {
8438
8463
  claudeSessionId,
8439
8464
  senderId
8440
8465
  );
8441
- console.log(`${TAG18} route-done surface=${authSurface} status=${response.status} route-ms=${Date.now() - routeStart}`);
8466
+ console.log(`${TAG18} route-done surface=cookie status=${response.status} route-ms=${Date.now() - routeStart}`);
8442
8467
  return response;
8443
8468
  });
8444
8469
  var claude_sessions_default = app12;
@@ -9770,7 +9795,7 @@ function applyBacklinkBoost(hits) {
9770
9795
  }
9771
9796
 
9772
9797
  // ../lib/graph-search/src/dedup.ts
9773
- import { createHash as createHash3 } from "crypto";
9798
+ import { createHash as createHash4 } from "crypto";
9774
9799
  var DEFAULT_LAYERS = [
9775
9800
  "nodeId",
9776
9801
  "slug",
@@ -9797,7 +9822,7 @@ function readContentHash(props) {
9797
9822
  const content = props["content"];
9798
9823
  const source = typeof compiled === "string" && compiled.length > 0 ? compiled : typeof content === "string" && content.length > 0 ? content : null;
9799
9824
  if (source === null) return null;
9800
- return createHash3("sha256").update(source).digest("hex").slice(0, 16);
9825
+ return createHash4("sha256").update(source).digest("hex").slice(0, 16);
9801
9826
  }
9802
9827
  function dedup(hits, layers = DEFAULT_LAYERS) {
9803
9828
  const sorted = [...hits].sort((a, b) => b.score - a.score);
@@ -12542,6 +12567,7 @@ app27.get("/", async (c) => {
12542
12567
  var health_default2 = app27;
12543
12568
 
12544
12569
  // server/routes/admin/linkedin-ingest.ts
12570
+ import { randomUUID as randomUUID9 } from "crypto";
12545
12571
  var TAG20 = "[linkedin-ingest-route]";
12546
12572
  var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
12547
12573
  var ISO = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
@@ -12584,11 +12610,14 @@ function buildInitialMessage(env) {
12584
12610
  "sourceUrl=" + env.pageUrl,
12585
12611
  "capturedAt=" + env.capturedAt
12586
12612
  ].join(" \xB7 ");
12613
+ const delegation = "Dispatch `database-operator` via the Task tool to carry out the ingestion described below \u2014 it owns every graph write. Do not call memory-write, memory-update, or any graph tool directly.";
12587
12614
  if (env.kind === "profile" && env.profile) {
12588
12615
  const p = env.profile;
12589
12616
  return [
12590
12617
  header,
12591
12618
  "",
12619
+ delegation,
12620
+ "",
12592
12621
  "Surface: LinkedIn profile. Run the document-ingest skill in document mode against the body below, anchored to a Person subject for this profile.",
12593
12622
  "",
12594
12623
  "Conservative-extraction rules apply (see Task 234 spec):",
@@ -12617,6 +12646,8 @@ function buildInitialMessage(env) {
12617
12646
  return [
12618
12647
  header,
12619
12648
  "",
12649
+ delegation,
12650
+ "",
12620
12651
  "Surface: LinkedIn DM thread. Run the document-ingest skill in document mode against the transcript below.",
12621
12652
  "",
12622
12653
  "Conservative-extraction rules apply (see Task 234 spec):",
@@ -12656,39 +12687,30 @@ app28.post("/", requireAdminSession, async (c) => {
12656
12687
  console.error(TAG20 + " rejected status=500 reason=admin-account-not-resolved");
12657
12688
  return c.json({ ok: false, error: "admin-account-not-resolved" }, 500);
12658
12689
  }
12659
- const userId = getUserIdForSession(cacheKey) ?? void 0;
12660
12690
  const payloadBytes = JSON.stringify(envelope).length;
12661
12691
  console.log(
12662
12692
  TAG20 + " received kind=" + envelope.kind + " account=" + senderId.slice(0, 8) + " pageUrl=" + envelope.pageUrl + " dispatchId=" + envelope.dispatchId + " payloadBytes=" + payloadBytes
12663
12693
  );
12664
12694
  const initialMessage = buildInitialMessage(envelope);
12665
12695
  const spawnStart = Date.now();
12666
- const { response, claudeSessionId } = await performSpawnWithInitialMessage({
12667
- senderId,
12668
- userId,
12669
- role: "admin",
12670
- channel: "linkedin-extension",
12671
- permissionMode: void 0,
12672
- hidden: true,
12673
- specialist: "database-operator",
12674
- model: void 0,
12696
+ const sessionId = randomUUID9();
12697
+ console.log(TAG20 + " route target=rc-spawn dispatchId=" + envelope.dispatchId + " sessionId=" + sessionId.slice(0, 8));
12698
+ const spawned = await managerRcSpawn({
12699
+ sessionId,
12675
12700
  initialMessage,
12676
- // Task 382 — LinkedIn ingest is a hidden autonomous spawn with no
12677
- // parent admin conversation; the database-operator threads
12678
- // `producedByTaskId` via work-create instead. Leave undefined.
12679
- conversationNodeId: void 0
12701
+ closeAfterTurn: true,
12702
+ logContext: "linkedin-ingest dispatchId=" + envelope.dispatchId
12680
12703
  });
12681
- if (!response.ok) {
12682
- const detail = await response.text().catch(() => "");
12704
+ if ("error" in spawned) {
12683
12705
  console.error(
12684
- TAG20 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + response.status + " ms=" + (Date.now() - spawnStart) + " detail=" + detail.slice(0, 200)
12706
+ TAG20 + " dispatch-failed dispatchId=" + envelope.dispatchId + " status=" + spawned.status + " ms=" + (Date.now() - spawnStart) + " message=" + spawned.error
12685
12707
  );
12686
- return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: response.status, detail: detail.slice(0, 500) }, 502);
12708
+ return c.json({ ok: false, error: "dispatch-failed", upstreamStatus: spawned.status, detail: spawned.error }, 502);
12687
12709
  }
12688
12710
  console.log(
12689
- TAG20 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + (claudeSessionId ?? "unknown") + " ms=" + (Date.now() - spawnStart)
12711
+ TAG20 + " dispatched dispatchId=" + envelope.dispatchId + " taskId=" + spawned.sessionId + " ms=" + (Date.now() - spawnStart)
12690
12712
  );
12691
- return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: claudeSessionId }, 202);
12713
+ return c.json({ ok: true, dispatchId: envelope.dispatchId, taskId: spawned.sessionId }, 202);
12692
12714
  });
12693
12715
  var linkedin_ingest_default = app28;
12694
12716
 
@@ -13056,7 +13078,7 @@ var admin_default = app34;
13056
13078
  import neo4j4 from "neo4j-driver";
13057
13079
  import { readFileSync as readFileSync17 } from "fs";
13058
13080
  import { resolve as resolve19 } from "path";
13059
- import { randomUUID as randomUUID8 } from "crypto";
13081
+ import { randomUUID as randomUUID10 } from "crypto";
13060
13082
  var PLATFORM_ROOT7 = process.env.MAXY_PLATFORM_ROOT ?? resolve19(process.cwd(), "..");
13061
13083
  var driver = null;
13062
13084
  function readPassword() {
@@ -13239,7 +13261,7 @@ async function consumeMagicTokenAndActivate(grantId) {
13239
13261
  }
13240
13262
  }
13241
13263
  async function generateNewMagicToken(grantId) {
13242
- const token = randomUUID8();
13264
+ const token = randomUUID10();
13243
13265
  const session = getSession3();
13244
13266
  try {
13245
13267
  const result = await session.run(
@@ -14280,6 +14302,10 @@ app42.post("/", async (c) => {
14280
14302
  if (!validateAgentSlug(body.agent)) {
14281
14303
  return c.json({ error: "Invalid agent name" }, 400);
14282
14304
  }
14305
+ if (!account || !isActiveAgentSlug(account.accountDir, body.agent)) {
14306
+ console.error(`[session] op=reject agent=${body.agent} reason=not-active`);
14307
+ return c.json({ error: "no-agent-for-address" }, 404);
14308
+ }
14283
14309
  agentSlug = body.agent;
14284
14310
  } else {
14285
14311
  agentSlug = account ? resolveDefaultAgentSlug(account.accountDir) : null;
@@ -14691,7 +14717,7 @@ function broadcastAdminShutdown(reason) {
14691
14717
  }
14692
14718
 
14693
14719
  // ../lib/entitlement/src/index.ts
14694
- import { createPublicKey, createHash as createHash4, verify as cryptoVerify } from "crypto";
14720
+ import { createPublicKey, createHash as createHash5, verify as cryptoVerify } from "crypto";
14695
14721
  import { existsSync as existsSync22, readFileSync as readFileSync21, statSync as statSync9 } from "fs";
14696
14722
  import { resolve as resolve24 } from "path";
14697
14723
 
@@ -14762,7 +14788,7 @@ function verifyAndResolve(brand, entitlementPath, account) {
14762
14788
  reason: "pubkey-missing"
14763
14789
  });
14764
14790
  }
14765
- const pubkeyHash = createHash4("sha256").update(pubkeyPem).digest("hex");
14791
+ const pubkeyHash = createHash5("sha256").update(pubkeyPem).digest("hex");
14766
14792
  if (pubkeyHash !== PUBKEY_SHA256) {
14767
14793
  return logResolved(anonymousFallback("pubkey-tamper"), {
14768
14794
  reason: "pubkey-tamper"
@@ -15491,6 +15517,9 @@ function brandedPublicHtml(agentSlug) {
15491
15517
  function escapeHtml(s) {
15492
15518
  return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15493
15519
  }
15520
+ function agentUnavailableHtml() {
15521
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>${escapeHtml(BRAND.productName)}</title></head><body style="font-family:system-ui,sans-serif;max-width:32rem;margin:4rem auto;padding:0 1.5rem;color:#222"><h1 style="font-size:1.25rem">Agent unavailable</h1><p>This agent isn't available right now. If you reached this page from a saved link, the agent may have been turned off.</p></body></html>`;
15522
+ }
15494
15523
  app43.get("/", (c) => {
15495
15524
  const host = (c.req.header("host") ?? "").split(":")[0];
15496
15525
  if (isPublicHost(host)) {
@@ -15587,6 +15616,11 @@ app43.get("/data", (c) => {
15587
15616
  app43.get("/:slug", async (c, next) => {
15588
15617
  const slug = c.req.param("slug");
15589
15618
  if (AGENT_SLUG_PATTERN.test(`/${slug}`)) {
15619
+ const account = resolveAccount();
15620
+ if (!account || !validateAgentSlug(slug) || !isActiveAgentSlug(account.accountDir, slug)) {
15621
+ console.error(`[public-catchall] op=reject path=/${slug} slug=${slug} reason=not-reachable`);
15622
+ return c.html(agentUnavailableHtml(), 404);
15623
+ }
15590
15624
  const branding = loadBrandingCache(slug);
15591
15625
  console.error(`[public-catchall] served path=/${slug} slug=${slug} branding=${branding ? "hit" : "miss"}`);
15592
15626
  return c.html(brandedPublicHtml(slug));