@personaai/runtime 0.5.1 → 0.5.3

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/dist/index.cjs CHANGED
@@ -69,12 +69,14 @@ function stripMountPath(path, mountPath) {
69
69
 
70
70
  // src/client-factory.ts
71
71
  var import_sdk = require("@personaai/sdk");
72
- function createClientForRequest(options, userId) {
72
+ function createClientForRequest(options, userId, logger) {
73
+ const clientLogger = logger ? logger.child("sdk") : void 0;
73
74
  return new import_sdk.PersonaClient({
74
75
  baseUrl: options.baseUrl,
75
76
  credential: options.credential,
76
77
  externalUserId: userId ?? void 0,
77
- fetch: options.fetch
78
+ fetch: options.fetch,
79
+ logger: clientLogger
78
80
  });
79
81
  }
80
82
 
@@ -133,6 +135,9 @@ function errorToResponse(err, mode) {
133
135
  };
134
136
  }
135
137
 
138
+ // src/runtime.ts
139
+ var import_sdk4 = require("@personaai/sdk");
140
+
136
141
  // src/runRegistry.ts
137
142
  var DEFAULT_RUN_GRACE_MS = 5 * 60 * 1e3;
138
143
  var DEFAULT_MAX_TRACKED_RUNS = 1e3;
@@ -154,7 +159,7 @@ function evictStaleRuns(runs, now, graceMs = DEFAULT_RUN_GRACE_MS, maxTracked =
154
159
  }
155
160
 
156
161
  // src/version.ts
157
- var RUNTIME_VERSION = "0.5.1";
162
+ var RUNTIME_VERSION = "0.5.3";
158
163
 
159
164
  // src/routes/health.ts
160
165
  var alwaysOnCapabilities = {
@@ -476,7 +481,16 @@ function parseChatBody(body) {
476
481
  };
477
482
  }
478
483
  var chatRoute = async (request, ctx) => {
484
+ const logger = ctx.logger.child("chat");
485
+ logger.debug("chatRoute start", { userId: request.userId, hasBody: !!request.body });
479
486
  const body = parseChatBody(request.body);
487
+ logger.trace("chatRoute body", {
488
+ agentId: body.agentId,
489
+ threadId: body.threadId,
490
+ messageCount: body.messages?.length ?? 0,
491
+ hasResume: !!body.resume,
492
+ hasContextOverride: !!body.contextOverride
493
+ });
480
494
  const userId = request.userId;
481
495
  const runCtx = {
482
496
  userId,
@@ -485,7 +499,10 @@ var chatRoute = async (request, ctx) => {
485
499
  threadId: body.threadId,
486
500
  messages: body.messages
487
501
  };
502
+ logger.debug("beforeRun hook", { agentId: body.agentId, threadId: body.threadId });
488
503
  await ctx.hooks?.beforeRun?.(runCtx);
504
+ logger.trace("beforeRun completed", { agentId: body.agentId });
505
+ logger.debug("starting chat stream", { agentId: body.agentId, threadId: body.threadId });
489
506
  const stream = ctx.client.chat.stream(body.agentId, {
490
507
  messages: body.messages,
491
508
  threadId: body.threadId,
@@ -493,9 +510,28 @@ var chatRoute = async (request, ctx) => {
493
510
  contextOverride: body.contextOverride
494
511
  });
495
512
  const runId = crypto.randomUUID();
513
+ logger.info("chat run created", {
514
+ runId,
515
+ agentId: body.agentId,
516
+ threadId: body.threadId,
517
+ userId
518
+ });
496
519
  const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
497
520
  ctx.runs.set(runId, driver);
498
- await driver.waitForFirstFrame();
521
+ logger.debug("run driver registered", { runId, trackedRuns: ctx.runs.size });
522
+ try {
523
+ await driver.waitForFirstFrame();
524
+ logger.debug("chat run first frame ready", { runId });
525
+ } catch (err) {
526
+ logger.warn("chat run failed before first frame", {
527
+ runId,
528
+ agentId: body.agentId,
529
+ error: err instanceof Error ? err.message : String(err)
530
+ });
531
+ ctx.runs.delete(runId);
532
+ throw err;
533
+ }
534
+ logger.info("chat stream ready", { runId, agentId: body.agentId });
499
535
  return {
500
536
  kind: "stream",
501
537
  status: 200,
@@ -528,22 +564,44 @@ function parseArchitectBody(body) {
528
564
  };
529
565
  }
530
566
  var architectRoute = async (request, ctx) => {
567
+ const logger = ctx.logger.child("architect");
568
+ logger.debug("architectRoute start", { userId: request.userId });
531
569
  const body = parseArchitectBody(request.body);
570
+ logger.trace("architectRoute body", {
571
+ messageCount: body.messages?.length ?? 0,
572
+ hasResume: !!body.resume
573
+ });
532
574
  const userId = request.userId;
533
575
  const runCtx = {
534
576
  userId,
535
577
  kind: "architect",
536
578
  messages: body.messages
537
579
  };
580
+ logger.debug("beforeRun hook (architect)", { userId });
538
581
  await ctx.hooks?.beforeRun?.(runCtx);
582
+ logger.trace("beforeRun completed (architect)");
583
+ logger.debug("starting architect stream");
539
584
  const stream = ctx.client.architect.stream({
540
585
  messages: body.messages,
541
586
  resume: body.resume
542
587
  });
543
588
  const runId = crypto.randomUUID();
589
+ logger.info("architect run created", { runId, userId });
544
590
  const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
545
591
  ctx.runs.set(runId, driver);
546
- await driver.waitForFirstFrame();
592
+ logger.debug("architect driver registered", { runId, trackedRuns: ctx.runs.size });
593
+ try {
594
+ await driver.waitForFirstFrame();
595
+ logger.debug("architect first frame ready", { runId });
596
+ } catch (err) {
597
+ logger.warn("architect run failed before first frame", {
598
+ runId,
599
+ error: err instanceof Error ? err.message : String(err)
600
+ });
601
+ ctx.runs.delete(runId);
602
+ throw err;
603
+ }
604
+ logger.info("architect stream ready", { runId });
547
605
  return {
548
606
  kind: "stream",
549
607
  status: 200,
@@ -692,6 +750,13 @@ var listAgents = async (request, ctx) => {
692
750
  });
693
751
  return json(200, items);
694
752
  };
753
+ var getAgentMcpConnections = async (request, ctx) => {
754
+ const connections = await ctx.client.agents.getMcpConnections(
755
+ requireParam(ctx.params, "id"),
756
+ request.query.returnTo
757
+ );
758
+ return json(200, connections);
759
+ };
695
760
  var createAgent = async (request, ctx) => {
696
761
  const body = requireBodyObject(request.body);
697
762
  const input = {
@@ -1252,6 +1317,11 @@ function buildRoutes(capabilities) {
1252
1317
  method: "DELETE",
1253
1318
  pattern: ["mcps", ":id", "oauth", "owner", "connection"],
1254
1319
  handler: disconnectOwnerConnection
1320
+ },
1321
+ {
1322
+ method: "GET",
1323
+ pattern: ["agents", ":id", "mcp-connections"],
1324
+ handler: getAgentMcpConnections
1255
1325
  }
1256
1326
  ];
1257
1327
  if (capabilities.agentsWrite) {
@@ -1364,23 +1434,86 @@ function createRuntime(options) {
1364
1434
  if (!options.baseUrl) throw new Error('createRuntime: "baseUrl" is required');
1365
1435
  if (!options.credential) throw new Error('createRuntime: "credential" is required');
1366
1436
  if (!options.resolveUser) throw new Error('createRuntime: "resolveUser" is required');
1437
+ const logger = options.logger ?? (0, import_sdk4.createLogger)(
1438
+ "runtime",
1439
+ options.logLevel !== void 0 ? { level: options.logLevel } : void 0
1440
+ );
1367
1441
  const capabilities = resolveCapabilities(options.capabilities);
1368
1442
  const routes = buildRoutes(capabilities);
1369
1443
  const mode = resolveMode(options);
1370
1444
  const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15e3;
1371
1445
  const runGraceMs = options.runGraceMs ?? DEFAULT_RUN_GRACE_MS;
1372
1446
  const maxTrackedRuns = options.maxTrackedRuns ?? DEFAULT_MAX_TRACKED_RUNS;
1447
+ logger.debug("runtime init", {
1448
+ mode,
1449
+ mountPath: options.mountPath ?? "",
1450
+ capabilities,
1451
+ heartbeatIntervalMs,
1452
+ runGraceMs,
1453
+ maxTrackedRuns
1454
+ });
1455
+ const enabledCaps = Object.entries(capabilities).filter(([, v]) => v).map(([k]) => k);
1456
+ logger.info("runtime created", {
1457
+ mode,
1458
+ mountPath: options.mountPath ?? "",
1459
+ enabledCapabilities: enabledCaps.length ? enabledCaps : ["(core only)"]
1460
+ });
1461
+ logger.trace("runtime config", {
1462
+ baseUrl: options.baseUrl,
1463
+ hasHooks: !!options.hooks,
1464
+ routeCount: routes.length
1465
+ });
1373
1466
  const runs = /* @__PURE__ */ new Map();
1374
- const evictionTimer = setInterval(
1375
- () => evictStaleRuns(runs, Date.now(), runGraceMs, maxTrackedRuns),
1376
- 6e4
1377
- );
1467
+ const evictionTimer = setInterval(() => {
1468
+ const before = runs.size;
1469
+ evictStaleRuns(runs, Date.now(), runGraceMs, maxTrackedRuns);
1470
+ if (runs.size !== before) {
1471
+ logger.debug("run eviction sweep", { before, after: runs.size, runGraceMs, maxTrackedRuns });
1472
+ } else {
1473
+ logger.trace("run eviction sweep \u2014 no evictions", { before });
1474
+ }
1475
+ }, 6e4);
1378
1476
  evictionTimer.unref?.();
1477
+ function getRequestPreview(req) {
1478
+ const preview = {
1479
+ method: req.method,
1480
+ path: req.path,
1481
+ hasQuery: req.query && Object.keys(req.query).length > 0,
1482
+ hasBody: req.body !== void 0,
1483
+ hasFile: !!req.file,
1484
+ fileCount: req.files?.length ?? 0,
1485
+ userId: req.userId ?? null
1486
+ };
1487
+ if (req.headers) {
1488
+ const redacted = {};
1489
+ for (const [k, v] of Object.entries(req.headers)) {
1490
+ if (v === void 0) continue;
1491
+ if (k.toLowerCase() === "authorization") redacted[k] = "***";
1492
+ else redacted[k] = v;
1493
+ }
1494
+ preview.headers = redacted;
1495
+ }
1496
+ if (req.query && Object.keys(req.query).length) preview.query = req.query;
1497
+ return preview;
1498
+ }
1379
1499
  async function handle(request) {
1500
+ const startMs = Date.now();
1501
+ logger.debug("handle start", { method: request.method, path: request.path });
1502
+ logger.trace("handle request details", getRequestPreview(request));
1380
1503
  try {
1381
1504
  const path = stripMountPath(request.path, options.mountPath);
1505
+ logger.trace("stripMountPath", {
1506
+ originalPath: request.path,
1507
+ mountPath: options.mountPath ?? "",
1508
+ strippedPath: path
1509
+ });
1382
1510
  const match = matchRoute(routes, request.method, path);
1383
1511
  if (match.kind === "not-found") {
1512
+ logger.warn("route not found", {
1513
+ method: request.method,
1514
+ path: request.path,
1515
+ strippedPath: path
1516
+ });
1384
1517
  throw new RuntimeHttpError(
1385
1518
  404,
1386
1519
  "NOT_FOUND",
@@ -1388,49 +1521,144 @@ function createRuntime(options) {
1388
1521
  );
1389
1522
  }
1390
1523
  if (match.kind === "method-not-allowed") {
1524
+ logger.warn("method not allowed", {
1525
+ method: request.method,
1526
+ path: request.path,
1527
+ strippedPath: path,
1528
+ allowed: match.allowed
1529
+ });
1391
1530
  const err = new RuntimeHttpError(
1392
1531
  405,
1393
1532
  "METHOD_NOT_ALLOWED",
1394
1533
  `${request.method} not allowed on ${request.path}. Allowed: ${match.allowed.join(", ")}.`
1395
1534
  );
1396
- const response = errorToResponse(err, mode);
1397
- return { ...response, headers: { ...response.headers, Allow: match.allowed.join(", ") } };
1535
+ const response2 = errorToResponse(err, mode);
1536
+ logger.debug("handle completed \u2014 405", {
1537
+ method: request.method,
1538
+ path: request.path,
1539
+ allowed: match.allowed,
1540
+ durationMs: Date.now() - startMs
1541
+ });
1542
+ return { ...response2, headers: { ...response2.headers, Allow: match.allowed.join(", ") } };
1398
1543
  }
1544
+ logger.info("route matched", {
1545
+ method: request.method,
1546
+ path: request.path,
1547
+ strippedPath: path,
1548
+ pattern: match.route.pattern.join("/"),
1549
+ params: match.params,
1550
+ requiresAuth: match.route.requiresAuth !== false
1551
+ });
1552
+ logger.debug("route matched", {
1553
+ method: request.method,
1554
+ path,
1555
+ pattern: match.route.pattern,
1556
+ params: match.params
1557
+ });
1558
+ logger.trace("route handler", {
1559
+ handler: match.route.handler.name || "anonymous",
1560
+ pattern: match.route.pattern
1561
+ });
1399
1562
  let userId = null;
1400
1563
  if (match.route.requiresAuth !== false) {
1564
+ logger.debug("resolving user", { path: request.path });
1401
1565
  try {
1402
1566
  const resolved = await options.resolveUser(request);
1403
1567
  userId = typeof resolved === "string" && resolved.length > 0 ? resolved : null;
1404
- } catch {
1568
+ logger.trace("resolveUser result", { userId: userId ?? null, hasResult: !!userId });
1569
+ } catch (err) {
1405
1570
  userId = null;
1571
+ logger.warn("resolveUser threw", {
1572
+ path: request.path,
1573
+ error: err instanceof Error ? err.message : String(err)
1574
+ });
1406
1575
  }
1407
1576
  if (userId === null) {
1577
+ logger.warn("unauthorized \u2014 could not resolve user", {
1578
+ method: request.method,
1579
+ path: request.path
1580
+ });
1408
1581
  throw new RuntimeHttpError(
1409
1582
  401,
1410
1583
  "UNAUTHORIZED",
1411
1584
  "Could not resolve an authenticated user for this request."
1412
1585
  );
1413
1586
  }
1587
+ logger.debug("user resolved", { userId });
1588
+ } else {
1589
+ logger.debug("skipping auth for public route", { path: request.path });
1414
1590
  }
1415
1591
  const resolvedRequest = { ...request, userId };
1416
- const client = createClientForRequest(options, userId);
1417
- return await match.route.handler(resolvedRequest, {
1592
+ const client = createClientForRequest(options, userId, logger);
1593
+ const routeLogger = logger.child("route");
1594
+ logger.debug("handler start", {
1595
+ method: request.method,
1596
+ path: request.path,
1597
+ handler: match.route.handler.name || "anonymous",
1598
+ userId
1599
+ });
1600
+ const response = await match.route.handler(resolvedRequest, {
1418
1601
  client,
1419
1602
  hooks: options.hooks,
1420
1603
  mode,
1421
1604
  params: match.params,
1422
1605
  heartbeatIntervalMs,
1423
1606
  runs,
1424
- capabilities
1607
+ capabilities,
1608
+ logger: routeLogger
1609
+ });
1610
+ const durationMs = Date.now() - startMs;
1611
+ logger.info("handle succeeded", {
1612
+ method: request.method,
1613
+ path: request.path,
1614
+ status: response.status,
1615
+ kind: response.kind,
1616
+ durationMs,
1617
+ userId
1618
+ });
1619
+ logger.debug("handle completed", {
1620
+ method: request.method,
1621
+ path: request.path,
1622
+ status: response.status,
1623
+ durationMs
1425
1624
  });
1625
+ logger.trace("handle response details", {
1626
+ method: request.method,
1627
+ path: request.path,
1628
+ status: response.status,
1629
+ headers: response.headers,
1630
+ kind: response.kind,
1631
+ durationMs
1632
+ });
1633
+ return response;
1426
1634
  } catch (err) {
1635
+ const durationMs = Date.now() - startMs;
1636
+ const isHttpError = err instanceof RuntimeHttpError;
1637
+ const meta = {
1638
+ method: request.method,
1639
+ path: request.path,
1640
+ durationMs,
1641
+ error: err instanceof Error ? err.message : String(err),
1642
+ code: isHttpError ? err.code : void 0,
1643
+ status: isHttpError ? err.status : void 0
1644
+ };
1645
+ if (isHttpError && err.status >= 500) {
1646
+ logger.error("handle failed \u2014 server error", meta);
1647
+ } else if (isHttpError && err.status >= 400) {
1648
+ logger.warn("handle failed \u2014 client error", meta);
1649
+ } else {
1650
+ logger.error("handle failed \u2014 unexpected error", meta);
1651
+ }
1652
+ logger.trace("handle error details", { error: err, durationMs });
1427
1653
  return errorToResponse(err, mode);
1428
1654
  }
1429
1655
  }
1430
1656
  return {
1431
1657
  handle,
1432
1658
  close() {
1659
+ logger.debug("runtime close", { trackedRuns: runs.size });
1433
1660
  clearInterval(evictionTimer);
1661
+ logger.info("runtime closed", { trackedRuns: runs.size });
1434
1662
  }
1435
1663
  };
1436
1664
  }