@personaai/runtime 0.9.4 → 0.10.0
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 +30 -20
- package/dist/index.cjs +228 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +25 -6
- package/dist/index.d.ts +25 -6
- package/dist/index.js +228 -5
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
# @personaai/runtime
|
|
2
2
|
|
|
3
3
|
Framework-agnostic runtime engine for [Persona](https://persona.hasanraiyan.me). This is the
|
|
4
|
-
shared engine every framework adapter (`@personaai/express`, `@personaai/nextjs`, ...) is meant
|
|
4
|
+
shared engine every framework adapter (`@personaai/adapters/express`, `@personaai/adapters/nextjs/server`, ...) is meant
|
|
5
5
|
to be a thin translation layer over — see
|
|
6
6
|
[the SDK Ecosystem plan](https://github.com/hasanraiyan/agent-marketplace/blob/feat/ai/product-research/11-sdk-new/package-ecosystem.md).
|
|
7
7
|
|
|
8
|
-
**v0.
|
|
9
|
-
(Wave 3 of the
|
|
10
|
-
[SDK Ecosystem plan](https://github.com/hasanraiyan/agent-marketplace/blob/feat/ai/product-research/11-sdk-new/package-ecosystem.md))
|
|
11
|
-
is published and mounts this runtime as an Express Router. For non-Express hosts, see
|
|
8
|
+
**v0.10.0.** Added full Workflows engine support (`POST /workflows/:id/stream`, `GET /workflows`, `POST /workflows/runs/:runId/cancel`, `GET /workflows/runs/:runId/resume`), `workflowsWrite` gated authoring capability, and `beforeWorkflowNode`/`afterWorkflowNode` lifecycle hooks. Unified adapter [`@personaai/adapters`](https://persona.hasanraiyan.me/guides/express/quickstart) provides `express`/`nextjs`/`nestjs` bindings. For non-covered hosts, see
|
|
12
9
|
[Quickstart](#quickstart) for how to run it directly against raw Node `http`, and
|
|
13
10
|
[Not yet implemented](#not-yet-implemented) for what's missing before it's a complete Level 2
|
|
14
11
|
runtime.
|
|
@@ -46,7 +43,7 @@ world — see `RunContext`/the design notes below for why this boundary is absol
|
|
|
46
43
|
|
|
47
44
|
## Quickstart (raw Node `http`)
|
|
48
45
|
|
|
49
|
-
Using Express? Skip this section and use the published [`@personaai/
|
|
46
|
+
Using Express, Next.js, or NestJS? Skip this section and use the published [`@personaai/adapters` adapters](https://persona.hasanraiyan.me/guides/express/quickstart) — `import { toExpressRouter } from '@personaai/adapters/express'`, `import { createPersonaHandler } from '@personaai/adapters/nextjs/server'`, or `import { PersonaModule } from '@personaai/adapters/nestjs'`.
|
|
50
47
|
For every other host there's no `@personaai/node` adapter package yet, so this release ships a
|
|
51
48
|
small, tested bridge at [`examples/node-handler.ts`](./examples/node-handler.ts) in this repo
|
|
52
49
|
for running the runtime directly against Node's `http` module, just enough to demo/smoke-test
|
|
@@ -108,6 +105,10 @@ user; `resolveUser` returning `null` or throwing responds `401`.
|
|
|
108
105
|
| `GET` | `/mcps/:id/oauth/user/status` | `client.mcps.oauth.getUserConnectionStatus(id)` |
|
|
109
106
|
| `DELETE` | `/mcps/:id/oauth/user/connection` | `client.mcps.oauth.disconnectUserConnection(id)` → `204` |
|
|
110
107
|
| `DELETE` | `/mcps/:id/oauth/owner/connection` | `client.mcps.oauth.disconnectOwnerConnection(id)` → `204` |
|
|
108
|
+
| `GET` | `/workflows` | `client.workflows.list({page, limit, search, scope, visibility, isEnabled})` — read-only discovery of available workflows. |
|
|
109
|
+
| `POST` | `/workflows/:id/stream` | `client.workflows.stream(id, {input, dryRun, version})` — multi-step workflow execution with node-level AG-UI event streaming. Response carries `x-persona-run-id`. |
|
|
110
|
+
| `GET` | `/workflows/runs/:runId/resume` | Reattaches to in-flight or recent workflow execution with frame replay (`?since=<seq>`). |
|
|
111
|
+
| `POST` | `/workflows/runs/:runId/cancel` | `client.workflows.cancel(runId)` → aborts active execution. |
|
|
111
112
|
| `GET` | `/health` | `client.whoami()` → `{status, version, capabilities}`. Does **not** require `resolveUser` — it's a liveness/capability probe, not a user-scoped call. |
|
|
112
113
|
|
|
113
114
|
`scope` for memory routes is `'user'` (default) or `'agent'` (`agentId` then required).
|
|
@@ -161,10 +162,19 @@ how to enable them safely.
|
|
|
161
162
|
| `GET` | `/audit-logs` | `auditLogs` | `client.auditLogs.list({page, limit, eventType})` |
|
|
162
163
|
| `POST` | `/architect` | `architect` | `client.architect.stream({messages, resume})`, streamed out as SSE, same `x-persona-run-id`/reconnect mechanics as `/chat`. No `agentId` — the Architect builds/edits the caller's own Agents. |
|
|
163
164
|
| `GET` | `/architect/:runId/resume` | `architect` | Reattaches to the matching `POST /architect` run. |
|
|
165
|
+
| `POST` | `/workflows` | `workflowsWrite` | `client.workflows.create(input)` |
|
|
166
|
+
| `GET`/`PATCH`/`DELETE` | `/workflows/:id` | `workflowsWrite` | `client.workflows.get/update/delete(id)` |
|
|
167
|
+
| `PUT` | `/workflows/:id/draft` | `workflowsWrite` | `client.workflows.saveDraft(id, draft)` |
|
|
168
|
+
| `POST` | `/workflows/:id/publish` | `workflowsWrite` | `client.workflows.publish(id)` |
|
|
169
|
+
| `GET` | `/workflows/:id/versions` | `workflowsWrite` | `client.workflows.listVersions(id)` |
|
|
170
|
+
| `GET` | `/workflows/:id/versions/:version` | `workflowsWrite` | `client.workflows.getVersion(id, version)` |
|
|
171
|
+
| `GET` | `/workflows/:id/mermaid` | `workflowsWrite` | `client.workflows.getMermaid(id)` → `{mermaid}` |
|
|
172
|
+
| `GET` | `/workflows/:id/runs` | `workflowsWrite` | `client.workflows.listRuns(id, params)` |
|
|
173
|
+
| `GET` | `/workflows/runs/:runId` | `workflowsWrite` | `client.workflows.getRun(runId)` |
|
|
164
174
|
|
|
165
175
|
A disabled capability's routes are simply absent from the route table — a request to one 404s
|
|
166
176
|
(or, where an always-on route shares the same path with a different method, e.g. `POST /agents`
|
|
167
|
-
while only `GET /agents` is always-on, `405`) rather than 403, so a disabled capability leaks no
|
|
177
|
+
while only `GET /agents` is always-on, or `POST /workflows` while `GET /workflows` is always-on, `405`) rather than 403, so a disabled capability leaks no
|
|
168
178
|
information about what it would have done.
|
|
169
179
|
|
|
170
180
|
## Capabilities — admin surface
|
|
@@ -173,25 +183,26 @@ The routes above are split into two trust tiers, and this is a deliberate design
|
|
|
173
183
|
oversight:
|
|
174
184
|
|
|
175
185
|
- **Always on**: things an end user does in their own chat session — send messages, manage their
|
|
176
|
-
own conversations/files/memory, connect their own MCP account. Scoped entirely to whichever
|
|
186
|
+
own conversations/files/memory, connect their own MCP account, run workflows. Scoped entirely to whichever
|
|
177
187
|
user `resolveUser` returns.
|
|
178
188
|
- **Opt-in via `capabilities`**: Project-level configuration — LLM provider credentials, skill
|
|
179
189
|
authoring, knowledge base and vector store management, security audit logs, an agent-building
|
|
180
|
-
co-pilot, and full Agent/MCP-server CRUD. **Every one of these defaults to `false`.** Upgrading
|
|
190
|
+
co-pilot, workflow canvas builder/publishing, and full Agent/MCP-server CRUD. **Every one of these defaults to `false`.** Upgrading
|
|
181
191
|
this package never silently exposes new surface to whoever `resolveUser` accepts.
|
|
182
192
|
|
|
183
193
|
```ts
|
|
184
194
|
createRuntime({
|
|
185
195
|
// ...
|
|
186
196
|
capabilities: {
|
|
187
|
-
agentsWrite: false,
|
|
188
|
-
mcps: false,
|
|
189
|
-
providers: false,
|
|
190
|
-
skills: false,
|
|
191
|
-
knowledge: false,
|
|
192
|
-
stores: false,
|
|
193
|
-
auditLogs: false,
|
|
194
|
-
architect: false,
|
|
197
|
+
agentsWrite: false, // default
|
|
198
|
+
mcps: false, // default
|
|
199
|
+
providers: false, // default — holds API keys, think hard before enabling
|
|
200
|
+
skills: false, // default
|
|
201
|
+
knowledge: false, // default
|
|
202
|
+
stores: false, // default
|
|
203
|
+
auditLogs: false, // default
|
|
204
|
+
architect: false, // default
|
|
205
|
+
workflowsWrite: false, // default — authoring and version snapshots
|
|
195
206
|
},
|
|
196
207
|
});
|
|
197
208
|
```
|
|
@@ -415,9 +426,8 @@ genuine unclosed gap or an intentional package boundary, not an oversight:
|
|
|
415
426
|
design**, not a gap: see [Capabilities](#capabilities--admin-surface). A capability is on or off
|
|
416
427
|
per mount; anything finer belongs in `resolveUser` or a hook, not the runtime.
|
|
417
428
|
- Framework adapters beyond the shipped ones (`@personaai/node`, `@personaai/fastify`,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
`@personaai/nestjs` (Wave 3) have shipped.
|
|
429
|
+
`@personaai/hono`) — **by design**, not a gap: this package is the foundation they're meant to
|
|
430
|
+
wrap, not a replacement for them. `@personaai/adapters` (unified `express`/`nextjs`/`nestjs` via subpaths, Wave 3) has shipped — legacy names `@personaai/nextjs`/`@personaai/express`/`@personaai/nestjs` are deprecated shims.
|
|
421
431
|
|
|
422
432
|
## Roadmap
|
|
423
433
|
|
package/dist/index.cjs
CHANGED
|
@@ -159,7 +159,7 @@ function evictStaleRuns(runs, now, graceMs = DEFAULT_RUN_GRACE_MS, maxTracked =
|
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// src/version.ts
|
|
162
|
-
var RUNTIME_VERSION = "0.
|
|
162
|
+
var RUNTIME_VERSION = "0.10.0";
|
|
163
163
|
|
|
164
164
|
// src/routes/health.ts
|
|
165
165
|
var alwaysOnCapabilities = {
|
|
@@ -168,7 +168,8 @@ var alwaysOnCapabilities = {
|
|
|
168
168
|
agents: true,
|
|
169
169
|
files: true,
|
|
170
170
|
memory: true,
|
|
171
|
-
mcpOAuth: true
|
|
171
|
+
mcpOAuth: true,
|
|
172
|
+
workflows: true
|
|
172
173
|
};
|
|
173
174
|
var healthRoute = async (_request, ctx) => {
|
|
174
175
|
try {
|
|
@@ -322,7 +323,8 @@ function newAccumulator() {
|
|
|
322
323
|
interrupted: false,
|
|
323
324
|
erroredInBand: false,
|
|
324
325
|
threadCreateFired: false,
|
|
325
|
-
toolNames: /* @__PURE__ */ new Map()
|
|
326
|
+
toolNames: /* @__PURE__ */ new Map(),
|
|
327
|
+
nodeMeta: /* @__PURE__ */ new Map()
|
|
326
328
|
};
|
|
327
329
|
}
|
|
328
330
|
async function processEvent(event, acc, runCtx, hooks) {
|
|
@@ -342,7 +344,45 @@ async function processEvent(event, acc, runCtx, hooks) {
|
|
|
342
344
|
break;
|
|
343
345
|
}
|
|
344
346
|
case import_sdk3.EventType.CUSTOM: {
|
|
345
|
-
if (e.name === "hitl_request" || e.name === "clarification_request")
|
|
347
|
+
if (e.name === "hitl_request" || e.name === "clarification_request") {
|
|
348
|
+
acc.interrupted = true;
|
|
349
|
+
} else if (e.name === "workflow_node_started") {
|
|
350
|
+
const val = e.value ?? {};
|
|
351
|
+
const nodeId = String(val.nodeId ?? "");
|
|
352
|
+
const nodeType = String(val.nodeType ?? "");
|
|
353
|
+
const nodeLabel = String(val.nodeLabel ?? "");
|
|
354
|
+
if (nodeId) acc.nodeMeta.set(nodeId, { nodeType, nodeLabel });
|
|
355
|
+
if (runCtx.workflowId) {
|
|
356
|
+
await hooks?.beforeWorkflowNode?.({
|
|
357
|
+
userId: runCtx.userId,
|
|
358
|
+
workflowId: runCtx.workflowId,
|
|
359
|
+
nodeId,
|
|
360
|
+
nodeType,
|
|
361
|
+
nodeLabel,
|
|
362
|
+
input: val.input
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
} else if (e.name === "workflow_node_completed") {
|
|
366
|
+
const val = e.value ?? {};
|
|
367
|
+
const nodeId = String(val.nodeId ?? "");
|
|
368
|
+
const meta = acc.nodeMeta.get(nodeId);
|
|
369
|
+
const nodeType = String(val.nodeType ?? meta?.nodeType ?? "");
|
|
370
|
+
const nodeLabel = String(val.nodeLabel ?? meta?.nodeLabel ?? "");
|
|
371
|
+
if (runCtx.workflowId) {
|
|
372
|
+
await hooks?.afterWorkflowNode?.(
|
|
373
|
+
{
|
|
374
|
+
userId: runCtx.userId,
|
|
375
|
+
workflowId: runCtx.workflowId,
|
|
376
|
+
nodeId,
|
|
377
|
+
nodeType,
|
|
378
|
+
nodeLabel
|
|
379
|
+
},
|
|
380
|
+
val.output ?? val
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
} else if (e.name === "workflow_node_failed") {
|
|
384
|
+
acc.erroredInBand = true;
|
|
385
|
+
}
|
|
346
386
|
break;
|
|
347
387
|
}
|
|
348
388
|
case import_sdk3.EventType.RUN_ERROR: {
|
|
@@ -449,6 +489,7 @@ var RunDriver = class {
|
|
|
449
489
|
userId: this.runCtx.userId,
|
|
450
490
|
phase: this.runCtx.kind,
|
|
451
491
|
agentId: this.runCtx.agentId,
|
|
492
|
+
workflowId: this.runCtx.workflowId,
|
|
452
493
|
threadId: this.runCtx.threadId
|
|
453
494
|
},
|
|
454
495
|
err
|
|
@@ -865,6 +906,7 @@ var createAgent = async (request, ctx) => {
|
|
|
865
906
|
socialLinks: body.socialLinks ?? void 0,
|
|
866
907
|
modelName: typeof body.modelName === "string" ? body.modelName : void 0,
|
|
867
908
|
webSearchEnabled: typeof body.webSearchEnabled === "boolean" ? body.webSearchEnabled : void 0,
|
|
909
|
+
sandboxEnabled: typeof body.sandboxEnabled === "boolean" ? body.sandboxEnabled : void 0,
|
|
868
910
|
visibility: body.visibility,
|
|
869
911
|
category: body.category,
|
|
870
912
|
skills: Array.isArray(body.skills) ? body.skills : void 0,
|
|
@@ -1346,6 +1388,158 @@ var listAuditLogs = async (request, ctx) => {
|
|
|
1346
1388
|
return json(200, items);
|
|
1347
1389
|
};
|
|
1348
1390
|
|
|
1391
|
+
// src/routes/workflows.ts
|
|
1392
|
+
var listWorkflows = async (request, ctx) => {
|
|
1393
|
+
const items = await ctx.client.workflows.list({
|
|
1394
|
+
page: toInt(request.query.page),
|
|
1395
|
+
limit: toInt(request.query.limit),
|
|
1396
|
+
search: request.query.search,
|
|
1397
|
+
scope: request.query.scope,
|
|
1398
|
+
visibility: request.query.visibility,
|
|
1399
|
+
isEnabled: request.query.isEnabled !== void 0 ? request.query.isEnabled === "true" : void 0
|
|
1400
|
+
});
|
|
1401
|
+
return json(200, items);
|
|
1402
|
+
};
|
|
1403
|
+
var createWorkflow = async (request, ctx) => {
|
|
1404
|
+
const body = requireBodyObject(request.body);
|
|
1405
|
+
const input = {
|
|
1406
|
+
name: requireStringField(body, "name"),
|
|
1407
|
+
description: typeof body.description === "string" ? body.description : void 0,
|
|
1408
|
+
visibility: body.visibility,
|
|
1409
|
+
isEnabled: typeof body.isEnabled === "boolean" ? body.isEnabled : void 0,
|
|
1410
|
+
draft: body.draft ?? void 0
|
|
1411
|
+
};
|
|
1412
|
+
const idempotencyKey = request.headers["idempotency-key"];
|
|
1413
|
+
const workflow = await ctx.client.workflows.create(input, idempotencyKey);
|
|
1414
|
+
return json(201, workflow);
|
|
1415
|
+
};
|
|
1416
|
+
var getWorkflow = async (_request, ctx) => {
|
|
1417
|
+
const workflow = await ctx.client.workflows.get(requireParam(ctx.params, "id"));
|
|
1418
|
+
return json(200, workflow);
|
|
1419
|
+
};
|
|
1420
|
+
var updateWorkflow = async (request, ctx) => {
|
|
1421
|
+
const body = requireBodyObject(request.body);
|
|
1422
|
+
const input = {
|
|
1423
|
+
name: typeof body.name === "string" ? body.name : void 0,
|
|
1424
|
+
description: typeof body.description === "string" ? body.description : void 0,
|
|
1425
|
+
visibility: body.visibility,
|
|
1426
|
+
isEnabled: typeof body.isEnabled === "boolean" ? body.isEnabled : void 0,
|
|
1427
|
+
draft: body.draft ?? void 0
|
|
1428
|
+
};
|
|
1429
|
+
const updated = await ctx.client.workflows.update(
|
|
1430
|
+
requireParam(ctx.params, "id"),
|
|
1431
|
+
input
|
|
1432
|
+
);
|
|
1433
|
+
return json(200, updated);
|
|
1434
|
+
};
|
|
1435
|
+
var deleteWorkflow = async (_request, ctx) => {
|
|
1436
|
+
await ctx.client.workflows.delete(requireParam(ctx.params, "id"));
|
|
1437
|
+
return noContent();
|
|
1438
|
+
};
|
|
1439
|
+
var saveWorkflowDraft = async (request, ctx) => {
|
|
1440
|
+
const body = requireBodyObject(request.body);
|
|
1441
|
+
const draft = body;
|
|
1442
|
+
const updated = await ctx.client.workflows.saveDraft(
|
|
1443
|
+
requireParam(ctx.params, "id"),
|
|
1444
|
+
draft
|
|
1445
|
+
);
|
|
1446
|
+
return json(200, updated);
|
|
1447
|
+
};
|
|
1448
|
+
var publishWorkflow = async (_request, ctx) => {
|
|
1449
|
+
const version = await ctx.client.workflows.publish(requireParam(ctx.params, "id"));
|
|
1450
|
+
return json(201, version);
|
|
1451
|
+
};
|
|
1452
|
+
var listWorkflowVersions = async (request, ctx) => {
|
|
1453
|
+
const items = await ctx.client.workflows.listVersions(
|
|
1454
|
+
requireParam(ctx.params, "id"),
|
|
1455
|
+
{
|
|
1456
|
+
page: toInt(request.query.page),
|
|
1457
|
+
limit: toInt(request.query.limit)
|
|
1458
|
+
}
|
|
1459
|
+
);
|
|
1460
|
+
return json(200, items);
|
|
1461
|
+
};
|
|
1462
|
+
var getWorkflowVersion = async (_request, ctx) => {
|
|
1463
|
+
const versionNum = toInt(ctx.params.version);
|
|
1464
|
+
const version = await ctx.client.workflows.getVersion(
|
|
1465
|
+
requireParam(ctx.params, "id"),
|
|
1466
|
+
versionNum ?? 1
|
|
1467
|
+
);
|
|
1468
|
+
return json(200, version);
|
|
1469
|
+
};
|
|
1470
|
+
var getWorkflowMermaid = async (_request, ctx) => {
|
|
1471
|
+
const result = await ctx.client.workflows.getMermaid(requireParam(ctx.params, "id"));
|
|
1472
|
+
return json(200, result);
|
|
1473
|
+
};
|
|
1474
|
+
var listWorkflowRuns = async (request, ctx) => {
|
|
1475
|
+
const items = await ctx.client.workflows.listRuns(requireParam(ctx.params, "id"), {
|
|
1476
|
+
page: toInt(request.query.page),
|
|
1477
|
+
limit: toInt(request.query.limit),
|
|
1478
|
+
status: request.query.status,
|
|
1479
|
+
isDryRun: request.query.isDryRun !== void 0 ? request.query.isDryRun === "true" : void 0
|
|
1480
|
+
});
|
|
1481
|
+
return json(200, items);
|
|
1482
|
+
};
|
|
1483
|
+
var getWorkflowRun = async (_request, ctx) => {
|
|
1484
|
+
const run = await ctx.client.workflows.getRun(requireParam(ctx.params, "runId"));
|
|
1485
|
+
return json(200, run);
|
|
1486
|
+
};
|
|
1487
|
+
var cancelWorkflowRun = async (_request, ctx) => {
|
|
1488
|
+
const result = await ctx.client.workflows.cancel(requireParam(ctx.params, "runId"));
|
|
1489
|
+
return json(200, result);
|
|
1490
|
+
};
|
|
1491
|
+
|
|
1492
|
+
// src/routes/workflowStream.ts
|
|
1493
|
+
function parseWorkflowStreamBody(body) {
|
|
1494
|
+
if (body === void 0 || body === null) return {};
|
|
1495
|
+
if (typeof body !== "object") {
|
|
1496
|
+
throw new RuntimeHttpError(400, "INVALID_REQUEST", "Request body must be a JSON object.");
|
|
1497
|
+
}
|
|
1498
|
+
const b = body;
|
|
1499
|
+
return {
|
|
1500
|
+
input: b.input,
|
|
1501
|
+
dryRun: typeof b.dryRun === "boolean" ? b.dryRun : void 0,
|
|
1502
|
+
version: typeof b.version === "number" ? b.version : void 0
|
|
1503
|
+
};
|
|
1504
|
+
}
|
|
1505
|
+
var workflowStreamRoute = async (request, ctx) => {
|
|
1506
|
+
const workflowId = requireParam(ctx.params, "id");
|
|
1507
|
+
const body = parseWorkflowStreamBody(request.body);
|
|
1508
|
+
const userId = request.userId;
|
|
1509
|
+
const runCtx = {
|
|
1510
|
+
userId,
|
|
1511
|
+
kind: "workflow",
|
|
1512
|
+
workflowId,
|
|
1513
|
+
input: body.input
|
|
1514
|
+
};
|
|
1515
|
+
await ctx.hooks?.beforeRun?.(runCtx);
|
|
1516
|
+
const stream = ctx.client.workflows.stream(workflowId, {
|
|
1517
|
+
input: body.input,
|
|
1518
|
+
dryRun: body.dryRun,
|
|
1519
|
+
version: body.version
|
|
1520
|
+
});
|
|
1521
|
+
const runId = crypto.randomUUID();
|
|
1522
|
+
const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
|
|
1523
|
+
ctx.runs.set(runId, driver);
|
|
1524
|
+
try {
|
|
1525
|
+
await driver.waitForFirstFrame();
|
|
1526
|
+
} catch (err) {
|
|
1527
|
+
ctx.runs.delete(runId);
|
|
1528
|
+
throw err;
|
|
1529
|
+
}
|
|
1530
|
+
return {
|
|
1531
|
+
kind: "stream",
|
|
1532
|
+
status: 200,
|
|
1533
|
+
headers: {
|
|
1534
|
+
"content-type": "text/event-stream",
|
|
1535
|
+
"cache-control": "no-cache",
|
|
1536
|
+
connection: "keep-alive",
|
|
1537
|
+
"x-persona-run-id": runId
|
|
1538
|
+
},
|
|
1539
|
+
body: withHeartbeats(driver.subscribe(-1), ctx.heartbeatIntervalMs)
|
|
1540
|
+
};
|
|
1541
|
+
};
|
|
1542
|
+
|
|
1349
1543
|
// src/runtime.ts
|
|
1350
1544
|
function resolveCapabilities(capabilities) {
|
|
1351
1545
|
return {
|
|
@@ -1356,7 +1550,8 @@ function resolveCapabilities(capabilities) {
|
|
|
1356
1550
|
knowledge: capabilities?.knowledge ?? false,
|
|
1357
1551
|
stores: capabilities?.stores ?? false,
|
|
1358
1552
|
auditLogs: capabilities?.auditLogs ?? false,
|
|
1359
|
-
architect: capabilities?.architect ?? false
|
|
1553
|
+
architect: capabilities?.architect ?? false,
|
|
1554
|
+
workflowsWrite: capabilities?.workflowsWrite ?? false
|
|
1360
1555
|
};
|
|
1361
1556
|
}
|
|
1362
1557
|
function buildRoutes(capabilities) {
|
|
@@ -1432,6 +1627,19 @@ function buildRoutes(capabilities) {
|
|
|
1432
1627
|
method: "POST",
|
|
1433
1628
|
pattern: ["mcps", ":id", "call-tool"],
|
|
1434
1629
|
handler: callMcpTool
|
|
1630
|
+
},
|
|
1631
|
+
// Workflows — read-only discovery & execution always on; authoring ops behind workflowsWrite.
|
|
1632
|
+
{ method: "GET", pattern: ["workflows"], handler: listWorkflows },
|
|
1633
|
+
{ method: "POST", pattern: ["workflows", ":id", "stream"], handler: workflowStreamRoute },
|
|
1634
|
+
{
|
|
1635
|
+
method: "GET",
|
|
1636
|
+
pattern: ["workflows", "runs", ":runId", "resume"],
|
|
1637
|
+
handler: createResumeRoute("workflow")
|
|
1638
|
+
},
|
|
1639
|
+
{
|
|
1640
|
+
method: "POST",
|
|
1641
|
+
pattern: ["workflows", "runs", ":runId", "cancel"],
|
|
1642
|
+
handler: cancelWorkflowRun
|
|
1435
1643
|
}
|
|
1436
1644
|
];
|
|
1437
1645
|
if (capabilities.agentsWrite) {
|
|
@@ -1532,6 +1740,21 @@ function buildRoutes(capabilities) {
|
|
|
1532
1740
|
}
|
|
1533
1741
|
);
|
|
1534
1742
|
}
|
|
1743
|
+
if (capabilities.workflowsWrite) {
|
|
1744
|
+
routes.push(
|
|
1745
|
+
{ method: "POST", pattern: ["workflows"], handler: createWorkflow },
|
|
1746
|
+
{ method: "GET", pattern: ["workflows", ":id"], handler: getWorkflow },
|
|
1747
|
+
{ method: "PATCH", pattern: ["workflows", ":id"], handler: updateWorkflow },
|
|
1748
|
+
{ method: "DELETE", pattern: ["workflows", ":id"], handler: deleteWorkflow },
|
|
1749
|
+
{ method: "PUT", pattern: ["workflows", ":id", "draft"], handler: saveWorkflowDraft },
|
|
1750
|
+
{ method: "POST", pattern: ["workflows", ":id", "publish"], handler: publishWorkflow },
|
|
1751
|
+
{ method: "GET", pattern: ["workflows", ":id", "versions"], handler: listWorkflowVersions },
|
|
1752
|
+
{ method: "GET", pattern: ["workflows", ":id", "versions", ":version"], handler: getWorkflowVersion },
|
|
1753
|
+
{ method: "GET", pattern: ["workflows", ":id", "mermaid"], handler: getWorkflowMermaid },
|
|
1754
|
+
{ method: "GET", pattern: ["workflows", ":id", "runs"], handler: listWorkflowRuns },
|
|
1755
|
+
{ method: "GET", pattern: ["workflows", "runs", ":runId"], handler: getWorkflowRun }
|
|
1756
|
+
);
|
|
1757
|
+
}
|
|
1535
1758
|
return routes;
|
|
1536
1759
|
}
|
|
1537
1760
|
function resolveMode(options) {
|