@personaai/runtime 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ChatMessageInput } from '@personaai/sdk';
1
+ import { ChatMessageInput, LogLevel, Logger } from '@personaai/sdk';
2
2
 
3
3
  type RuntimeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
4
4
  /** A single uploaded file — present on `RuntimeRequest.file` for a multipart `POST /files`. */
@@ -220,6 +220,10 @@ interface CreateRuntimeOptions {
220
220
  maxTrackedRuns?: number;
221
221
  /** Opt-in switches for Project-level admin surface. See {@link RuntimeCapabilities} — everything defaults to off. */
222
222
  capabilities?: RuntimeCapabilities;
223
+ /** Log level for the runtime — off by default. Overrides the global level set via `setLogLevel()` from `@personaai/sdk`. */
224
+ logLevel?: LogLevel;
225
+ /** Custom logger instance — when provided, `logLevel` is ignored. */
226
+ logger?: Logger;
223
227
  }
224
228
  interface Runtime {
225
229
  handle(request: RuntimeRequest): Promise<RuntimeResponse>;
@@ -242,6 +246,6 @@ declare class RuntimeHttpError extends Error {
242
246
  constructor(status: number, code: string, message: string);
243
247
  }
244
248
 
245
- declare const RUNTIME_VERSION = "0.5.2";
249
+ declare const RUNTIME_VERSION = "0.5.3";
246
250
 
247
251
  export { type CreateRuntimeOptions, type ErrorContext, type FileUploadContext, type MemoryWriteContext, RUNTIME_VERSION, type ResolveUser, type RunContext, type RunResult, type Runtime, type RuntimeBinaryResponse, type RuntimeBufferedResponse, type RuntimeCapabilities, type RuntimeHooks, RuntimeHttpError, type RuntimeMethod, type RuntimeRequest, type RuntimeResponse, type RuntimeStreamResponse, type RuntimeUploadedFile, type ThreadCreateContext, type ToolCallContext, createRuntime };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ChatMessageInput } from '@personaai/sdk';
1
+ import { ChatMessageInput, LogLevel, Logger } from '@personaai/sdk';
2
2
 
3
3
  type RuntimeMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
4
4
  /** A single uploaded file — present on `RuntimeRequest.file` for a multipart `POST /files`. */
@@ -220,6 +220,10 @@ interface CreateRuntimeOptions {
220
220
  maxTrackedRuns?: number;
221
221
  /** Opt-in switches for Project-level admin surface. See {@link RuntimeCapabilities} — everything defaults to off. */
222
222
  capabilities?: RuntimeCapabilities;
223
+ /** Log level for the runtime — off by default. Overrides the global level set via `setLogLevel()` from `@personaai/sdk`. */
224
+ logLevel?: LogLevel;
225
+ /** Custom logger instance — when provided, `logLevel` is ignored. */
226
+ logger?: Logger;
223
227
  }
224
228
  interface Runtime {
225
229
  handle(request: RuntimeRequest): Promise<RuntimeResponse>;
@@ -242,6 +246,6 @@ declare class RuntimeHttpError extends Error {
242
246
  constructor(status: number, code: string, message: string);
243
247
  }
244
248
 
245
- declare const RUNTIME_VERSION = "0.5.2";
249
+ declare const RUNTIME_VERSION = "0.5.3";
246
250
 
247
251
  export { type CreateRuntimeOptions, type ErrorContext, type FileUploadContext, type MemoryWriteContext, RUNTIME_VERSION, type ResolveUser, type RunContext, type RunResult, type Runtime, type RuntimeBinaryResponse, type RuntimeBufferedResponse, type RuntimeCapabilities, type RuntimeHooks, RuntimeHttpError, type RuntimeMethod, type RuntimeRequest, type RuntimeResponse, type RuntimeStreamResponse, type RuntimeUploadedFile, type ThreadCreateContext, type ToolCallContext, createRuntime };
package/dist/index.js CHANGED
@@ -41,12 +41,14 @@ function stripMountPath(path, mountPath) {
41
41
 
42
42
  // src/client-factory.ts
43
43
  import { PersonaClient } from "@personaai/sdk";
44
- function createClientForRequest(options, userId) {
44
+ function createClientForRequest(options, userId, logger) {
45
+ const clientLogger = logger ? logger.child("sdk") : void 0;
45
46
  return new PersonaClient({
46
47
  baseUrl: options.baseUrl,
47
48
  credential: options.credential,
48
49
  externalUserId: userId ?? void 0,
49
- fetch: options.fetch
50
+ fetch: options.fetch,
51
+ logger: clientLogger
50
52
  });
51
53
  }
52
54
 
@@ -105,6 +107,9 @@ function errorToResponse(err, mode) {
105
107
  };
106
108
  }
107
109
 
110
+ // src/runtime.ts
111
+ import { createLogger } from "@personaai/sdk";
112
+
108
113
  // src/runRegistry.ts
109
114
  var DEFAULT_RUN_GRACE_MS = 5 * 60 * 1e3;
110
115
  var DEFAULT_MAX_TRACKED_RUNS = 1e3;
@@ -126,7 +131,7 @@ function evictStaleRuns(runs, now, graceMs = DEFAULT_RUN_GRACE_MS, maxTracked =
126
131
  }
127
132
 
128
133
  // src/version.ts
129
- var RUNTIME_VERSION = "0.5.2";
134
+ var RUNTIME_VERSION = "0.5.3";
130
135
 
131
136
  // src/routes/health.ts
132
137
  var alwaysOnCapabilities = {
@@ -448,7 +453,16 @@ function parseChatBody(body) {
448
453
  };
449
454
  }
450
455
  var chatRoute = async (request, ctx) => {
456
+ const logger = ctx.logger.child("chat");
457
+ logger.debug("chatRoute start", { userId: request.userId, hasBody: !!request.body });
451
458
  const body = parseChatBody(request.body);
459
+ logger.trace("chatRoute body", {
460
+ agentId: body.agentId,
461
+ threadId: body.threadId,
462
+ messageCount: body.messages?.length ?? 0,
463
+ hasResume: !!body.resume,
464
+ hasContextOverride: !!body.contextOverride
465
+ });
452
466
  const userId = request.userId;
453
467
  const runCtx = {
454
468
  userId,
@@ -457,7 +471,10 @@ var chatRoute = async (request, ctx) => {
457
471
  threadId: body.threadId,
458
472
  messages: body.messages
459
473
  };
474
+ logger.debug("beforeRun hook", { agentId: body.agentId, threadId: body.threadId });
460
475
  await ctx.hooks?.beforeRun?.(runCtx);
476
+ logger.trace("beforeRun completed", { agentId: body.agentId });
477
+ logger.debug("starting chat stream", { agentId: body.agentId, threadId: body.threadId });
461
478
  const stream = ctx.client.chat.stream(body.agentId, {
462
479
  messages: body.messages,
463
480
  threadId: body.threadId,
@@ -465,9 +482,28 @@ var chatRoute = async (request, ctx) => {
465
482
  contextOverride: body.contextOverride
466
483
  });
467
484
  const runId = crypto.randomUUID();
485
+ logger.info("chat run created", {
486
+ runId,
487
+ agentId: body.agentId,
488
+ threadId: body.threadId,
489
+ userId
490
+ });
468
491
  const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
469
492
  ctx.runs.set(runId, driver);
470
- await driver.waitForFirstFrame();
493
+ logger.debug("run driver registered", { runId, trackedRuns: ctx.runs.size });
494
+ try {
495
+ await driver.waitForFirstFrame();
496
+ logger.debug("chat run first frame ready", { runId });
497
+ } catch (err) {
498
+ logger.warn("chat run failed before first frame", {
499
+ runId,
500
+ agentId: body.agentId,
501
+ error: err instanceof Error ? err.message : String(err)
502
+ });
503
+ ctx.runs.delete(runId);
504
+ throw err;
505
+ }
506
+ logger.info("chat stream ready", { runId, agentId: body.agentId });
471
507
  return {
472
508
  kind: "stream",
473
509
  status: 200,
@@ -500,22 +536,44 @@ function parseArchitectBody(body) {
500
536
  };
501
537
  }
502
538
  var architectRoute = async (request, ctx) => {
539
+ const logger = ctx.logger.child("architect");
540
+ logger.debug("architectRoute start", { userId: request.userId });
503
541
  const body = parseArchitectBody(request.body);
542
+ logger.trace("architectRoute body", {
543
+ messageCount: body.messages?.length ?? 0,
544
+ hasResume: !!body.resume
545
+ });
504
546
  const userId = request.userId;
505
547
  const runCtx = {
506
548
  userId,
507
549
  kind: "architect",
508
550
  messages: body.messages
509
551
  };
552
+ logger.debug("beforeRun hook (architect)", { userId });
510
553
  await ctx.hooks?.beforeRun?.(runCtx);
554
+ logger.trace("beforeRun completed (architect)");
555
+ logger.debug("starting architect stream");
511
556
  const stream = ctx.client.architect.stream({
512
557
  messages: body.messages,
513
558
  resume: body.resume
514
559
  });
515
560
  const runId = crypto.randomUUID();
561
+ logger.info("architect run created", { runId, userId });
516
562
  const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
517
563
  ctx.runs.set(runId, driver);
518
- await driver.waitForFirstFrame();
564
+ logger.debug("architect driver registered", { runId, trackedRuns: ctx.runs.size });
565
+ try {
566
+ await driver.waitForFirstFrame();
567
+ logger.debug("architect first frame ready", { runId });
568
+ } catch (err) {
569
+ logger.warn("architect run failed before first frame", {
570
+ runId,
571
+ error: err instanceof Error ? err.message : String(err)
572
+ });
573
+ ctx.runs.delete(runId);
574
+ throw err;
575
+ }
576
+ logger.info("architect stream ready", { runId });
519
577
  return {
520
578
  kind: "stream",
521
579
  status: 200,
@@ -1348,23 +1406,86 @@ function createRuntime(options) {
1348
1406
  if (!options.baseUrl) throw new Error('createRuntime: "baseUrl" is required');
1349
1407
  if (!options.credential) throw new Error('createRuntime: "credential" is required');
1350
1408
  if (!options.resolveUser) throw new Error('createRuntime: "resolveUser" is required');
1409
+ const logger = options.logger ?? createLogger(
1410
+ "runtime",
1411
+ options.logLevel !== void 0 ? { level: options.logLevel } : void 0
1412
+ );
1351
1413
  const capabilities = resolveCapabilities(options.capabilities);
1352
1414
  const routes = buildRoutes(capabilities);
1353
1415
  const mode = resolveMode(options);
1354
1416
  const heartbeatIntervalMs = options.heartbeatIntervalMs ?? 15e3;
1355
1417
  const runGraceMs = options.runGraceMs ?? DEFAULT_RUN_GRACE_MS;
1356
1418
  const maxTrackedRuns = options.maxTrackedRuns ?? DEFAULT_MAX_TRACKED_RUNS;
1419
+ logger.debug("runtime init", {
1420
+ mode,
1421
+ mountPath: options.mountPath ?? "",
1422
+ capabilities,
1423
+ heartbeatIntervalMs,
1424
+ runGraceMs,
1425
+ maxTrackedRuns
1426
+ });
1427
+ const enabledCaps = Object.entries(capabilities).filter(([, v]) => v).map(([k]) => k);
1428
+ logger.info("runtime created", {
1429
+ mode,
1430
+ mountPath: options.mountPath ?? "",
1431
+ enabledCapabilities: enabledCaps.length ? enabledCaps : ["(core only)"]
1432
+ });
1433
+ logger.trace("runtime config", {
1434
+ baseUrl: options.baseUrl,
1435
+ hasHooks: !!options.hooks,
1436
+ routeCount: routes.length
1437
+ });
1357
1438
  const runs = /* @__PURE__ */ new Map();
1358
- const evictionTimer = setInterval(
1359
- () => evictStaleRuns(runs, Date.now(), runGraceMs, maxTrackedRuns),
1360
- 6e4
1361
- );
1439
+ const evictionTimer = setInterval(() => {
1440
+ const before = runs.size;
1441
+ evictStaleRuns(runs, Date.now(), runGraceMs, maxTrackedRuns);
1442
+ if (runs.size !== before) {
1443
+ logger.debug("run eviction sweep", { before, after: runs.size, runGraceMs, maxTrackedRuns });
1444
+ } else {
1445
+ logger.trace("run eviction sweep \u2014 no evictions", { before });
1446
+ }
1447
+ }, 6e4);
1362
1448
  evictionTimer.unref?.();
1449
+ function getRequestPreview(req) {
1450
+ const preview = {
1451
+ method: req.method,
1452
+ path: req.path,
1453
+ hasQuery: req.query && Object.keys(req.query).length > 0,
1454
+ hasBody: req.body !== void 0,
1455
+ hasFile: !!req.file,
1456
+ fileCount: req.files?.length ?? 0,
1457
+ userId: req.userId ?? null
1458
+ };
1459
+ if (req.headers) {
1460
+ const redacted = {};
1461
+ for (const [k, v] of Object.entries(req.headers)) {
1462
+ if (v === void 0) continue;
1463
+ if (k.toLowerCase() === "authorization") redacted[k] = "***";
1464
+ else redacted[k] = v;
1465
+ }
1466
+ preview.headers = redacted;
1467
+ }
1468
+ if (req.query && Object.keys(req.query).length) preview.query = req.query;
1469
+ return preview;
1470
+ }
1363
1471
  async function handle(request) {
1472
+ const startMs = Date.now();
1473
+ logger.debug("handle start", { method: request.method, path: request.path });
1474
+ logger.trace("handle request details", getRequestPreview(request));
1364
1475
  try {
1365
1476
  const path = stripMountPath(request.path, options.mountPath);
1477
+ logger.trace("stripMountPath", {
1478
+ originalPath: request.path,
1479
+ mountPath: options.mountPath ?? "",
1480
+ strippedPath: path
1481
+ });
1366
1482
  const match = matchRoute(routes, request.method, path);
1367
1483
  if (match.kind === "not-found") {
1484
+ logger.warn("route not found", {
1485
+ method: request.method,
1486
+ path: request.path,
1487
+ strippedPath: path
1488
+ });
1368
1489
  throw new RuntimeHttpError(
1369
1490
  404,
1370
1491
  "NOT_FOUND",
@@ -1372,49 +1493,144 @@ function createRuntime(options) {
1372
1493
  );
1373
1494
  }
1374
1495
  if (match.kind === "method-not-allowed") {
1496
+ logger.warn("method not allowed", {
1497
+ method: request.method,
1498
+ path: request.path,
1499
+ strippedPath: path,
1500
+ allowed: match.allowed
1501
+ });
1375
1502
  const err = new RuntimeHttpError(
1376
1503
  405,
1377
1504
  "METHOD_NOT_ALLOWED",
1378
1505
  `${request.method} not allowed on ${request.path}. Allowed: ${match.allowed.join(", ")}.`
1379
1506
  );
1380
- const response = errorToResponse(err, mode);
1381
- return { ...response, headers: { ...response.headers, Allow: match.allowed.join(", ") } };
1507
+ const response2 = errorToResponse(err, mode);
1508
+ logger.debug("handle completed \u2014 405", {
1509
+ method: request.method,
1510
+ path: request.path,
1511
+ allowed: match.allowed,
1512
+ durationMs: Date.now() - startMs
1513
+ });
1514
+ return { ...response2, headers: { ...response2.headers, Allow: match.allowed.join(", ") } };
1382
1515
  }
1516
+ logger.info("route matched", {
1517
+ method: request.method,
1518
+ path: request.path,
1519
+ strippedPath: path,
1520
+ pattern: match.route.pattern.join("/"),
1521
+ params: match.params,
1522
+ requiresAuth: match.route.requiresAuth !== false
1523
+ });
1524
+ logger.debug("route matched", {
1525
+ method: request.method,
1526
+ path,
1527
+ pattern: match.route.pattern,
1528
+ params: match.params
1529
+ });
1530
+ logger.trace("route handler", {
1531
+ handler: match.route.handler.name || "anonymous",
1532
+ pattern: match.route.pattern
1533
+ });
1383
1534
  let userId = null;
1384
1535
  if (match.route.requiresAuth !== false) {
1536
+ logger.debug("resolving user", { path: request.path });
1385
1537
  try {
1386
1538
  const resolved = await options.resolveUser(request);
1387
1539
  userId = typeof resolved === "string" && resolved.length > 0 ? resolved : null;
1388
- } catch {
1540
+ logger.trace("resolveUser result", { userId: userId ?? null, hasResult: !!userId });
1541
+ } catch (err) {
1389
1542
  userId = null;
1543
+ logger.warn("resolveUser threw", {
1544
+ path: request.path,
1545
+ error: err instanceof Error ? err.message : String(err)
1546
+ });
1390
1547
  }
1391
1548
  if (userId === null) {
1549
+ logger.warn("unauthorized \u2014 could not resolve user", {
1550
+ method: request.method,
1551
+ path: request.path
1552
+ });
1392
1553
  throw new RuntimeHttpError(
1393
1554
  401,
1394
1555
  "UNAUTHORIZED",
1395
1556
  "Could not resolve an authenticated user for this request."
1396
1557
  );
1397
1558
  }
1559
+ logger.debug("user resolved", { userId });
1560
+ } else {
1561
+ logger.debug("skipping auth for public route", { path: request.path });
1398
1562
  }
1399
1563
  const resolvedRequest = { ...request, userId };
1400
- const client = createClientForRequest(options, userId);
1401
- return await match.route.handler(resolvedRequest, {
1564
+ const client = createClientForRequest(options, userId, logger);
1565
+ const routeLogger = logger.child("route");
1566
+ logger.debug("handler start", {
1567
+ method: request.method,
1568
+ path: request.path,
1569
+ handler: match.route.handler.name || "anonymous",
1570
+ userId
1571
+ });
1572
+ const response = await match.route.handler(resolvedRequest, {
1402
1573
  client,
1403
1574
  hooks: options.hooks,
1404
1575
  mode,
1405
1576
  params: match.params,
1406
1577
  heartbeatIntervalMs,
1407
1578
  runs,
1408
- capabilities
1579
+ capabilities,
1580
+ logger: routeLogger
1409
1581
  });
1582
+ const durationMs = Date.now() - startMs;
1583
+ logger.info("handle succeeded", {
1584
+ method: request.method,
1585
+ path: request.path,
1586
+ status: response.status,
1587
+ kind: response.kind,
1588
+ durationMs,
1589
+ userId
1590
+ });
1591
+ logger.debug("handle completed", {
1592
+ method: request.method,
1593
+ path: request.path,
1594
+ status: response.status,
1595
+ durationMs
1596
+ });
1597
+ logger.trace("handle response details", {
1598
+ method: request.method,
1599
+ path: request.path,
1600
+ status: response.status,
1601
+ headers: response.headers,
1602
+ kind: response.kind,
1603
+ durationMs
1604
+ });
1605
+ return response;
1410
1606
  } catch (err) {
1607
+ const durationMs = Date.now() - startMs;
1608
+ const isHttpError = err instanceof RuntimeHttpError;
1609
+ const meta = {
1610
+ method: request.method,
1611
+ path: request.path,
1612
+ durationMs,
1613
+ error: err instanceof Error ? err.message : String(err),
1614
+ code: isHttpError ? err.code : void 0,
1615
+ status: isHttpError ? err.status : void 0
1616
+ };
1617
+ if (isHttpError && err.status >= 500) {
1618
+ logger.error("handle failed \u2014 server error", meta);
1619
+ } else if (isHttpError && err.status >= 400) {
1620
+ logger.warn("handle failed \u2014 client error", meta);
1621
+ } else {
1622
+ logger.error("handle failed \u2014 unexpected error", meta);
1623
+ }
1624
+ logger.trace("handle error details", { error: err, durationMs });
1411
1625
  return errorToResponse(err, mode);
1412
1626
  }
1413
1627
  }
1414
1628
  return {
1415
1629
  handle,
1416
1630
  close() {
1631
+ logger.debug("runtime close", { trackedRuns: runs.size });
1417
1632
  clearInterval(evictionTimer);
1633
+ logger.info("runtime closed", { trackedRuns: runs.size });
1418
1634
  }
1419
1635
  };
1420
1636
  }