@jini-ai/http-kit 0.2.1

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 (227) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +107 -0
  3. package/dist/active-context.d.ts +99 -0
  4. package/dist/active-context.d.ts.map +1 -0
  5. package/dist/active-context.js +77 -0
  6. package/dist/active-context.js.map +1 -0
  7. package/dist/adapter.d.ts +36 -0
  8. package/dist/adapter.d.ts.map +1 -0
  9. package/dist/adapter.js +92 -0
  10. package/dist/adapter.js.map +1 -0
  11. package/dist/agents.d.ts +55 -0
  12. package/dist/agents.d.ts.map +1 -0
  13. package/dist/agents.js +23 -0
  14. package/dist/agents.js.map +1 -0
  15. package/dist/api-security-middleware.d.ts +146 -0
  16. package/dist/api-security-middleware.d.ts.map +1 -0
  17. package/dist/api-security-middleware.js +276 -0
  18. package/dist/api-security-middleware.js.map +1 -0
  19. package/dist/attachments.d.ts +317 -0
  20. package/dist/attachments.d.ts.map +1 -0
  21. package/dist/attachments.js +664 -0
  22. package/dist/attachments.js.map +1 -0
  23. package/dist/cancel-owned-runs.d.ts +44 -0
  24. package/dist/cancel-owned-runs.d.ts.map +1 -0
  25. package/dist/cancel-owned-runs.js +35 -0
  26. package/dist/cancel-owned-runs.js.map +1 -0
  27. package/dist/compat.d.ts +22 -0
  28. package/dist/compat.d.ts.map +1 -0
  29. package/dist/compat.js +17 -0
  30. package/dist/compat.js.map +1 -0
  31. package/dist/component-catalog.d.ts +74 -0
  32. package/dist/component-catalog.d.ts.map +1 -0
  33. package/dist/component-catalog.js +74 -0
  34. package/dist/component-catalog.js.map +1 -0
  35. package/dist/connectors.d.ts +165 -0
  36. package/dist/connectors.d.ts.map +1 -0
  37. package/dist/connectors.js +474 -0
  38. package/dist/connectors.js.map +1 -0
  39. package/dist/daemon-status.d.ts +72 -0
  40. package/dist/daemon-status.d.ts.map +1 -0
  41. package/dist/daemon-status.js +60 -0
  42. package/dist/daemon-status.js.map +1 -0
  43. package/dist/db-ops.d.ts +116 -0
  44. package/dist/db-ops.d.ts.map +1 -0
  45. package/dist/db-ops.js +191 -0
  46. package/dist/db-ops.js.map +1 -0
  47. package/dist/delegated-tools.d.ts +46 -0
  48. package/dist/delegated-tools.d.ts.map +1 -0
  49. package/dist/delegated-tools.js +155 -0
  50. package/dist/delegated-tools.js.map +1 -0
  51. package/dist/express/run-stream.d.ts +15 -0
  52. package/dist/express/run-stream.d.ts.map +1 -0
  53. package/dist/express/run-stream.js +11 -0
  54. package/dist/express/run-stream.js.map +1 -0
  55. package/dist/express-index.d.ts +32 -0
  56. package/dist/express-index.d.ts.map +1 -0
  57. package/dist/express-index.js +14 -0
  58. package/dist/express-index.js.map +1 -0
  59. package/dist/fastify/adapter.d.ts +31 -0
  60. package/dist/fastify/adapter.d.ts.map +1 -0
  61. package/dist/fastify/adapter.js +64 -0
  62. package/dist/fastify/adapter.js.map +1 -0
  63. package/dist/fastify/agents.d.ts +13 -0
  64. package/dist/fastify/agents.d.ts.map +1 -0
  65. package/dist/fastify/agents.js +7 -0
  66. package/dist/fastify/agents.js.map +1 -0
  67. package/dist/fastify/api-security-middleware.d.ts +64 -0
  68. package/dist/fastify/api-security-middleware.d.ts.map +1 -0
  69. package/dist/fastify/api-security-middleware.js +139 -0
  70. package/dist/fastify/api-security-middleware.js.map +1 -0
  71. package/dist/fastify/compat.d.ts +22 -0
  72. package/dist/fastify/compat.d.ts.map +1 -0
  73. package/dist/fastify/compat.js +16 -0
  74. package/dist/fastify/compat.js.map +1 -0
  75. package/dist/fastify/daemon-status.d.ts +22 -0
  76. package/dist/fastify/daemon-status.d.ts.map +1 -0
  77. package/dist/fastify/daemon-status.js +9 -0
  78. package/dist/fastify/daemon-status.js.map +1 -0
  79. package/dist/fastify/host-tools.d.ts +13 -0
  80. package/dist/fastify/host-tools.d.ts.map +1 -0
  81. package/dist/fastify/host-tools.js +8 -0
  82. package/dist/fastify/host-tools.js.map +1 -0
  83. package/dist/fastify/index.d.ts +36 -0
  84. package/dist/fastify/index.d.ts.map +1 -0
  85. package/dist/fastify/index.js +18 -0
  86. package/dist/fastify/index.js.map +1 -0
  87. package/dist/fastify/local-daemon-request.d.ts +43 -0
  88. package/dist/fastify/local-daemon-request.d.ts.map +1 -0
  89. package/dist/fastify/local-daemon-request.js +155 -0
  90. package/dist/fastify/local-daemon-request.js.map +1 -0
  91. package/dist/fastify/origin.d.ts +21 -0
  92. package/dist/fastify/origin.d.ts.map +1 -0
  93. package/dist/fastify/origin.js +14 -0
  94. package/dist/fastify/origin.js.map +1 -0
  95. package/dist/fastify/request.d.ts +20 -0
  96. package/dist/fastify/request.d.ts.map +1 -0
  97. package/dist/fastify/request.js +25 -0
  98. package/dist/fastify/request.js.map +1 -0
  99. package/dist/fastify/response.d.ts +20 -0
  100. package/dist/fastify/response.d.ts.map +1 -0
  101. package/dist/fastify/response.js +41 -0
  102. package/dist/fastify/response.js.map +1 -0
  103. package/dist/fastify/route-registration-guard.d.ts +70 -0
  104. package/dist/fastify/route-registration-guard.d.ts.map +1 -0
  105. package/dist/fastify/route-registration-guard.js +69 -0
  106. package/dist/fastify/route-registration-guard.js.map +1 -0
  107. package/dist/fastify/run-stream.d.ts +18 -0
  108. package/dist/fastify/run-stream.d.ts.map +1 -0
  109. package/dist/fastify/run-stream.js +10 -0
  110. package/dist/fastify/run-stream.js.map +1 -0
  111. package/dist/fastify/runs.d.ts +17 -0
  112. package/dist/fastify/runs.d.ts.map +1 -0
  113. package/dist/fastify/runs.js +33 -0
  114. package/dist/fastify/runs.js.map +1 -0
  115. package/dist/frontend-control.d.ts +131 -0
  116. package/dist/frontend-control.d.ts.map +1 -0
  117. package/dist/frontend-control.js +77 -0
  118. package/dist/frontend-control.js.map +1 -0
  119. package/dist/frontend-sessions.d.ts +100 -0
  120. package/dist/frontend-sessions.d.ts.map +1 -0
  121. package/dist/frontend-sessions.js +180 -0
  122. package/dist/frontend-sessions.js.map +1 -0
  123. package/dist/health.d.ts +91 -0
  124. package/dist/health.d.ts.map +1 -0
  125. package/dist/health.js +53 -0
  126. package/dist/health.js.map +1 -0
  127. package/dist/host-tools.d.ts +174 -0
  128. package/dist/host-tools.d.ts.map +1 -0
  129. package/dist/host-tools.js +338 -0
  130. package/dist/host-tools.js.map +1 -0
  131. package/dist/index.d.ts +86 -0
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +40 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/local-daemon-request.d.ts +44 -0
  136. package/dist/local-daemon-request.d.ts.map +1 -0
  137. package/dist/local-daemon-request.js +145 -0
  138. package/dist/local-daemon-request.js.map +1 -0
  139. package/dist/media.d.ts +131 -0
  140. package/dist/media.d.ts.map +1 -0
  141. package/dist/media.js +352 -0
  142. package/dist/media.js.map +1 -0
  143. package/dist/memory.d.ts +230 -0
  144. package/dist/memory.d.ts.map +1 -0
  145. package/dist/memory.js +331 -0
  146. package/dist/memory.js.map +1 -0
  147. package/dist/model-proxy.d.ts +45 -0
  148. package/dist/model-proxy.d.ts.map +1 -0
  149. package/dist/model-proxy.js +619 -0
  150. package/dist/model-proxy.js.map +1 -0
  151. package/dist/origin-validation.d.ts +64 -0
  152. package/dist/origin-validation.d.ts.map +1 -0
  153. package/dist/origin-validation.js +226 -0
  154. package/dist/origin-validation.js.map +1 -0
  155. package/dist/origin.d.ts +32 -0
  156. package/dist/origin.d.ts.map +1 -0
  157. package/dist/origin.js +14 -0
  158. package/dist/origin.js.map +1 -0
  159. package/dist/pack-http.d.ts +30 -0
  160. package/dist/pack-http.d.ts.map +1 -0
  161. package/dist/pack-http.js +12 -0
  162. package/dist/pack-http.js.map +1 -0
  163. package/dist/raw-sse.d.ts +44 -0
  164. package/dist/raw-sse.d.ts.map +1 -0
  165. package/dist/raw-sse.js +93 -0
  166. package/dist/raw-sse.js.map +1 -0
  167. package/dist/remote-run-events.d.ts +66 -0
  168. package/dist/remote-run-events.d.ts.map +1 -0
  169. package/dist/remote-run-events.js +230 -0
  170. package/dist/remote-run-events.js.map +1 -0
  171. package/dist/request.d.ts +19 -0
  172. package/dist/request.d.ts.map +1 -0
  173. package/dist/request.js +25 -0
  174. package/dist/request.js.map +1 -0
  175. package/dist/research.d.ts +42 -0
  176. package/dist/research.d.ts.map +1 -0
  177. package/dist/research.js +316 -0
  178. package/dist/research.js.map +1 -0
  179. package/dist/response.d.ts +14 -0
  180. package/dist/response.d.ts.map +1 -0
  181. package/dist/response.js +45 -0
  182. package/dist/response.js.map +1 -0
  183. package/dist/route-manifest.d.ts +31 -0
  184. package/dist/route-manifest.d.ts.map +1 -0
  185. package/dist/route-manifest.js +103 -0
  186. package/dist/route-manifest.js.map +1 -0
  187. package/dist/route-registration-guard.d.ts +65 -0
  188. package/dist/route-registration-guard.d.ts.map +1 -0
  189. package/dist/route-registration-guard.js +71 -0
  190. package/dist/route-registration-guard.js.map +1 -0
  191. package/dist/routines.d.ts +114 -0
  192. package/dist/routines.d.ts.map +1 -0
  193. package/dist/routines.js +320 -0
  194. package/dist/routines.js.map +1 -0
  195. package/dist/run-stream.d.ts +60 -0
  196. package/dist/run-stream.d.ts.map +1 -0
  197. package/dist/run-stream.js +108 -0
  198. package/dist/run-stream.js.map +1 -0
  199. package/dist/runs.d.ts +79 -0
  200. package/dist/runs.d.ts.map +1 -0
  201. package/dist/runs.js +232 -0
  202. package/dist/runs.js.map +1 -0
  203. package/dist/sse.d.ts +139 -0
  204. package/dist/sse.d.ts.map +1 -0
  205. package/dist/sse.js +157 -0
  206. package/dist/sse.js.map +1 -0
  207. package/dist/terminals.d.ts +125 -0
  208. package/dist/terminals.d.ts.map +1 -0
  209. package/dist/terminals.js +349 -0
  210. package/dist/terminals.js.map +1 -0
  211. package/dist/tool-catalog.d.ts +68 -0
  212. package/dist/tool-catalog.d.ts.map +1 -0
  213. package/dist/tool-catalog.js +68 -0
  214. package/dist/tool-catalog.js.map +1 -0
  215. package/dist/types.d.ts +58 -0
  216. package/dist/types.d.ts.map +1 -0
  217. package/dist/types.js +5 -0
  218. package/dist/types.js.map +1 -0
  219. package/dist/workspace-root.d.ts +69 -0
  220. package/dist/workspace-root.d.ts.map +1 -0
  221. package/dist/workspace-root.js +63 -0
  222. package/dist/workspace-root.js.map +1 -0
  223. package/dist/xai.d.ts +86 -0
  224. package/dist/xai.d.ts.map +1 -0
  225. package/dist/xai.js +668 -0
  226. package/dist/xai.js.map +1 -0
  227. package/package.json +58 -0
@@ -0,0 +1,66 @@
1
+ import type { Express, NextFunction, Request, Response } from 'express';
2
+ import type { RemoteToolEventRecorder, RunLifecycle } from '@jini-ai/daemon';
3
+ import { type AdapterContext } from './adapter.js';
4
+ export interface RemoteToolBridgeTokenConfig {
5
+ /** Env var name for the required shared secret. Defaults to `JINI_REMOTE_TOOL_BRIDGE_TOKEN`. */
6
+ readonly tokenEnvVar?: string;
7
+ }
8
+ export interface RemoteRunEventInternalErrorContext {
9
+ readonly source: 'tool-use' | 'tool-result';
10
+ readonly correlationId: string;
11
+ readonly error: unknown;
12
+ }
13
+ export interface RemoteRunEventHttpDeps {
14
+ /** Used to distinguish "unknown run" (404) from other outcomes before recording — same precedent as `delegated-tools.ts`'s `delegatedToolExecuteRoute` — and again afterwards to classify a recorder failure (see {@link toRemoteEventError}). */
15
+ readonly lifecycle: RunLifecycle;
16
+ readonly recorder: RemoteToolEventRecorder;
17
+ readonly tokenConfig?: RemoteToolBridgeTokenConfig;
18
+ /** Defaults to `process.env`. Threaded through so tests never have to mutate real process env. */
19
+ readonly env?: NodeJS.ProcessEnv;
20
+ /** Host-owned sink for the real exception behind a generic `INTERNAL_ERROR` response (SEC-005). Defaults to `console.error`. */
21
+ readonly onInternalError?: (context: RemoteRunEventInternalErrorContext) => void;
22
+ }
23
+ /**
24
+ * Gates both remote-run-event routes behind a dedicated shared secret. Unlike
25
+ * `registerApiBearerAuthMiddleware`, there is no "unset means open" or "disabled" mode: a route
26
+ * that lets a caller inject events into someone else's run must never silently run unauthenticated
27
+ * just because a host forgot to configure the token. If the token isn't configured, the route
28
+ * fails closed with 503, not 200.
29
+ *
30
+ * Kept as its own middleware rather than folded into `api-security-middleware.ts`'s
31
+ * `requireStrictBearerToken` (which has the identical fail-closed/no-loopback-exemption posture)
32
+ * because these two routes own their own error codes — `REMOTE_TOOL_BRIDGE_*`, distinct from the
33
+ * general API token's, so an operator can tell which secret is missing from the response alone.
34
+ * The *decision* logic is shared: header parsing and the constant-time comparison both come from
35
+ * that module, so the two gates cannot drift on what counts as a well-formed header or on
36
+ * comparison safety.
37
+ */
38
+ export declare function requireRemoteToolBridgeToken(deps: {
39
+ readonly tokenConfig?: RemoteToolBridgeTokenConfig;
40
+ readonly env?: NodeJS.ProcessEnv;
41
+ }): (req: Request, res: Response, next: NextFunction) => void;
42
+ export interface RemoteToolUseRequest {
43
+ readonly runId: string;
44
+ readonly toolUseId: string;
45
+ readonly toolId: string;
46
+ readonly input?: unknown;
47
+ }
48
+ export interface RemoteToolResultRequest {
49
+ readonly runId: string;
50
+ readonly toolUseId: string;
51
+ readonly content: string;
52
+ readonly isError?: boolean;
53
+ }
54
+ export interface RemoteRunEventResponse {
55
+ readonly recorded: true;
56
+ }
57
+ export declare const remoteToolUseRoute: import("./types.js").JsonRouteSpec<RemoteToolUseRequest, RemoteRunEventResponse, RemoteRunEventHttpDeps>;
58
+ export declare const remoteToolResultRoute: import("./types.js").JsonRouteSpec<RemoteToolResultRequest, RemoteRunEventResponse, RemoteRunEventHttpDeps>;
59
+ /**
60
+ * Mounts both remote-run-event routes on `app`, gated by {@link requireRemoteToolBridgeToken}.
61
+ * Unlike `registerRunRoutes`/`registerDelegatedToolRoutes`, these routes do not use
62
+ * `requireSameOrigin` — they are not meant to be called from a browser at all, only from a host's
63
+ * own trusted backend process, so the dedicated bearer token above is the sole gate.
64
+ */
65
+ export declare function registerRemoteRunEventRoutes(app: Express, deps: RemoteRunEventHttpDeps, adapter: AdapterContext): void;
66
+ //# sourceMappingURL=remote-run-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-run-events.d.ts","sourceRoot":"","sources":["../src/remote-run-events.ts"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExE,OAAO,KAAK,EAAE,uBAAuB,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAmC,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAMpF,MAAM,WAAW,2BAA2B;IAC1C,gGAAgG;IAChG,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,kCAAkC;IACjD,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,aAAa,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC,kPAAkP;IAClP,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;IAC3C,QAAQ,CAAC,WAAW,CAAC,EAAE,2BAA2B,CAAC;IACnD,kGAAkG;IAClG,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACjC,gIAAgI;IAChI,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,kCAAkC,KAAK,IAAI,CAAC;CAClF;AASD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IACjD,QAAQ,CAAC,WAAW,CAAC,EAAE,2BAA2B,CAAC;IACnD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CAClC,IAGS,KAAK,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,KAAG,IAAI,CAe/D;AAmCD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,QAAQ,EAAE,IAAI,CAAC;CACzB;AAuFD,eAAO,MAAM,kBAAkB,0GAc7B,CAAC;AAEH,eAAO,MAAM,qBAAqB,6GAkBhC,CAAC;AAEH;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAMtH"}
@@ -0,0 +1,230 @@
1
+ /**
2
+ * @module remote-run-events
3
+ *
4
+ * `POST /api/runs/:runId/tool-use` and `POST /api/runs/:runId/tool-result` — lets a tool call that
5
+ * executed in a DIFFERENT OS process from the one holding this run's `RunLifecycle` report its
6
+ * outcome back into the run's own event log, so SSE subscribers (a chat UI, a reattaching client)
7
+ * see it exactly as they would a locally-executed delegated tool call.
8
+ *
9
+ * This exists to relax `@jini-ai/daemon`'s co-location requirement (`RunLifecycle`/`ToolExecutor`
10
+ * previously had to live in one process per run, because `DelegatedToolBridge.execute()` calls
11
+ * `lifecycle.emit()` in-process with no remote path — see this repo's own `tovu-learnings.md` §1a).
12
+ * It does not remove any authorization: whatever executed the tool remotely already ran it through
13
+ * its OWN `ToolExecutor`/`ToolPolicy` gate before calling here. This route only lets that outcome
14
+ * be recorded into a run it does not own the process of.
15
+ *
16
+ * **Deliberately a separate trust boundary from `registerApiBearerAuthMiddleware`'s general API
17
+ * token** (`api-security-middleware.ts`): that token may reach browser-adjacent callers depending
18
+ * on deployment. This route is meant only for a host's own trusted backend-to-backend delegation
19
+ * (e.g. a sidecar tool-execution process reporting back to the process that owns the run), so its
20
+ * token has no "disabled" escape hatch and does not exempt loopback peers the way the general
21
+ * bearer gate does — a loopback bind is exactly the shape a same-machine but lower-trust process
22
+ * would also present, and this route grants direct run-event-injection, a materially different
23
+ * capability from what the general API token was designed to gate.
24
+ */
25
+ import { randomUUID } from 'node:crypto';
26
+ import { createApiError, isTerminalRunState } from '@jini-ai/protocol';
27
+ import { defineJsonRoute, mountJsonRoute } from './adapter.js';
28
+ import { bearerTokenFromHeader, timingSafeTokenMatch } from './api-security-middleware.js';
29
+ import { validationError } from './request.js';
30
+ import { sendApiError, statusForError } from './response.js';
31
+ import { err, ok } from './types.js';
32
+ function defaultInternalErrorSink(context) {
33
+ // eslint-disable-next-line no-console
34
+ console.error(`[@jini-ai/http-kit] internal error (remote-run-events/${context.source}, correlationId=${context.correlationId})`, context.error);
35
+ }
36
+ const DEFAULT_TOKEN_ENV_VAR = 'JINI_REMOTE_TOOL_BRIDGE_TOKEN';
37
+ /**
38
+ * Gates both remote-run-event routes behind a dedicated shared secret. Unlike
39
+ * `registerApiBearerAuthMiddleware`, there is no "unset means open" or "disabled" mode: a route
40
+ * that lets a caller inject events into someone else's run must never silently run unauthenticated
41
+ * just because a host forgot to configure the token. If the token isn't configured, the route
42
+ * fails closed with 503, not 200.
43
+ *
44
+ * Kept as its own middleware rather than folded into `api-security-middleware.ts`'s
45
+ * `requireStrictBearerToken` (which has the identical fail-closed/no-loopback-exemption posture)
46
+ * because these two routes own their own error codes — `REMOTE_TOOL_BRIDGE_*`, distinct from the
47
+ * general API token's, so an operator can tell which secret is missing from the response alone.
48
+ * The *decision* logic is shared: header parsing and the constant-time comparison both come from
49
+ * that module, so the two gates cannot drift on what counts as a well-formed header or on
50
+ * comparison safety.
51
+ */
52
+ export function requireRemoteToolBridgeToken(deps) {
53
+ const tokenEnvVar = deps.tokenConfig?.tokenEnvVar ?? DEFAULT_TOKEN_ENV_VAR;
54
+ const env = deps.env ?? process.env;
55
+ return (req, res, next) => {
56
+ const token = env[tokenEnvVar];
57
+ if (!token) {
58
+ const error = createApiError('REMOTE_TOOL_BRIDGE_NOT_CONFIGURED', `${tokenEnvVar} is not set — remote run-event ingestion is disabled`);
59
+ sendApiError(res, statusForError(error), error);
60
+ return;
61
+ }
62
+ const presented = bearerTokenFromHeader(req.get('authorization'));
63
+ if (presented === null || !timingSafeTokenMatch(presented, token)) {
64
+ const error = createApiError('REMOTE_TOOL_BRIDGE_TOKEN_REQUIRED', `Authorization: Bearer <${tokenEnvVar}> required`);
65
+ sendApiError(res, statusForError(error), error);
66
+ return;
67
+ }
68
+ next();
69
+ };
70
+ }
71
+ function isRecord(value) {
72
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
73
+ }
74
+ function requireNonEmptyString(body, key) {
75
+ const value = body[key];
76
+ return typeof value === 'string' && value.trim().length > 0 ? value : undefined;
77
+ }
78
+ /**
79
+ * Like {@link requireNonEmptyString} but accepts `''`, for the one field where empty is a real
80
+ * value rather than a missing one: a tool's result `content`.
81
+ *
82
+ * The local path already produces empty content legitimately — `serializeDelegatedToolOutput`
83
+ * (`@jini-ai/daemon`'s `delegated-tool-bridge.ts`) maps a handler returning `undefined` to `''`,
84
+ * and `DelegatedToolBridge.execute()` emits that verbatim as the `tool_result` event's `content`.
85
+ * Rejecting `''` here would mean a tool that legitimately produces no output can be reported
86
+ * in-process but **not** remotely — a silent semantic divergence between the two paths that would
87
+ * surface as a 400 on the one tool call a host least expects to fail. Identifiers (`runId`,
88
+ * `toolUseId`, `toolId`) keep the non-empty check: for those, empty really is missing.
89
+ */
90
+ function requireString(body, key) {
91
+ const value = body[key];
92
+ return typeof value === 'string' ? value : undefined;
93
+ }
94
+ function parseRunId(input) {
95
+ const runId = input.params.runId;
96
+ return typeof runId === 'string' && runId.length > 0
97
+ ? ok(runId)
98
+ : err(validationError('runId must be a non-empty path parameter'));
99
+ }
100
+ function parseRemoteToolUse(input) {
101
+ const parsedRunId = parseRunId(input);
102
+ if (!parsedRunId.ok)
103
+ return parsedRunId;
104
+ if (!isRecord(input.body))
105
+ return err(validationError('body must be a JSON object'));
106
+ const toolUseId = requireNonEmptyString(input.body, 'toolUseId');
107
+ if (toolUseId === undefined) {
108
+ return err(validationError('toolUseId must be a non-empty string', [{ path: 'toolUseId', message: 'required non-empty string' }]));
109
+ }
110
+ const toolId = requireNonEmptyString(input.body, 'toolId');
111
+ if (toolId === undefined) {
112
+ return err(validationError('toolId must be a non-empty string', [{ path: 'toolId', message: 'required non-empty string' }]));
113
+ }
114
+ return ok({ runId: parsedRunId.value, toolUseId, toolId, input: input.body.input });
115
+ }
116
+ function parseRemoteToolResult(input) {
117
+ const parsedRunId = parseRunId(input);
118
+ if (!parsedRunId.ok)
119
+ return parsedRunId;
120
+ if (!isRecord(input.body))
121
+ return err(validationError('body must be a JSON object'));
122
+ const toolUseId = requireNonEmptyString(input.body, 'toolUseId');
123
+ if (toolUseId === undefined) {
124
+ return err(validationError('toolUseId must be a non-empty string', [{ path: 'toolUseId', message: 'required non-empty string' }]));
125
+ }
126
+ // Empty is allowed here, deliberately — see `requireString`'s own doc for the local-path parity
127
+ // this preserves. Only a non-string (or absent) `content` is a validation failure.
128
+ const content = requireString(input.body, 'content');
129
+ if (content === undefined) {
130
+ return err(validationError('content must be a string', [{ path: 'content', message: 'required string' }]));
131
+ }
132
+ const isError = input.body.isError;
133
+ if (isError !== undefined && typeof isError !== 'boolean') {
134
+ return err(validationError('isError must be a boolean when provided', [{ path: 'isError', message: 'boolean when provided' }]));
135
+ }
136
+ return ok({ runId: parsedRunId.value, toolUseId, content, ...(isError === undefined ? {} : { isError }) });
137
+ }
138
+ /**
139
+ * Records a caught `recordToolUse`/`recordToolResult` throw as the right HTTP outcome.
140
+ *
141
+ * `RunLifecycle.emit()` throws for two documented reasons (see `run-lifecycle.ts`'s own `emit`) —
142
+ * unknown `runId` and "already terminal" — but the recorder call it sits behind can also fail for
143
+ * reasons that are nobody's business but the operator's: an event-log write hitting a full disk, a
144
+ * SQLite error naming its own file path, a driver fault carrying a credential in its message.
145
+ *
146
+ * This previously reported *every* one of those as `CONFLICT` carrying `error.message` verbatim,
147
+ * which turned any such fault into an unauthenticated-adjacent information leak and mislabelled a
148
+ * server fault as a client-resolvable conflict. So the outcome is now decided by the run's actual
149
+ * state rather than by whatever the exception happened to say:
150
+ *
151
+ * - run is gone -> `NOT_FOUND` (it was there for the pre-check; it isn't now).
152
+ * - run is terminal -> `CONFLICT`, the one legitimate business conflict. The message is
153
+ * *constructed here* from data this module already owns rather than forwarded from the
154
+ * exception, so even a misattributed error cannot smuggle its text out through this branch.
155
+ * - anything else -> SEC-005: a generic `INTERNAL_ERROR` plus a correlation id, with the real
156
+ * error handed to the host's sink so it is diagnosable server-side but never echoed.
157
+ *
158
+ * Residual, deliberately accepted: a genuine storage fault that happens to coincide with an
159
+ * already-terminal run is reported as `CONFLICT` rather than `INTERNAL_ERROR`. Distinguishing those
160
+ * would require matching on `emit`'s message text, trading a rare wrong status code for a check
161
+ * that breaks silently the next time that string is reworded. Either way nothing leaks, which is
162
+ * the property that mattered.
163
+ */
164
+ async function toRemoteEventError(error, runId, deps, source) {
165
+ const status = await deps.lifecycle.get(runId).catch(() => undefined);
166
+ if (status === undefined) {
167
+ return createApiError('NOT_FOUND', `run "${runId}" was not found`);
168
+ }
169
+ if (isTerminalRunState(status.state)) {
170
+ return createApiError('CONFLICT', `run "${runId}" is already terminal — no further events can be recorded`, {
171
+ details: { runId },
172
+ });
173
+ }
174
+ const correlationId = randomUUID();
175
+ const sink = deps.onInternalError ?? defaultInternalErrorSink;
176
+ sink({ source, correlationId, error });
177
+ return createApiError('INTERNAL_ERROR', 'an internal error occurred', { requestId: correlationId });
178
+ }
179
+ export const remoteToolUseRoute = defineJsonRoute({
180
+ method: 'post',
181
+ path: '/api/runs/:runId/tool-use',
182
+ parse: parseRemoteToolUse,
183
+ handle: async (input, deps) => {
184
+ const run = await deps.lifecycle.get(input.runId);
185
+ if (run === undefined)
186
+ return err(createApiError('NOT_FOUND', `run "${input.runId}" was not found`));
187
+ try {
188
+ await deps.recorder.recordToolUse(input.runId, { toolUseId: input.toolUseId, toolId: input.toolId, input: input.input });
189
+ return ok({ recorded: true });
190
+ }
191
+ catch (error) {
192
+ return err(await toRemoteEventError(error, input.runId, deps, 'tool-use'));
193
+ }
194
+ },
195
+ });
196
+ export const remoteToolResultRoute = defineJsonRoute({
197
+ method: 'post',
198
+ path: '/api/runs/:runId/tool-result',
199
+ parse: parseRemoteToolResult,
200
+ handle: async (input, deps) => {
201
+ const run = await deps.lifecycle.get(input.runId);
202
+ if (run === undefined)
203
+ return err(createApiError('NOT_FOUND', `run "${input.runId}" was not found`));
204
+ try {
205
+ await deps.recorder.recordToolResult(input.runId, {
206
+ toolUseId: input.toolUseId,
207
+ content: input.content,
208
+ ...(input.isError === undefined ? {} : { isError: input.isError }),
209
+ });
210
+ return ok({ recorded: true });
211
+ }
212
+ catch (error) {
213
+ return err(await toRemoteEventError(error, input.runId, deps, 'tool-result'));
214
+ }
215
+ },
216
+ });
217
+ /**
218
+ * Mounts both remote-run-event routes on `app`, gated by {@link requireRemoteToolBridgeToken}.
219
+ * Unlike `registerRunRoutes`/`registerDelegatedToolRoutes`, these routes do not use
220
+ * `requireSameOrigin` — they are not meant to be called from a browser at all, only from a host's
221
+ * own trusted backend process, so the dedicated bearer token above is the sole gate.
222
+ */
223
+ export function registerRemoteRunEventRoutes(app, deps, adapter) {
224
+ const gate = requireRemoteToolBridgeToken(deps);
225
+ app.use('/api/runs/:runId/tool-use', gate);
226
+ app.use('/api/runs/:runId/tool-result', gate);
227
+ mountJsonRoute(app, remoteToolUseRoute, deps, adapter);
228
+ mountJsonRoute(app, remoteToolResultRoute, deps, adapter);
229
+ }
230
+ //# sourceMappingURL=remote-run-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remote-run-events.js","sourceRoot":"","sources":["../src/remote-run-events.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Request-side normalization: turns a raw Express `Request` into the framework-independent
3
+ * `RouteInputContext` the rest of the module operates on, and builds a standard `BAD_REQUEST`
4
+ * `ApiError` for validation failures.
5
+ */
6
+ import type { Request } from 'express';
7
+ import { type ApiError, type ApiValidationIssue } from '@jini-ai/protocol';
8
+ import type { RouteInputContext } from './types.js';
9
+ /**
10
+ * Extracts `body`/`query`/`params` from an Express `Request` into a `RouteInputContext`, so a
11
+ * route's `parse` function can be unit-tested without constructing a real `Request`.
12
+ */
13
+ export declare function rawInput(req: Request): RouteInputContext;
14
+ /**
15
+ * Builds a `BAD_REQUEST` `ApiError` for a failed parse. When `issues` is non-empty, attaches
16
+ * them as structured validation details so clients can render per-field errors.
17
+ */
18
+ export declare function validationError(message: string, issues?: Pick<ApiValidationIssue, 'path' | 'message'>[]): ApiError;
19
+ //# sourceMappingURL=request.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAkB,KAAK,QAAQ,EAAE,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,GAAG,iBAAiB,CAMxD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,MAAM,EACf,MAAM,GAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,GAAG,SAAS,CAAC,EAAO,GAC1D,QAAQ,CAOV"}
@@ -0,0 +1,25 @@
1
+ import { createApiError } from '@jini-ai/protocol';
2
+ /**
3
+ * Extracts `body`/`query`/`params` from an Express `Request` into a `RouteInputContext`, so a
4
+ * route's `parse` function can be unit-tested without constructing a real `Request`.
5
+ */
6
+ export function rawInput(req) {
7
+ return {
8
+ body: req.body,
9
+ query: (req.query ?? {}),
10
+ params: (req.params ?? {}),
11
+ };
12
+ }
13
+ /**
14
+ * Builds a `BAD_REQUEST` `ApiError` for a failed parse. When `issues` is non-empty, attaches
15
+ * them as structured validation details so clients can render per-field errors.
16
+ */
17
+ export function validationError(message, issues = []) {
18
+ if (issues.length === 0) {
19
+ return createApiError('BAD_REQUEST', message);
20
+ }
21
+ return createApiError('BAD_REQUEST', message, {
22
+ details: { kind: 'validation', issues },
23
+ });
24
+ }
25
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAA0C,MAAM,mBAAmB,CAAC;AAG3F;;;GAGG;AACH,MAAM,UAAU,QAAQ,CAAC,GAAY;IACnC,OAAO;QACL,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,KAAK,EAAE,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAA4B;QACnD,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,IAAI,EAAE,CAA2B;KACrD,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAe,EACf,SAAyD,EAAE;IAE3D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO,cAAc,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,cAAc,CAAC,aAAa,EAAE,OAAO,EAAE;QAC5C,OAAO,EAAE,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,EAAiD;KACvF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,42 @@
1
+ import type { Express } from 'express';
2
+ import { type AdapterContext } from './adapter.js';
3
+ export interface ResearchSource {
4
+ readonly title: string;
5
+ readonly url: string;
6
+ readonly snippet: string;
7
+ readonly publishedAt?: string;
8
+ readonly provider: string;
9
+ }
10
+ /** Matches OD's real `@open-design/contracts/api/research.ts` `ResearchFindings` shape — see module doc. */
11
+ export interface ResearchSearchResponse {
12
+ readonly query: string;
13
+ readonly summary: string;
14
+ readonly sources: readonly ResearchSource[];
15
+ readonly provider: string;
16
+ readonly depth: 'shallow';
17
+ readonly fetchedAt: number;
18
+ }
19
+ export interface ResearchInternalErrorContext {
20
+ readonly correlationId: string;
21
+ readonly error: unknown;
22
+ }
23
+ export interface ResearchProviderCredentials {
24
+ readonly apiKey?: string;
25
+ readonly baseUrl?: string;
26
+ readonly model?: string;
27
+ }
28
+ export interface ResearchHttpDeps {
29
+ /** Resolves per-provider credentials. Defaults to `TAVILY_API_KEY` for the built-in Tavily transport. */
30
+ readonly resolveCredentials?: (providerId: string) => Promise<ResearchProviderCredentials>;
31
+ /** Host-owned sink for the real exception behind a generic `INTERNAL_ERROR` response (SEC-005). Defaults to `console.error`. */
32
+ readonly onInternalError?: (context: ResearchInternalErrorContext) => void;
33
+ }
34
+ interface ResearchSearchRequest {
35
+ readonly query: string;
36
+ readonly maxSources?: number;
37
+ }
38
+ export declare const researchSearchRoute: import("./types.js").JsonRouteSpec<ResearchSearchRequest, ResearchSearchResponse, ResearchHttpDeps>;
39
+ /** Mounts `POST /api/research/search` on `app`. A pack's `http(app, services)` calls this directly. */
40
+ export declare function registerResearchRoutes(app: Express, deps: ResearchHttpDeps, adapter: AdapterContext): void;
41
+ export {};
42
+ //# sourceMappingURL=research.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"research.d.ts","sourceRoot":"","sources":["../src/research.ts"],"names":[],"mappings":"AAiGA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGvC,OAAO,EAAmC,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAapF,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,4GAA4G;AAC5G,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,cAAc,EAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,4BAA4B;IAC3C,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,yGAAyG;IACzG,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,2BAA2B,CAAC,CAAC;IAC3F,gIAAgI;IAChI,QAAQ,CAAC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE,4BAA4B,KAAK,IAAI,CAAC;CAC5E;AA8BD,UAAU,qBAAqB;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAkKD,eAAO,MAAM,mBAAmB,qGA6C9B,CAAC;AAEH,uGAAuG;AACvG,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI,CAE1G"}