@personaai/runtime 0.9.5 → 0.11.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 +31 -21
- package/dist/index.cjs +234 -8
- 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 +234 -8
- package/dist/index.js.map +1 -1
- package/package.json +72 -72
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).
|
|
@@ -159,12 +160,21 @@ how to enable them safely.
|
|
|
159
160
|
| `GET` | `/stores/:id/files` | `stores` | `client.stores.listFiles(id)` |
|
|
160
161
|
| `GET`/`PUT`/`DELETE` | `/stores/:id/file` | `stores` | `client.stores.getFile/writeFile/deleteFile(id, {path, content?})` |
|
|
161
162
|
| `GET` | `/audit-logs` | `auditLogs` | `client.auditLogs.list({page, limit, eventType})` |
|
|
162
|
-
| `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
|
+
| `POST` | `/architect` | `architect` | `client.architect.stream({messages, threadId, 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. `threadId` resumes a named Architect conversation (has no effect without an asserted external user). |
|
|
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
|
|
@@ -679,6 +720,7 @@ function parseArchitectBody(body) {
|
|
|
679
720
|
}
|
|
680
721
|
return {
|
|
681
722
|
messages: b.messages,
|
|
723
|
+
threadId: typeof b.threadId === "string" ? b.threadId : void 0,
|
|
682
724
|
resume: b.resume ?? void 0
|
|
683
725
|
};
|
|
684
726
|
}
|
|
@@ -687,6 +729,7 @@ var architectRoute = async (request, ctx) => {
|
|
|
687
729
|
logger.debug("architectRoute start", { userId: request.userId });
|
|
688
730
|
const body = parseArchitectBody(request.body);
|
|
689
731
|
logger.trace("architectRoute body", {
|
|
732
|
+
threadId: body.threadId,
|
|
690
733
|
messageCount: body.messages?.length ?? 0,
|
|
691
734
|
hasResume: !!body.resume
|
|
692
735
|
});
|
|
@@ -694,18 +737,20 @@ var architectRoute = async (request, ctx) => {
|
|
|
694
737
|
const runCtx = {
|
|
695
738
|
userId,
|
|
696
739
|
kind: "architect",
|
|
740
|
+
threadId: body.threadId,
|
|
697
741
|
messages: body.messages
|
|
698
742
|
};
|
|
699
|
-
logger.debug("beforeRun hook (architect)", { userId });
|
|
743
|
+
logger.debug("beforeRun hook (architect)", { userId, threadId: body.threadId });
|
|
700
744
|
await ctx.hooks?.beforeRun?.(runCtx);
|
|
701
745
|
logger.trace("beforeRun completed (architect)");
|
|
702
|
-
logger.debug("starting architect stream");
|
|
746
|
+
logger.debug("starting architect stream", { threadId: body.threadId });
|
|
703
747
|
const stream = ctx.client.architect.stream({
|
|
704
748
|
messages: body.messages,
|
|
749
|
+
threadId: body.threadId,
|
|
705
750
|
resume: body.resume
|
|
706
751
|
});
|
|
707
752
|
const runId = crypto.randomUUID();
|
|
708
|
-
logger.info("architect run created", { runId, userId });
|
|
753
|
+
logger.info("architect run created", { runId, userId, threadId: body.threadId });
|
|
709
754
|
const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
|
|
710
755
|
ctx.runs.set(runId, driver);
|
|
711
756
|
logger.debug("architect driver registered", { runId, trackedRuns: ctx.runs.size });
|
|
@@ -1347,6 +1392,158 @@ var listAuditLogs = async (request, ctx) => {
|
|
|
1347
1392
|
return json(200, items);
|
|
1348
1393
|
};
|
|
1349
1394
|
|
|
1395
|
+
// src/routes/workflows.ts
|
|
1396
|
+
var listWorkflows = async (request, ctx) => {
|
|
1397
|
+
const items = await ctx.client.workflows.list({
|
|
1398
|
+
page: toInt(request.query.page),
|
|
1399
|
+
limit: toInt(request.query.limit),
|
|
1400
|
+
search: request.query.search,
|
|
1401
|
+
scope: request.query.scope,
|
|
1402
|
+
visibility: request.query.visibility,
|
|
1403
|
+
isEnabled: request.query.isEnabled !== void 0 ? request.query.isEnabled === "true" : void 0
|
|
1404
|
+
});
|
|
1405
|
+
return json(200, items);
|
|
1406
|
+
};
|
|
1407
|
+
var createWorkflow = async (request, ctx) => {
|
|
1408
|
+
const body = requireBodyObject(request.body);
|
|
1409
|
+
const input = {
|
|
1410
|
+
name: requireStringField(body, "name"),
|
|
1411
|
+
description: typeof body.description === "string" ? body.description : void 0,
|
|
1412
|
+
visibility: body.visibility,
|
|
1413
|
+
isEnabled: typeof body.isEnabled === "boolean" ? body.isEnabled : void 0,
|
|
1414
|
+
draft: body.draft ?? void 0
|
|
1415
|
+
};
|
|
1416
|
+
const idempotencyKey = request.headers["idempotency-key"];
|
|
1417
|
+
const workflow = await ctx.client.workflows.create(input, idempotencyKey);
|
|
1418
|
+
return json(201, workflow);
|
|
1419
|
+
};
|
|
1420
|
+
var getWorkflow = async (_request, ctx) => {
|
|
1421
|
+
const workflow = await ctx.client.workflows.get(requireParam(ctx.params, "id"));
|
|
1422
|
+
return json(200, workflow);
|
|
1423
|
+
};
|
|
1424
|
+
var updateWorkflow = async (request, ctx) => {
|
|
1425
|
+
const body = requireBodyObject(request.body);
|
|
1426
|
+
const input = {
|
|
1427
|
+
name: typeof body.name === "string" ? body.name : void 0,
|
|
1428
|
+
description: typeof body.description === "string" ? body.description : void 0,
|
|
1429
|
+
visibility: body.visibility,
|
|
1430
|
+
isEnabled: typeof body.isEnabled === "boolean" ? body.isEnabled : void 0,
|
|
1431
|
+
draft: body.draft ?? void 0
|
|
1432
|
+
};
|
|
1433
|
+
const updated = await ctx.client.workflows.update(
|
|
1434
|
+
requireParam(ctx.params, "id"),
|
|
1435
|
+
input
|
|
1436
|
+
);
|
|
1437
|
+
return json(200, updated);
|
|
1438
|
+
};
|
|
1439
|
+
var deleteWorkflow = async (_request, ctx) => {
|
|
1440
|
+
await ctx.client.workflows.delete(requireParam(ctx.params, "id"));
|
|
1441
|
+
return noContent();
|
|
1442
|
+
};
|
|
1443
|
+
var saveWorkflowDraft = async (request, ctx) => {
|
|
1444
|
+
const body = requireBodyObject(request.body);
|
|
1445
|
+
const draft = body;
|
|
1446
|
+
const updated = await ctx.client.workflows.saveDraft(
|
|
1447
|
+
requireParam(ctx.params, "id"),
|
|
1448
|
+
draft
|
|
1449
|
+
);
|
|
1450
|
+
return json(200, updated);
|
|
1451
|
+
};
|
|
1452
|
+
var publishWorkflow = async (_request, ctx) => {
|
|
1453
|
+
const version = await ctx.client.workflows.publish(requireParam(ctx.params, "id"));
|
|
1454
|
+
return json(201, version);
|
|
1455
|
+
};
|
|
1456
|
+
var listWorkflowVersions = async (request, ctx) => {
|
|
1457
|
+
const items = await ctx.client.workflows.listVersions(
|
|
1458
|
+
requireParam(ctx.params, "id"),
|
|
1459
|
+
{
|
|
1460
|
+
page: toInt(request.query.page),
|
|
1461
|
+
limit: toInt(request.query.limit)
|
|
1462
|
+
}
|
|
1463
|
+
);
|
|
1464
|
+
return json(200, items);
|
|
1465
|
+
};
|
|
1466
|
+
var getWorkflowVersion = async (_request, ctx) => {
|
|
1467
|
+
const versionNum = toInt(ctx.params.version);
|
|
1468
|
+
const version = await ctx.client.workflows.getVersion(
|
|
1469
|
+
requireParam(ctx.params, "id"),
|
|
1470
|
+
versionNum ?? 1
|
|
1471
|
+
);
|
|
1472
|
+
return json(200, version);
|
|
1473
|
+
};
|
|
1474
|
+
var getWorkflowMermaid = async (_request, ctx) => {
|
|
1475
|
+
const result = await ctx.client.workflows.getMermaid(requireParam(ctx.params, "id"));
|
|
1476
|
+
return json(200, result);
|
|
1477
|
+
};
|
|
1478
|
+
var listWorkflowRuns = async (request, ctx) => {
|
|
1479
|
+
const items = await ctx.client.workflows.listRuns(requireParam(ctx.params, "id"), {
|
|
1480
|
+
page: toInt(request.query.page),
|
|
1481
|
+
limit: toInt(request.query.limit),
|
|
1482
|
+
status: request.query.status,
|
|
1483
|
+
isDryRun: request.query.isDryRun !== void 0 ? request.query.isDryRun === "true" : void 0
|
|
1484
|
+
});
|
|
1485
|
+
return json(200, items);
|
|
1486
|
+
};
|
|
1487
|
+
var getWorkflowRun = async (_request, ctx) => {
|
|
1488
|
+
const run = await ctx.client.workflows.getRun(requireParam(ctx.params, "runId"));
|
|
1489
|
+
return json(200, run);
|
|
1490
|
+
};
|
|
1491
|
+
var cancelWorkflowRun = async (_request, ctx) => {
|
|
1492
|
+
const result = await ctx.client.workflows.cancel(requireParam(ctx.params, "runId"));
|
|
1493
|
+
return json(200, result);
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
// src/routes/workflowStream.ts
|
|
1497
|
+
function parseWorkflowStreamBody(body) {
|
|
1498
|
+
if (body === void 0 || body === null) return {};
|
|
1499
|
+
if (typeof body !== "object") {
|
|
1500
|
+
throw new RuntimeHttpError(400, "INVALID_REQUEST", "Request body must be a JSON object.");
|
|
1501
|
+
}
|
|
1502
|
+
const b = body;
|
|
1503
|
+
return {
|
|
1504
|
+
input: b.input,
|
|
1505
|
+
dryRun: typeof b.dryRun === "boolean" ? b.dryRun : void 0,
|
|
1506
|
+
version: typeof b.version === "number" ? b.version : void 0
|
|
1507
|
+
};
|
|
1508
|
+
}
|
|
1509
|
+
var workflowStreamRoute = async (request, ctx) => {
|
|
1510
|
+
const workflowId = requireParam(ctx.params, "id");
|
|
1511
|
+
const body = parseWorkflowStreamBody(request.body);
|
|
1512
|
+
const userId = request.userId;
|
|
1513
|
+
const runCtx = {
|
|
1514
|
+
userId,
|
|
1515
|
+
kind: "workflow",
|
|
1516
|
+
workflowId,
|
|
1517
|
+
input: body.input
|
|
1518
|
+
};
|
|
1519
|
+
await ctx.hooks?.beforeRun?.(runCtx);
|
|
1520
|
+
const stream = ctx.client.workflows.stream(workflowId, {
|
|
1521
|
+
input: body.input,
|
|
1522
|
+
dryRun: body.dryRun,
|
|
1523
|
+
version: body.version
|
|
1524
|
+
});
|
|
1525
|
+
const runId = crypto.randomUUID();
|
|
1526
|
+
const driver = new RunDriver(runId, runCtx, stream, ctx.hooks, ctx.mode);
|
|
1527
|
+
ctx.runs.set(runId, driver);
|
|
1528
|
+
try {
|
|
1529
|
+
await driver.waitForFirstFrame();
|
|
1530
|
+
} catch (err) {
|
|
1531
|
+
ctx.runs.delete(runId);
|
|
1532
|
+
throw err;
|
|
1533
|
+
}
|
|
1534
|
+
return {
|
|
1535
|
+
kind: "stream",
|
|
1536
|
+
status: 200,
|
|
1537
|
+
headers: {
|
|
1538
|
+
"content-type": "text/event-stream",
|
|
1539
|
+
"cache-control": "no-cache",
|
|
1540
|
+
connection: "keep-alive",
|
|
1541
|
+
"x-persona-run-id": runId
|
|
1542
|
+
},
|
|
1543
|
+
body: withHeartbeats(driver.subscribe(-1), ctx.heartbeatIntervalMs)
|
|
1544
|
+
};
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1350
1547
|
// src/runtime.ts
|
|
1351
1548
|
function resolveCapabilities(capabilities) {
|
|
1352
1549
|
return {
|
|
@@ -1357,7 +1554,8 @@ function resolveCapabilities(capabilities) {
|
|
|
1357
1554
|
knowledge: capabilities?.knowledge ?? false,
|
|
1358
1555
|
stores: capabilities?.stores ?? false,
|
|
1359
1556
|
auditLogs: capabilities?.auditLogs ?? false,
|
|
1360
|
-
architect: capabilities?.architect ?? false
|
|
1557
|
+
architect: capabilities?.architect ?? false,
|
|
1558
|
+
workflowsWrite: capabilities?.workflowsWrite ?? false
|
|
1361
1559
|
};
|
|
1362
1560
|
}
|
|
1363
1561
|
function buildRoutes(capabilities) {
|
|
@@ -1433,6 +1631,19 @@ function buildRoutes(capabilities) {
|
|
|
1433
1631
|
method: "POST",
|
|
1434
1632
|
pattern: ["mcps", ":id", "call-tool"],
|
|
1435
1633
|
handler: callMcpTool
|
|
1634
|
+
},
|
|
1635
|
+
// Workflows — read-only discovery & execution always on; authoring ops behind workflowsWrite.
|
|
1636
|
+
{ method: "GET", pattern: ["workflows"], handler: listWorkflows },
|
|
1637
|
+
{ method: "POST", pattern: ["workflows", ":id", "stream"], handler: workflowStreamRoute },
|
|
1638
|
+
{
|
|
1639
|
+
method: "GET",
|
|
1640
|
+
pattern: ["workflows", "runs", ":runId", "resume"],
|
|
1641
|
+
handler: createResumeRoute("workflow")
|
|
1642
|
+
},
|
|
1643
|
+
{
|
|
1644
|
+
method: "POST",
|
|
1645
|
+
pattern: ["workflows", "runs", ":runId", "cancel"],
|
|
1646
|
+
handler: cancelWorkflowRun
|
|
1436
1647
|
}
|
|
1437
1648
|
];
|
|
1438
1649
|
if (capabilities.agentsWrite) {
|
|
@@ -1533,6 +1744,21 @@ function buildRoutes(capabilities) {
|
|
|
1533
1744
|
}
|
|
1534
1745
|
);
|
|
1535
1746
|
}
|
|
1747
|
+
if (capabilities.workflowsWrite) {
|
|
1748
|
+
routes.push(
|
|
1749
|
+
{ method: "POST", pattern: ["workflows"], handler: createWorkflow },
|
|
1750
|
+
{ method: "GET", pattern: ["workflows", ":id"], handler: getWorkflow },
|
|
1751
|
+
{ method: "PATCH", pattern: ["workflows", ":id"], handler: updateWorkflow },
|
|
1752
|
+
{ method: "DELETE", pattern: ["workflows", ":id"], handler: deleteWorkflow },
|
|
1753
|
+
{ method: "PUT", pattern: ["workflows", ":id", "draft"], handler: saveWorkflowDraft },
|
|
1754
|
+
{ method: "POST", pattern: ["workflows", ":id", "publish"], handler: publishWorkflow },
|
|
1755
|
+
{ method: "GET", pattern: ["workflows", ":id", "versions"], handler: listWorkflowVersions },
|
|
1756
|
+
{ method: "GET", pattern: ["workflows", ":id", "versions", ":version"], handler: getWorkflowVersion },
|
|
1757
|
+
{ method: "GET", pattern: ["workflows", ":id", "mermaid"], handler: getWorkflowMermaid },
|
|
1758
|
+
{ method: "GET", pattern: ["workflows", ":id", "runs"], handler: listWorkflowRuns },
|
|
1759
|
+
{ method: "GET", pattern: ["workflows", "runs", ":runId"], handler: getWorkflowRun }
|
|
1760
|
+
);
|
|
1761
|
+
}
|
|
1536
1762
|
return routes;
|
|
1537
1763
|
}
|
|
1538
1764
|
function resolveMode(options) {
|