@cogitator-ai/koa 0.8.6 → 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.
Files changed (58) hide show
  1. package/README.md +102 -40
  2. package/dist/app.d.ts +7 -0
  3. package/dist/app.d.ts.map +1 -1
  4. package/dist/app.js +31 -14
  5. package/dist/app.js.map +1 -1
  6. package/dist/index.d.ts +2 -2
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/middleware/auth.d.ts +6 -0
  11. package/dist/middleware/auth.d.ts.map +1 -1
  12. package/dist/middleware/auth.js +17 -2
  13. package/dist/middleware/auth.js.map +1 -1
  14. package/dist/middleware/body-parser.d.ts.map +1 -1
  15. package/dist/middleware/body-parser.js +3 -4
  16. package/dist/middleware/body-parser.js.map +1 -1
  17. package/dist/middleware/context.d.ts +5 -0
  18. package/dist/middleware/context.d.ts.map +1 -1
  19. package/dist/middleware/context.js +10 -1
  20. package/dist/middleware/context.js.map +1 -1
  21. package/dist/middleware/error-handler.d.ts.map +1 -1
  22. package/dist/middleware/error-handler.js +16 -36
  23. package/dist/middleware/error-handler.js.map +1 -1
  24. package/dist/middleware/index.d.ts +1 -1
  25. package/dist/middleware/index.d.ts.map +1 -1
  26. package/dist/middleware/index.js +1 -1
  27. package/dist/middleware/index.js.map +1 -1
  28. package/dist/middleware/unmatched.d.ts +20 -0
  29. package/dist/middleware/unmatched.d.ts.map +1 -0
  30. package/dist/middleware/unmatched.js +83 -0
  31. package/dist/middleware/unmatched.js.map +1 -0
  32. package/dist/routes/agents.d.ts.map +1 -1
  33. package/dist/routes/agents.js +91 -50
  34. package/dist/routes/agents.js.map +1 -1
  35. package/dist/routes/swarms.d.ts.map +1 -1
  36. package/dist/routes/swarms.js +32 -26
  37. package/dist/routes/swarms.js.map +1 -1
  38. package/dist/routes/threads.d.ts.map +1 -1
  39. package/dist/routes/threads.js +25 -26
  40. package/dist/routes/threads.js.map +1 -1
  41. package/dist/routes/workflows.d.ts.map +1 -1
  42. package/dist/routes/workflows.js +16 -25
  43. package/dist/routes/workflows.js.map +1 -1
  44. package/dist/types.d.ts +66 -2
  45. package/dist/types.d.ts.map +1 -1
  46. package/dist/utils/errors.d.ts +14 -6
  47. package/dist/utils/errors.d.ts.map +1 -1
  48. package/dist/utils/errors.js +29 -12
  49. package/dist/utils/errors.js.map +1 -1
  50. package/dist/utils/request.d.ts +10 -0
  51. package/dist/utils/request.d.ts.map +1 -1
  52. package/dist/utils/request.js +14 -0
  53. package/dist/utils/request.js.map +1 -1
  54. package/dist/websocket/handler.d.ts +6 -0
  55. package/dist/websocket/handler.d.ts.map +1 -1
  56. package/dist/websocket/handler.js +48 -18
  57. package/dist/websocket/handler.js.map +1 -1
  58. package/package.json +8 -8
@@ -1,4 +1,5 @@
1
- import { CogitatorError, ERROR_STATUS_CODES, ErrorCode } from '@cogitator-ai/types';
1
+ import { INTERNAL_ERROR, describeHttpError } from '@cogitator-ai/server-shared';
2
+ import { sendError } from '../utils/errors.js';
2
3
  export function createErrorHandler() {
3
4
  return async (ctx, next) => {
4
5
  try {
@@ -7,46 +8,25 @@ export function createErrorHandler() {
7
8
  catch (err) {
8
9
  if (ctx.headerSent)
9
10
  return;
10
- if (CogitatorError.isCogitatorError(err)) {
11
- ctx.status = err.statusCode ?? ERROR_STATUS_CODES[err.code] ?? 500;
12
- ctx.body = {
13
- error: {
14
- message: err.message,
15
- code: err.code,
16
- },
17
- };
11
+ const described = describeHttpError(err);
12
+ if (described) {
13
+ sendError(ctx, described);
18
14
  return;
19
15
  }
20
- const status = err.status;
21
- if (status && status >= 400 && status < 600) {
22
- ctx.status = status;
23
- if (status >= 500) {
24
- console.error('[CogitatorKoa] Unhandled error:', err);
25
- ctx.body = {
26
- error: {
27
- message: 'Internal server error',
28
- code: ErrorCode.INTERNAL_ERROR,
29
- },
30
- };
31
- }
32
- else {
33
- ctx.body = {
34
- error: {
35
- message: err instanceof Error ? err.message : 'HTTP error',
36
- code: status === 413 ? 'PAYLOAD_TOO_LARGE' : 'HTTP_ERROR',
37
- },
38
- };
39
- }
16
+ const status = err?.status;
17
+ if (status && status >= 400 && status < 500) {
18
+ sendError(ctx, {
19
+ status,
20
+ code: status === 413 ? 'PAYLOAD_TOO_LARGE' : 'HTTP_ERROR',
21
+ message: err instanceof Error ? err.message : 'HTTP error',
22
+ });
40
23
  return;
41
24
  }
42
25
  console.error('[CogitatorKoa] Unhandled error:', err);
43
- ctx.status = 500;
44
- ctx.body = {
45
- error: {
46
- message: 'Internal server error',
47
- code: ErrorCode.INTERNAL_ERROR,
48
- },
49
- };
26
+ sendError(ctx, {
27
+ ...INTERNAL_ERROR,
28
+ ...(status && status >= 500 && status < 600 && { status }),
29
+ });
50
30
  }
51
31
  };
52
32
  }
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEpF,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,EAAE,GAAY,EAAE,IAAU,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,CAAC,UAAU;gBAAE,OAAO;YAE3B,IAAI,cAAc,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;gBACzC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,UAAU,IAAI,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC;gBACnE,GAAG,CAAC,IAAI,GAAG;oBACT,KAAK,EAAE;wBACL,OAAO,EAAE,GAAG,CAAC,OAAO;wBACpB,IAAI,EAAE,GAAG,CAAC,IAAI;qBACf;iBACF,CAAC;gBACF,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAI,GAA2B,CAAC,MAAM,CAAC;YACnD,IAAI,MAAM,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC5C,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;gBACpB,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC;oBAClB,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;oBACtD,GAAG,CAAC,IAAI,GAAG;wBACT,KAAK,EAAE;4BACL,OAAO,EAAE,uBAAuB;4BAChC,IAAI,EAAE,SAAS,CAAC,cAAc;yBAC/B;qBACF,CAAC;gBACJ,CAAC;qBAAM,CAAC;oBACN,GAAG,CAAC,IAAI,GAAG;wBACT,KAAK,EAAE;4BACL,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY;4BAC1D,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,YAAY;yBAC1D;qBACF,CAAC;gBACJ,CAAC;gBACD,OAAO;YACT,CAAC;YAED,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;YAEtD,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG;gBACT,KAAK,EAAE;oBACL,OAAO,EAAE,uBAAuB;oBAChC,IAAI,EAAE,SAAS,CAAC,cAAc;iBAC/B;aACF,CAAC;QACJ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"error-handler.js","sourceRoot":"","sources":["../../src/middleware/error-handler.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,UAAU,kBAAkB;IAChC,OAAO,KAAK,EAAE,GAAY,EAAE,IAAU,EAAE,EAAE;QACxC,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,CAAC,UAAU;gBAAE,OAAO;YAE3B,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC;YACzC,IAAI,SAAS,EAAE,CAAC;gBACd,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC1B,OAAO;YACT,CAAC;YAED,MAAM,MAAM,GAAI,GAAkC,EAAE,MAAM,CAAC;YAC3D,IAAI,MAAM,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC5C,SAAS,CAAC,GAAG,EAAE;oBACb,MAAM;oBACN,IAAI,EAAE,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,YAAY;oBACzD,OAAO,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY;iBAC3D,CAAC,CAAC;gBACH,OAAO;YACT,CAAC;YAED,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,GAAG,CAAC,CAAC;YACtD,SAAS,CAAC,GAAG,EAAE;gBACb,GAAG,cAAc;gBACjB,GAAG,CAAC,MAAM,IAAI,MAAM,IAAI,GAAG,IAAI,MAAM,GAAG,GAAG,IAAI,EAAE,MAAM,EAAE,CAAC;aAC3D,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { createContextMiddleware } from './context.js';
2
- export { createAuthMiddleware } from './auth.js';
2
+ export { createAnonymousMiddleware, createAuthMiddleware } from './auth.js';
3
3
  export { createBodyParser } from './body-parser.js';
4
4
  export { createErrorHandler } from './error-handler.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -1,5 +1,5 @@
1
1
  export { createContextMiddleware } from './context.js';
2
- export { createAuthMiddleware } from './auth.js';
2
+ export { createAnonymousMiddleware, createAuthMiddleware } from './auth.js';
3
3
  export { createBodyParser } from './body-parser.js';
4
4
  export { createErrorHandler } from './error-handler.js';
5
5
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,yBAAyB,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type Router from '@koa/router';
2
+ import type { Middleware } from 'koa';
3
+ import type { CogitatorState } from '../types.js';
4
+ /**
5
+ * `middleware`, for the requests a route of the router serves. Any other request passes
6
+ * through untouched, so the middleware downstream of the router gets it as it came.
7
+ */
8
+ export declare function forRoutes(middleware: Middleware): Middleware;
9
+ /**
10
+ * Registers the last route of the router, the one for the requests no other route serves. It
11
+ * lets the middleware downstream of the router answer them first. What is left unanswered goes
12
+ * through `guards`, the authentication and anonymous policy of the API, and is answered with
13
+ * `404 NOT_FOUND` in the error envelope, so under `anonymous: 'deny'` an anonymous caller gets
14
+ * `401` on every path and cannot tell which paths exist. An `OPTIONS` request for a path a route
15
+ * serves answers `204` with the methods it serves in `Allow`, which `router.allowedMethods()`
16
+ * would answer for every path once this route matches them all, so the app needs no
17
+ * `allowedMethods()`.
18
+ */
19
+ export declare function routeUnmatched(router: Router<CogitatorState>, guards: readonly Middleware[]): void;
20
+ //# sourceMappingURL=unmatched.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmatched.d.ts","sourceRoot":"","sources":["../../src/middleware/unmatched.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAEtC,OAAO,KAAK,EAAW,UAAU,EAAQ,MAAM,KAAK,CAAC;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAqBlD;;;GAGG;AACH,wBAAgB,SAAS,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAE5D;AA8BD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,CAAC,cAAc,CAAC,EAC9B,MAAM,EAAE,SAAS,UAAU,EAAE,GAC5B,IAAI,CAqBN"}
@@ -0,0 +1,83 @@
1
+ import { notFound } from '../utils/errors.js';
2
+ /** The name of the route that answers what no other route of the router serves */
3
+ const UNMATCHED_ROUTE = 'cogitator:unmatched';
4
+ /**
5
+ * A path that matches every request, in the syntax of each path-to-regexp major `@koa/router`
6
+ * has shipped with: 8 first, then 6. A router compiles a path when it is registered and throws
7
+ * on the syntax of the other one.
8
+ */
9
+ const ANY_PATH = ['{/*rest}', '(.*)'];
10
+ /** Whether a route of the router other than the unmatched one serves the request */
11
+ function servedByRoute(ctx) {
12
+ const matched = ctx.matched ?? [];
13
+ return matched.some((layer) => layer.name !== UNMATCHED_ROUTE && layer.methods.includes(ctx.method));
14
+ }
15
+ /**
16
+ * `middleware`, for the requests a route of the router serves. Any other request passes
17
+ * through untouched, so the middleware downstream of the router gets it as it came.
18
+ */
19
+ export function forRoutes(middleware) {
20
+ return (ctx, next) => (servedByRoute(ctx) ? middleware(ctx, next) : next());
21
+ }
22
+ /**
23
+ * The methods the routes of the router serve on the request's path, besides the unmatched one,
24
+ * with `HEAD` wherever `GET` is served and `OPTIONS`; none when no route serves the path
25
+ */
26
+ function allowedMethods(ctx) {
27
+ const matched = ctx.matched ?? [];
28
+ const methods = new Set(matched.filter((layer) => layer.name !== UNMATCHED_ROUTE).flatMap((layer) => layer.methods));
29
+ if (methods.size === 0)
30
+ return [];
31
+ if (methods.has('GET'))
32
+ methods.add('HEAD');
33
+ methods.add('OPTIONS');
34
+ return [...methods].sort();
35
+ }
36
+ function unanswered(ctx) {
37
+ return ctx.respond !== false && !ctx.headerSent && ctx.status === 404 && ctx.body == null;
38
+ }
39
+ async function runChain(chain, ctx, last) {
40
+ const dispatch = async (at) => {
41
+ const middleware = chain[at];
42
+ if (middleware)
43
+ await middleware(ctx, () => dispatch(at + 1));
44
+ else
45
+ last();
46
+ };
47
+ await dispatch(0);
48
+ }
49
+ /**
50
+ * Registers the last route of the router, the one for the requests no other route serves. It
51
+ * lets the middleware downstream of the router answer them first. What is left unanswered goes
52
+ * through `guards`, the authentication and anonymous policy of the API, and is answered with
53
+ * `404 NOT_FOUND` in the error envelope, so under `anonymous: 'deny'` an anonymous caller gets
54
+ * `401` on every path and cannot tell which paths exist. An `OPTIONS` request for a path a route
55
+ * serves answers `204` with the methods it serves in `Allow`, which `router.allowedMethods()`
56
+ * would answer for every path once this route matches them all, so the app needs no
57
+ * `allowedMethods()`.
58
+ */
59
+ export function routeUnmatched(router, guards) {
60
+ const answer = async (ctx, next) => {
61
+ const allowed = ctx.method === 'OPTIONS' ? allowedMethods(ctx) : [];
62
+ if (allowed.length > 0) {
63
+ ctx.set('Allow', allowed.join(', '));
64
+ ctx.status = 204;
65
+ return;
66
+ }
67
+ await next();
68
+ if (!unanswered(ctx))
69
+ return;
70
+ await runChain(guards, ctx, () => notFound(ctx, 'Not found'));
71
+ };
72
+ for (const [at, path] of ANY_PATH.entries()) {
73
+ try {
74
+ router.all(UNMATCHED_ROUTE, path, answer);
75
+ return;
76
+ }
77
+ catch (error) {
78
+ if (at === ANY_PATH.length - 1)
79
+ throw error;
80
+ }
81
+ }
82
+ }
83
+ //# sourceMappingURL=unmatched.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unmatched.js","sourceRoot":"","sources":["../../src/middleware/unmatched.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,aAAa,CAAC;AAYrE,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,cAAc,CAAC,CAsJ1D"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAGjC,OAAO,KAAK,EAAE,cAAc,EAAqB,MAAM,aAAa,CAAC;AAkDrE,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,cAAc,CAAC,CA6K1D"}
@@ -2,8 +2,33 @@ import Router from '@koa/router';
2
2
  import { KoaStreamWriter, setupSSEHeaders } from '../streaming/index.js';
3
3
  import { AgentStreamSession, parseResumeRequest, parseRunRequest, toAgentRunResponse, } from '@cogitator-ai/server-shared';
4
4
  import { getOwn } from '../utils/lookup.js';
5
- import { resolveError } from '../utils/errors.js';
6
- import { getRequestBody, onClientDisconnect } from '../utils/request.js';
5
+ import { describeError, invalidInput, notFound, respondWithError, sendError, } from '../utils/errors.js';
6
+ import { getRequestBody, onClientDisconnect, refuseThread, runDeps } from '../utils/request.js';
7
+ async function streamAgent(ctx, threadId, errorLabel, execute) {
8
+ setupSSEHeaders(ctx);
9
+ const writer = new KoaStreamWriter(ctx, {
10
+ heartbeatMs: ctx.state.cogitator.sseHeartbeatMs,
11
+ });
12
+ const session = new AgentStreamSession((event) => writer.send(event), { threadId });
13
+ const abortController = new AbortController();
14
+ onClientDisconnect(ctx, () => {
15
+ writer.close();
16
+ abortController.abort();
17
+ });
18
+ session.start();
19
+ try {
20
+ session.complete(await execute(session, abortController.signal));
21
+ }
22
+ catch (error) {
23
+ if (!abortController.signal.aborted) {
24
+ const failure = describeError(error, errorLabel);
25
+ session.fail(failure.message, failure.code);
26
+ }
27
+ }
28
+ finally {
29
+ writer.close();
30
+ }
31
+ }
7
32
  export function createAgentRoutes() {
8
33
  const router = new Router();
9
34
  router.get('/agents', (ctx) => {
@@ -21,24 +46,29 @@ export function createAgentRoutes() {
21
46
  const { name } = ctx.params;
22
47
  const agent = getOwn(agents, name);
23
48
  if (!agent) {
24
- ctx.status = 404;
25
- ctx.body = { error: { message: `Agent '${name}' not found`, code: 'NOT_FOUND' } };
49
+ notFound(ctx, `Agent '${name}' not found`);
26
50
  return;
27
51
  }
28
52
  const parsed = parseRunRequest(getRequestBody(ctx), {
29
53
  acceptContext: ctx.state.cogitator.acceptContext,
30
54
  });
31
55
  if (!parsed.ok) {
32
- ctx.status = 400;
33
- ctx.body = { error: { message: parsed.message, code: 'INVALID_INPUT' } };
56
+ invalidInput(ctx, parsed.message);
34
57
  return;
35
58
  }
59
+ const refusal = await refuseThread(ctx, parsed.value.threadId);
60
+ if (refusal) {
61
+ sendError(ctx, refusal);
62
+ return;
63
+ }
64
+ const deps = await runDeps(ctx);
36
65
  const abortController = new AbortController();
37
66
  onClientDisconnect(ctx, () => abortController.abort());
38
67
  try {
39
68
  const result = await runtime.run(agent, {
40
69
  ...parsed.value,
41
70
  userId: ctx.state.auth?.userId,
71
+ ...deps,
42
72
  signal: abortController.signal,
43
73
  });
44
74
  ctx.body = toAgentRunResponse(result);
@@ -46,9 +76,7 @@ export function createAgentRoutes() {
46
76
  catch (error) {
47
77
  if (abortController.signal.aborted)
48
78
  return;
49
- const { status, body } = resolveError(error, 'Agent run error');
50
- ctx.status = status;
51
- ctx.body = body;
79
+ respondWithError(ctx, error, 'Agent run error');
52
80
  }
53
81
  });
54
82
  router.post('/agents/:name/resume', async (ctx) => {
@@ -56,22 +84,27 @@ export function createAgentRoutes() {
56
84
  const { name } = ctx.params;
57
85
  const agent = getOwn(agents, name);
58
86
  if (!agent) {
59
- ctx.status = 404;
60
- ctx.body = { error: { message: `Agent '${name}' not found`, code: 'NOT_FOUND' } };
87
+ notFound(ctx, `Agent '${name}' not found`);
61
88
  return;
62
89
  }
63
90
  const parsed = parseResumeRequest(getRequestBody(ctx));
64
91
  if (!parsed.ok) {
65
- ctx.status = 400;
66
- ctx.body = { error: { message: parsed.message, code: 'INVALID_INPUT' } };
92
+ invalidInput(ctx, parsed.message);
67
93
  return;
68
94
  }
69
95
  const { threadId, decisions, defaultDecision } = parsed.value;
96
+ const refusal = await refuseThread(ctx, threadId);
97
+ if (refusal) {
98
+ sendError(ctx, refusal);
99
+ return;
100
+ }
101
+ const deps = await runDeps(ctx);
70
102
  const abortController = new AbortController();
71
103
  onClientDisconnect(ctx, () => abortController.abort());
72
104
  try {
73
105
  const result = await runtime.resume(agent, threadId, {
74
106
  userId: ctx.state.auth?.userId,
107
+ ...deps,
75
108
  decisions,
76
109
  defaultDecision,
77
110
  signal: abortController.signal,
@@ -81,9 +114,7 @@ export function createAgentRoutes() {
81
114
  catch (error) {
82
115
  if (abortController.signal.aborted)
83
116
  return;
84
- const { status, body } = resolveError(error, 'Agent resume error');
85
- ctx.status = status;
86
- ctx.body = body;
117
+ respondWithError(ctx, error, 'Agent resume error');
87
118
  }
88
119
  });
89
120
  router.post('/agents/:name/stream', async (ctx) => {
@@ -91,51 +122,61 @@ export function createAgentRoutes() {
91
122
  const { name } = ctx.params;
92
123
  const agent = getOwn(agents, name);
93
124
  if (!agent) {
94
- ctx.status = 404;
95
- ctx.body = { error: { message: `Agent '${name}' not found`, code: 'NOT_FOUND' } };
125
+ notFound(ctx, `Agent '${name}' not found`);
96
126
  return;
97
127
  }
98
128
  const parsed = parseRunRequest(getRequestBody(ctx), {
99
129
  acceptContext: ctx.state.cogitator.acceptContext,
100
130
  });
101
131
  if (!parsed.ok) {
102
- ctx.status = 400;
103
- ctx.body = { error: { message: parsed.message, code: 'INVALID_INPUT' } };
132
+ invalidInput(ctx, parsed.message);
104
133
  return;
105
134
  }
106
- setupSSEHeaders(ctx);
107
- const writer = new KoaStreamWriter(ctx, {
108
- heartbeatMs: ctx.state.cogitator.sseHeartbeatMs,
109
- });
110
- const session = new AgentStreamSession((event) => writer.send(event), {
111
- threadId: parsed.value.threadId,
112
- });
113
- const abortController = new AbortController();
114
- onClientDisconnect(ctx, () => {
115
- writer.close();
116
- abortController.abort();
117
- });
118
- session.start();
119
- try {
120
- const result = await runtime.run(agent, {
121
- ...parsed.value,
122
- threadId: session.threadId,
123
- userId: ctx.state.auth?.userId,
124
- stream: true,
125
- signal: abortController.signal,
126
- ...session.callbacks,
127
- });
128
- session.complete(result);
135
+ const refusal = await refuseThread(ctx, parsed.value.threadId);
136
+ if (refusal) {
137
+ sendError(ctx, refusal);
138
+ return;
129
139
  }
130
- catch (error) {
131
- if (!abortController.signal.aborted) {
132
- const { body } = resolveError(error, 'Agent stream error');
133
- session.fail(body.error.message, body.error.code);
134
- }
140
+ const deps = await runDeps(ctx);
141
+ await streamAgent(ctx, parsed.value.threadId, 'Agent stream error', (session, signal) => runtime.run(agent, {
142
+ ...parsed.value,
143
+ threadId: session.threadId,
144
+ userId: ctx.state.auth?.userId,
145
+ ...deps,
146
+ stream: true,
147
+ signal,
148
+ ...session.callbacks,
149
+ }));
150
+ });
151
+ router.post('/agents/:name/resume/stream', async (ctx) => {
152
+ const { agents, runtime } = ctx.state.cogitator;
153
+ const { name } = ctx.params;
154
+ const agent = getOwn(agents, name);
155
+ if (!agent) {
156
+ notFound(ctx, `Agent '${name}' not found`);
157
+ return;
158
+ }
159
+ const parsed = parseResumeRequest(getRequestBody(ctx));
160
+ if (!parsed.ok) {
161
+ invalidInput(ctx, parsed.message);
162
+ return;
135
163
  }
136
- finally {
137
- writer.close();
164
+ const { threadId, decisions, defaultDecision } = parsed.value;
165
+ const refusal = await refuseThread(ctx, threadId);
166
+ if (refusal) {
167
+ sendError(ctx, refusal);
168
+ return;
138
169
  }
170
+ const deps = await runDeps(ctx);
171
+ await streamAgent(ctx, threadId, 'Agent resume stream error', (session, signal) => runtime.resume(agent, threadId, {
172
+ userId: ctx.state.auth?.userId,
173
+ ...deps,
174
+ decisions,
175
+ defaultDecision,
176
+ stream: true,
177
+ signal,
178
+ ...session.callbacks,
179
+ }));
139
180
  });
140
181
  return router;
141
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzE,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,eAAe,EACf,kBAAkB,GACnB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAEzE,MAAM,UAAU,iBAAiB;IAC/B,MAAM,MAAM,GAAG,IAAI,MAAM,EAAkB,CAAC;IAE5C,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,GAAG,EAAE,EAAE;QAC5B,MAAM,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;QACvC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/D,IAAI;YACJ,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,WAAW;YACrC,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE;SACpD,CAAC,CAAC,CAAC;QAEJ,MAAM,QAAQ,GAAsB,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QAC1D,GAAG,CAAC,IAAI,GAAG,QAAQ,CAAC;IACtB,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC7C,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;QAChD,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAEnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,IAAI,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;YAClF,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;YAClD,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,aAAa;SACjD,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,CAAC;YACzE,OAAO;QACT,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;gBACtC,GAAG,MAAM,CAAC,KAAK;gBACf,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM;gBAC9B,MAAM,EAAE,eAAe,CAAC,MAAM;aAC/B,CAAC,CAAC;YAEH,GAAG,CAAC,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO;YAC3C,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC;YAChE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;YACpB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAChD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;QAChD,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAEnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,IAAI,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;YAClF,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,CAAC;YACzE,OAAO;QACT,CAAC;QACD,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,MAAM,CAAC,KAAK,CAAC;QAE9D,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAC9C,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC,CAAC;QAEvD,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE;gBACnD,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM;gBAC9B,SAAS;gBACT,eAAe;gBACf,MAAM,EAAE,eAAe,CAAC,MAAM;aAC/B,CAAC,CAAC;YAEH,GAAG,CAAC,IAAI,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QACxC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO;gBAAE,OAAO;YAC3C,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC;YACnE,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC;YACpB,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,sBAAsB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAChD,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC;QAChD,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;QAC5B,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QAEnC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,UAAU,IAAI,aAAa,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,CAAC;YAClF,OAAO;QACT,CAAC;QAED,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE;YAClD,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,aAAa;SACjD,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;YACf,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC;YACjB,GAAG,CAAC,IAAI,GAAG,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,CAAC;YACzE,OAAO;QACT,CAAC;QAED,eAAe,CAAC,GAAG,CAAC,CAAC;QACrB,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,GAAG,EAAE;YACtC,WAAW,EAAE,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,cAAc;SAChD,CAAC,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACpE,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ;SAChC,CAAC,CAAC;QACH,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAC;QAE9C,kBAAkB,CAAC,GAAG,EAAE,GAAG,EAAE;YAC3B,MAAM,CAAC,KAAK,EAAE,CAAC;YACf,eAAe,CAAC,KAAK,EAAE,CAAC;QAC1B,CAAC,CAAC,CAAC;QAEH,OAAO,CAAC,KAAK,EAAE,CAAC;QAEhB,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE;gBACtC,GAAG,MAAM,CAAC,KAAK;gBACf,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBAC1B,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM;gBAC9B,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,eAAe,CAAC,MAAM;gBAC9B,GAAG,OAAO,CAAC,SAAS;aACrB,CAAC,CAAC;YACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpC,MAAM,EAAE,IAAI,EAAE,GAAG,YAAY,CAAC,KAAK,EAAE,oBAAoB,CAAC,CAAC;gBAC3D,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACpD,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/routes/agents.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"swarms.d.ts","sourceRoot":"","sources":["../../src/routes/swarms.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,aAAa,CAAC;AAczF,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,cAAc,CAAC,CA6L1D"}
1
+ {"version":3,"file":"swarms.d.ts","sourceRoot":"","sources":["../../src/routes/swarms.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,EAAE,cAAc,EAAyC,MAAM,aAAa,CAAC;AAuBzF,wBAAgB,iBAAiB,IAAI,MAAM,CAAC,cAAc,CAAC,CAmM1D"}
@@ -1,9 +1,9 @@
1
1
  import Router from '@koa/router';
2
2
  import { KoaStreamWriter, setupSSEHeaders } from '../streaming/index.js';
3
- import { generateId, parseSwarmRunRequest, swarmAgentNames, withSwarm, } from '@cogitator-ai/server-shared';
3
+ import { generateId, parseSwarmRunRequest, swarmAgentNames, swarmTimeoutWithin, withSwarm, } from '@cogitator-ai/server-shared';
4
4
  import { getOwn } from '../utils/lookup.js';
5
- import { isModuleNotFoundError, resolveError } from '../utils/errors.js';
6
- import { getRequestBody, onClientDisconnect } from '../utils/request.js';
5
+ import { describeError, invalidInput, isModuleNotFoundError, notFound, notInstalled, respondWithError, sendError, } from '../utils/errors.js';
6
+ import { getRequestBody, onClientDisconnect, refuseThread } from '../utils/request.js';
7
7
  import { serializeSwarmUsage, toSwarmRunResponse } from '../utils/results.js';
8
8
  export function createSwarmRoutes() {
9
9
  const router = new Router();
@@ -25,18 +25,22 @@ export function createSwarmRoutes() {
25
25
  const { name } = ctx.params;
26
26
  const swarmConfig = getOwn(swarms, name);
27
27
  if (!swarmConfig) {
28
- ctx.status = 404;
29
- ctx.body = { error: { message: `Swarm '${name}' not found`, code: 'NOT_FOUND' } };
28
+ notFound(ctx, `Swarm '${name}' not found`);
30
29
  return;
31
30
  }
32
31
  const parsed = parseSwarmRunRequest(getRequestBody(ctx), {
33
32
  acceptContext: ctx.state.cogitator.acceptContext,
34
33
  });
35
34
  if (!parsed.ok) {
36
- ctx.status = 400;
37
- ctx.body = { error: { message: parsed.message, code: 'INVALID_INPUT' } };
35
+ invalidInput(ctx, parsed.message);
38
36
  return;
39
37
  }
38
+ const refusal = await refuseThread(ctx, parsed.value.threadId);
39
+ if (refusal) {
40
+ sendError(ctx, refusal);
41
+ return;
42
+ }
43
+ const timeout = swarmTimeoutWithin(parsed.value.timeout, ctx.state.cogitator.limits);
40
44
  let aborted = false;
41
45
  let abortSwarm;
42
46
  onClientDisconnect(ctx, () => {
@@ -49,7 +53,11 @@ export function createSwarmRoutes() {
49
53
  return;
50
54
  const response = await withSwarm(new Swarm(runtime, swarmConfig), async (swarm) => {
51
55
  abortSwarm = () => swarm.abort();
52
- const result = await swarm.run({ ...parsed.value, userId: ctx.state.auth?.userId });
56
+ const result = await swarm.run({
57
+ ...parsed.value,
58
+ timeout,
59
+ userId: ctx.state.auth?.userId,
60
+ });
53
61
  return toSwarmRunResponse(swarm, result);
54
62
  });
55
63
  if (aborted)
@@ -60,13 +68,10 @@ export function createSwarmRoutes() {
60
68
  if (aborted)
61
69
  return;
62
70
  if (isModuleNotFoundError(error)) {
63
- ctx.status = 501;
64
- ctx.body = { error: { message: 'Swarms package not installed', code: 'UNIMPLEMENTED' } };
71
+ notInstalled(ctx, 'Swarms');
65
72
  return;
66
73
  }
67
- const { status, body } = resolveError(error, 'Swarm run error');
68
- ctx.status = status;
69
- ctx.body = body;
74
+ respondWithError(ctx, error, 'Swarm run error');
70
75
  }
71
76
  });
72
77
  router.post('/swarms/:name/stream', async (ctx) => {
@@ -74,18 +79,22 @@ export function createSwarmRoutes() {
74
79
  const { name } = ctx.params;
75
80
  const swarmConfig = getOwn(swarms, name);
76
81
  if (!swarmConfig) {
77
- ctx.status = 404;
78
- ctx.body = { error: { message: `Swarm '${name}' not found`, code: 'NOT_FOUND' } };
82
+ notFound(ctx, `Swarm '${name}' not found`);
79
83
  return;
80
84
  }
81
85
  const parsed = parseSwarmRunRequest(getRequestBody(ctx), {
82
86
  acceptContext: ctx.state.cogitator.acceptContext,
83
87
  });
84
88
  if (!parsed.ok) {
85
- ctx.status = 400;
86
- ctx.body = { error: { message: parsed.message, code: 'INVALID_INPUT' } };
89
+ invalidInput(ctx, parsed.message);
90
+ return;
91
+ }
92
+ const refusal = await refuseThread(ctx, parsed.value.threadId);
93
+ if (refusal) {
94
+ sendError(ctx, refusal);
87
95
  return;
88
96
  }
97
+ const timeout = swarmTimeoutWithin(parsed.value.timeout, ctx.state.cogitator.limits);
89
98
  setupSSEHeaders(ctx);
90
99
  const writer = new KoaStreamWriter(ctx, {
91
100
  heartbeatMs: ctx.state.cogitator.sseHeartbeatMs,
@@ -107,6 +116,7 @@ export function createSwarmRoutes() {
107
116
  writer.start(messageId);
108
117
  const result = await swarm.run({
109
118
  ...parsed.value,
119
+ timeout,
110
120
  userId: ctx.state.auth?.userId,
111
121
  onAgentStart: (agentName) => {
112
122
  writer.swarmEvent('agent_start', { agentName, timestamp: Date.now() });
@@ -121,7 +131,7 @@ export function createSwarmRoutes() {
121
131
  onAgentError: (agentName, error) => {
122
132
  writer.swarmEvent('agent_error', {
123
133
  agentName,
124
- error: resolveError(error, `Swarm agent ${agentName} error`).body.error.message,
134
+ error: describeError(error, `Swarm agent ${agentName} error`).message,
125
135
  });
126
136
  },
127
137
  onMessage: (message) => {
@@ -148,8 +158,8 @@ export function createSwarmRoutes() {
148
158
  writer.error('Swarms package not installed', 'UNIMPLEMENTED');
149
159
  }
150
160
  else {
151
- const { body } = resolveError(error, 'Swarm stream error');
152
- writer.error(body.error.message, body.error.code);
161
+ const failure = describeError(error, 'Swarm stream error');
162
+ writer.error(failure.message, failure.code);
153
163
  }
154
164
  }
155
165
  finally {
@@ -161,15 +171,11 @@ export function createSwarmRoutes() {
161
171
  const { name } = ctx.params;
162
172
  const swarmConfig = getOwn(swarms, name);
163
173
  if (!swarmConfig) {
164
- ctx.status = 404;
165
- ctx.body = { error: { message: `Swarm '${name}' not found`, code: 'NOT_FOUND' } };
174
+ notFound(ctx, `Swarm '${name}' not found`);
166
175
  return;
167
176
  }
168
177
  if (!swarmConfig.blackboard?.enabled) {
169
- ctx.status = 400;
170
- ctx.body = {
171
- error: { message: 'Blackboard not enabled for this swarm', code: 'INVALID_INPUT' },
172
- };
178
+ invalidInput(ctx, 'Blackboard not enabled for this swarm');
173
179
  return;
174
180
  }
175
181
  const response = {