@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/README.md +421 -416
- package/dist/index.cjs +243 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +243 -15
- package/dist/index.js.map +1 -1
- package/package.json +71 -71
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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1376
|
-
|
|
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
|
|
1397
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|