@agenthippo-ai/agenthippo 0.1.1 → 1.110.9

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 (158) hide show
  1. package/out/agentpack/index.js +2 -1
  2. package/out/agentpack/paths.js +23 -0
  3. package/out/analytics/dashboard/fleetDisplayUtils.js +3 -0
  4. package/out/analytics/dashboard/provider.js +13 -2
  5. package/out/analytics/dashboard/webview/panel.js +293 -82
  6. package/out/analytics/identity.js +0 -0
  7. package/out/analytics/service.js +23 -25
  8. package/out/analytics/sessionEvents.js +109 -22
  9. package/out/auth/authBaseUrl.js +59 -0
  10. package/out/auth/bootstrapApply.js +9 -10
  11. package/out/auth/brokerClient.js +49 -15
  12. package/out/auth/onboardingPrompt.js +112 -0
  13. package/out/auth/provider.js +9 -22
  14. package/out/auth/sessionStore.js +3 -40
  15. package/out/auth/signInFlow.js +2 -1
  16. package/out/auth-broker/config.js +54 -4
  17. package/out/auth-broker/server.js +152 -20
  18. package/out/auth-broker/store.js +25 -24
  19. package/out/authority/warrant.js +206 -0
  20. package/out/branding.js +27 -1
  21. package/out/cli/commands/ask.js +3 -0
  22. package/out/cli/commands/chat.js +3 -0
  23. package/out/cli/commands/conformance.js +249 -0
  24. package/out/cli/commands/data-plane.js +67 -0
  25. package/out/cli/commands/deploy.js +50 -10
  26. package/out/cli/commands/gateway.js +58 -0
  27. package/out/cli/commands/idp.js +85 -0
  28. package/out/cli/commands/pack.js +182 -3
  29. package/out/cli/commands/sandbox.js +332 -0
  30. package/out/cli/commands/serve.js +1165 -82
  31. package/out/cli/commands/store.js +12 -4
  32. package/out/cli/credential-context.js +42 -80
  33. package/out/cli/data-plane/broker.js +78 -0
  34. package/out/cli/data-plane/connector-host.js +121 -0
  35. package/out/cli/data-plane/egress.js +189 -0
  36. package/out/cli/data-plane/gateway.js +218 -0
  37. package/out/cli/data-plane/index.js +152 -0
  38. package/out/cli/data-plane/registry.js +89 -0
  39. package/out/cli/data-plane/remote.js +106 -0
  40. package/out/cli/data-plane/sidecar.js +161 -0
  41. package/out/cli/deploy/blueprints/registry.js +84 -0
  42. package/out/cli/deploy/layers/identity.js +172 -0
  43. package/out/cli/deploy-init.js +1182 -347
  44. package/out/cli/deps.js +106 -2
  45. package/out/cli/idp-init.js +338 -0
  46. package/out/cli/index.js +192 -23
  47. package/out/cli/pack-install-ref.js +15 -0
  48. package/out/cli/pack-loader.js +45 -3
  49. package/out/cli/pack-runtime.js +61 -0
  50. package/out/cli/runtime.js +44 -3
  51. package/out/cli/serve-hardening.js +139 -3
  52. package/out/cli/state-sync.js +16 -15
  53. package/out/cli/utils.js +12 -1
  54. package/out/engines/base.js +32 -3
  55. package/out/engines/claude.js +47 -8
  56. package/out/engines/codex.js +125 -22
  57. package/out/engines/custom.js +54 -12
  58. package/out/evidence/sessionShip.js +610 -0
  59. package/out/extension.js +13 -2
  60. package/out/gateway/bridge.js +40 -2
  61. package/out/gateway/client.js +34 -1
  62. package/out/gateway/installSource.js +68 -0
  63. package/out/gateway/lifecycle.js +185 -51
  64. package/out/gateway/outboundPaths.js +4 -1
  65. package/out/gateway/outboundSend.js +6 -6
  66. package/out/gateway/secretsPreflight.js +90 -0
  67. package/out/gateway/service.js +7 -2
  68. package/out/headless/index.js +40 -9
  69. package/out/live-e2e/mcp-demo/server.mjs +32 -0
  70. package/out/live-probe/connector.mjs +8 -0
  71. package/out/live-probe/env-report.mjs +8 -0
  72. package/out/mcp.js +115 -67
  73. package/out/pack/caps.js +271 -0
  74. package/out/pack/governor.js +309 -0
  75. package/out/pack/lease-schema.js +270 -0
  76. package/out/pack/lease.js +640 -0
  77. package/out/pack/reconcile.js +217 -0
  78. package/out/pack/signature.js +839 -0
  79. package/out/remote/handleRemoteChat.js +44 -3
  80. package/out/remote/remoteTrust.js +277 -0
  81. package/out/remote/serveClient.js +52 -5
  82. package/out/runner/host.js +131 -0
  83. package/out/runner/protocol.js +108 -0
  84. package/out/runner/remoteEngine.js +130 -0
  85. package/out/runtime/interfaces/edit-tracker.js +2 -0
  86. package/out/runtime/vscode/vscode-edit-tracker.js +2 -0
  87. package/out/sandbox/containerDriver.js +570 -0
  88. package/out/sandbox/processDriver.js +315 -0
  89. package/out/sandbox/sandboxRunner.js +191 -0
  90. package/out/sandbox/support.js +301 -0
  91. package/out/sandbox/types.js +55 -0
  92. package/out/scripts/agentide-litellm.sh +67 -5
  93. package/out/scripts/agentide_litellm_hooks_v1.py +182 -21
  94. package/out/scripts/analytics/agentide-litellm-otel.config.yaml +94 -118
  95. package/out/scripts/analytics/setup-analytics.ps1 +37 -13
  96. package/out/scripts/analytics/setup-analytics.sh +93 -13
  97. package/out/scripts/analytics/start-analytics.sh +86 -14
  98. package/out/scripts/gateway/setup-gateway.sh +72 -32
  99. package/out/shared/models.js +12 -111
  100. package/out/shared/providers.js +2 -2
  101. package/out/spotlight/install.js +7 -1
  102. package/out/store/index.js +41 -10
  103. package/out/sync.js +3 -1
  104. package/out/test/analytics.test.js +198 -34
  105. package/out/test/auth-baseurl.test.js +132 -0
  106. package/out/test/auth-bootstrapApply.test.js +27 -6
  107. package/out/test/auth-broker-hardening.test.js +227 -0
  108. package/out/test/auth-broker.test.js +112 -9
  109. package/out/test/blueprint-registry.test.js +105 -0
  110. package/out/test/branding.test.js +13 -0
  111. package/out/test/caps-governor.test.js +455 -0
  112. package/out/test/codex-new-file-survival.test.js +119 -0
  113. package/out/test/configuration-scopes.test.js +131 -0
  114. package/out/test/credential-context.test.js +23 -28
  115. package/out/test/custom-env-layering.test.js +151 -0
  116. package/out/test/data-plane-engine-wiring.test.js +250 -0
  117. package/out/test/data-plane.test.js +311 -0
  118. package/out/test/deploy-init.test.js +295 -4
  119. package/out/test/egress-broker.test.js +172 -0
  120. package/out/test/fleetDisplayUtils.test.js +4 -0
  121. package/out/test/gateway-client.test.js +28 -0
  122. package/out/test/gateway-install-source.test.js +111 -0
  123. package/out/test/gateway-lifecycle.test.js +1 -1
  124. package/out/test/gateway-outbound.test.js +7 -0
  125. package/out/test/gateway-secrets-preflight.test.js +127 -0
  126. package/out/test/gateway-session-routing.test.js +18 -0
  127. package/out/test/governor-hardening.test.js +217 -0
  128. package/out/test/headless-sync.test.js +22 -0
  129. package/out/test/idp-init.test.js +151 -0
  130. package/out/test/lease-signature.test.js +357 -0
  131. package/out/test/lease-trust-root.test.js +273 -0
  132. package/out/test/lease-validation.test.js +169 -0
  133. package/out/test/lease.test.js +114 -0
  134. package/out/test/mcp-obo.test.js +52 -27
  135. package/out/test/mcp.test.js +69 -3
  136. package/out/test/model-routing.test.js +133 -0
  137. package/out/test/pack-admission.test.js +216 -0
  138. package/out/test/pack-digest-parity.test.js +140 -0
  139. package/out/test/pack-reconcile.test.js +183 -0
  140. package/out/test/pack-signature-artifact.test.js +103 -0
  141. package/out/test/pack-signature.test.js +176 -0
  142. package/out/test/pack-trust-root.test.js +202 -0
  143. package/out/test/remote-trust.test.js +242 -0
  144. package/out/test/runner-protocol.test.js +249 -0
  145. package/out/test/sandbox-driver.test.js +553 -0
  146. package/out/test/sandbox-hardening.test.js +395 -0
  147. package/out/test/sandbox-runner.test.js +343 -0
  148. package/out/test/serve-gateway-turn.test.js +267 -0
  149. package/out/test/serve-hardening.test.js +125 -6
  150. package/out/test/serveClient.test.js +38 -5
  151. package/out/test/sessionShip.test.js +560 -0
  152. package/out/test/spotlightPublish.test.js +28 -0
  153. package/out/test/state-sync-peer.test.js +18 -5
  154. package/out/test/store-install-safe.test.js +69 -0
  155. package/out/test/tls-bypass.test.js +90 -0
  156. package/out/test/warrant.test.js +138 -0
  157. package/out/utils.js +73 -20
  158. package/package.json +1 -1
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) AgentHippo.ai. All rights reserved.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.TURN_NONCE_HEADER = exports.TURN_NONCE_ENV = exports.DATA_MCP_URL_ENV = void 0;
40
+ exports.startDataGateway = startDataGateway;
41
+ /**
42
+ * Data-plane MCP gateway — a streamable-HTTP MCP server inside the serve (broker) process,
43
+ * bound to 127.0.0.1 (secure-obo-design §3.1/§3.2).
44
+ *
45
+ * Every request must present `Authorization: Bearer <turn nonce>`; the nonce resolves in
46
+ * the TurnRegistry to a verified {userId, sessionKey}. The credential is then selected
47
+ * SERVER-SIDE from the broker at call time (§5 step 7) — no parameter in the MCP request
48
+ * can name a user or a credential. Unknown/expired nonce ⇒ 401, fail closed.
49
+ *
50
+ * What nonce theft buys is documented in §3.3: loopback-only, turn-scoped, tool-surface-
51
+ * only access as the user the model was already acting for.
52
+ */
53
+ const http = __importStar(require("http"));
54
+ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
55
+ const streamableHttp_js_1 = require("@modelcontextprotocol/sdk/server/streamableHttp.js");
56
+ const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
57
+ const serve_hardening_1 = require("../serve-hardening");
58
+ const registry_1 = require("./registry");
59
+ /** Loopback URL of the data-plane gateway; set per turn into the engine child env. */
60
+ exports.DATA_MCP_URL_ENV = 'AGENTHIPPO_DATA_MCP_URL';
61
+ /** The turn capability. Per-spawn env only; never written to any shared file. */
62
+ exports.TURN_NONCE_ENV = 'AGENTHIPPO_TURN_NONCE';
63
+ /**
64
+ * Request header carrying an EDGE-registered turn nonce (P1 fix, obo-inprogress §2). The
65
+ * trusted edge (IdP Caddy) registers the turn with the sidecar via forward_auth and copies
66
+ * this header onto the request it proxies to serve. Trusted exactly like the pinned identity
67
+ * header: only the in-network edge can reach serve, and the edge overwrites any client-
68
+ * supplied value. A forged value fails closed at the gateway (unknown nonce ⇒ 401).
69
+ */
70
+ exports.TURN_NONCE_HEADER = 'x-agent-turn-nonce';
71
+ function bearerToken(header) {
72
+ const m = header?.match(/^Bearer\s+(\S+)$/i);
73
+ return m?.[1];
74
+ }
75
+ /** sha256 prefix for audit lines — never log a raw nonce (secure-obo-design §11). */
76
+ function noncePrefix(nonce) {
77
+ return nonce ? (0, registry_1.hashNonce)(nonce).slice(0, 12) : 'none';
78
+ }
79
+ async function startDataGateway(opts) {
80
+ const server = http.createServer(async (req, res) => {
81
+ try {
82
+ const urlPath = req.url?.split('?')[0] ?? '';
83
+ // Release-by-nonce (edge-registration mode): serve holds the nonce but no control-API
84
+ // authority, so it ends the turn here. Auth is the nonce itself — releasing is the one
85
+ // operation safe to grant any nonce holder (fail-closed: it only kills the capability
86
+ // early; the registry deadline remains the backstop when this is never called).
87
+ if (urlPath === '/turn' && req.method === 'DELETE') {
88
+ const nonce = bearerToken(req.headers.authorization);
89
+ if (nonce) {
90
+ const released = opts.registry.releaseByNonce(nonce);
91
+ opts.audit?.('edge', 'turn_release', { released, nonce_sha256_prefix: noncePrefix(nonce) });
92
+ }
93
+ res.writeHead(204);
94
+ res.end();
95
+ return;
96
+ }
97
+ if (urlPath !== '/mcp') {
98
+ res.writeHead(404, { 'Content-Type': 'application/json' });
99
+ res.end(JSON.stringify({ error: 'not found' }));
100
+ return;
101
+ }
102
+ if (req.method !== 'POST') {
103
+ // Stateless transport: one POST per JSON-RPC exchange; no GET stream, no sessions.
104
+ res.writeHead(405, { 'Content-Type': 'application/json', 'Allow': 'POST' });
105
+ res.end(JSON.stringify({ error: 'method not allowed' }));
106
+ return;
107
+ }
108
+ const nonce = bearerToken(req.headers.authorization);
109
+ const turn = nonce ? opts.registry.resolve(nonce) : undefined;
110
+ if (!turn) {
111
+ // Expired / unknown / missing — a stale engine subprocess fails CLOSED here
112
+ // instead of ever using a stale user's credential (§3.2).
113
+ opts.audit?.('unauthorized', 'nonce_reject', {
114
+ reason: nonce ? 'unknown_or_expired' : 'missing',
115
+ nonce_sha256_prefix: noncePrefix(nonce),
116
+ });
117
+ res.writeHead(401, { 'Content-Type': 'application/json' });
118
+ res.end(JSON.stringify({
119
+ jsonrpc: '2.0',
120
+ error: { code: -32001, message: 'Unauthorized: missing, unknown, or expired turn nonce' },
121
+ id: null,
122
+ }));
123
+ return;
124
+ }
125
+ opts.logger?.info(`[DataPlane] authorized request user=${turn.userId} session=${turn.sessionKey}`);
126
+ let body;
127
+ try {
128
+ body = JSON.parse(await (0, serve_hardening_1.readBodyWithCap)(req));
129
+ }
130
+ catch {
131
+ res.writeHead(400, { 'Content-Type': 'application/json' });
132
+ res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32700, message: 'Parse error' }, id: null }));
133
+ return;
134
+ }
135
+ // Fresh stateless server+transport per request (SDK stateless pattern) so a request
136
+ // carries exactly its own turn's authority and nothing outlives the response.
137
+ const mcp = buildTurnScopedMcpServer(opts, turn);
138
+ const transport = new streamableHttp_js_1.StreamableHTTPServerTransport({ sessionIdGenerator: undefined, enableJsonResponse: true });
139
+ res.on('close', () => {
140
+ void transport.close();
141
+ void mcp.close();
142
+ });
143
+ await mcp.connect(transport);
144
+ await transport.handleRequest(req, res, body);
145
+ }
146
+ catch (err) {
147
+ opts.logger?.warn(`[DataPlane] gateway error: ${err instanceof Error ? err.message : String(err)}`);
148
+ if (!res.headersSent) {
149
+ res.writeHead(500, { 'Content-Type': 'application/json' });
150
+ }
151
+ res.end(JSON.stringify({ jsonrpc: '2.0', error: { code: -32603, message: 'Internal error' }, id: null }));
152
+ }
153
+ });
154
+ const bindHost = opts.host ?? '127.0.0.1';
155
+ await new Promise((resolve, reject) => {
156
+ server.once('error', reject);
157
+ // Default loopback; a sidecar binds 0.0.0.0 for the private pod network only.
158
+ server.listen(opts.port ?? 0, bindHost, () => resolve());
159
+ });
160
+ const address = server.address();
161
+ if (!address || typeof address === 'string') {
162
+ throw new Error('Data gateway failed to bind a port');
163
+ }
164
+ const url = `http://${opts.advertiseHost ?? '127.0.0.1'}:${address.port}/mcp`;
165
+ opts.logger?.info(`[DataPlane] gateway listening at ${url}`);
166
+ return {
167
+ url,
168
+ close: () => new Promise((resolve) => server.close(() => resolve())),
169
+ };
170
+ }
171
+ function buildTurnScopedMcpServer(opts, turn) {
172
+ const server = new index_js_1.Server({ name: 'agenthippo-data-gateway', version: '1.0.0' }, { capabilities: { tools: {} } });
173
+ server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({
174
+ tools: await opts.connector.listTools(),
175
+ }));
176
+ server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request) => {
177
+ const toolName = request.params.name;
178
+ // Server-side credential selection at call time (§5 step 7): the registry entry's
179
+ // verified userId picks the credential — never anything in the MCP request.
180
+ let secretRef;
181
+ try {
182
+ const credential = await opts.broker.acquire(turn.userId, turn.inboundToken);
183
+ secretRef = credential.secretRef;
184
+ opts.audit?.(turn.sessionKey, 'tool_authorize', { tool: toolName, userId: turn.userId, provider: opts.broker.provider.name });
185
+ }
186
+ catch (err) {
187
+ const message = err instanceof Error ? err.message : String(err);
188
+ opts.audit?.(turn.sessionKey, 'tool_deny', { tool: toolName, userId: turn.userId, reason: 'acquire_failed', error: message });
189
+ return {
190
+ content: [{ type: 'text', text: JSON.stringify({ ok: false, error: `credential acquire failed (fail-closed): ${message}` }) }],
191
+ isError: true,
192
+ };
193
+ }
194
+ try {
195
+ return await opts.connector.callTool(toolName, request.params.arguments ?? {}, { userId: turn.userId, credential: secretRef });
196
+ }
197
+ catch (err) {
198
+ // Mid-turn credential expiry: drop the cache, re-acquire once, retry once (§5).
199
+ const message = err instanceof Error ? err.message : String(err);
200
+ if (/401|unauthoriz|expired/i.test(message)) {
201
+ opts.broker.invalidate(turn.userId);
202
+ try {
203
+ const retryCred = await opts.broker.acquire(turn.userId, turn.inboundToken);
204
+ return await opts.connector.callTool(toolName, request.params.arguments ?? {}, { userId: turn.userId, credential: retryCred.secretRef });
205
+ }
206
+ catch {
207
+ // fall through to the original error below
208
+ }
209
+ }
210
+ opts.audit?.(turn.sessionKey, 'tool_deny', { tool: toolName, userId: turn.userId, reason: 'call_failed', error: message });
211
+ return {
212
+ content: [{ type: 'text', text: JSON.stringify({ ok: false, error: message }) }],
213
+ isError: true,
214
+ };
215
+ }
216
+ });
217
+ return server;
218
+ }
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) AgentHippo.ai. All rights reserved.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ var desc = Object.getOwnPropertyDescriptor(m, k);
8
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
9
+ desc = { enumerable: true, get: function() { return m[k]; } };
10
+ }
11
+ Object.defineProperty(o, k2, desc);
12
+ }) : (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ o[k2] = m[k];
15
+ }));
16
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
17
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
18
+ }) : function(o, v) {
19
+ o["default"] = v;
20
+ });
21
+ var __importStar = (this && this.__importStar) || (function () {
22
+ var ownKeys = function(o) {
23
+ ownKeys = Object.getOwnPropertyNames || function (o) {
24
+ var ar = [];
25
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
26
+ return ar;
27
+ };
28
+ return ownKeys(o);
29
+ };
30
+ return function (mod) {
31
+ if (mod && mod.__esModule) return mod;
32
+ var result = {};
33
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
34
+ __setModuleDefault(result, mod);
35
+ return result;
36
+ };
37
+ })();
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.DataPlane = exports.resolveEdgeDataPlane = exports.EdgeDataPlane = exports.resolveRemoteDataPlane = exports.RemoteDataPlane = exports.startDataPlaneSidecar = exports.EGRESS_URL_ENV = exports.startEgressBroker = exports.TURN_NONCE_HEADER = exports.TURN_NONCE_ENV = exports.DATA_MCP_URL_ENV = exports.startDataGateway = exports.TURN_META_KEY = exports.ConnectorHost = exports.identityProvider = exports.passthroughProvider = exports.resolveProvider = exports.CredentialBroker = exports.DEFAULT_TURN_DEADLINE_MS = exports.hashNonce = exports.TurnRegistry = void 0;
40
+ exports.resolveDataPlaneConfig = resolveDataPlaneConfig;
41
+ /**
42
+ * DataPlane — orchestration of the credential broker + data-plane gateway
43
+ * (secure-obo-design §3): TurnRegistry + CredentialBroker + broker-owned connector
44
+ * subprocess + loopback streamable-HTTP MCP gateway.
45
+ *
46
+ * serve starts one DataPlane at boot when the pack declares `spec.dataPlane`, then
47
+ * registers each verified turn to get `{ AGENTHIPPO_TURN_NONCE }` for the engine's
48
+ * per-spawn env. The pack's MCP config points at `${env:AGENTHIPPO_DATA_MCP_URL}` with
49
+ * `bearerTokenEnv: AGENTHIPPO_TURN_NONCE` — turn-invariant bytes, per-turn authority.
50
+ */
51
+ const path = __importStar(require("path"));
52
+ const registry_1 = require("./registry");
53
+ const broker_1 = require("./broker");
54
+ const connector_host_1 = require("./connector-host");
55
+ const gateway_1 = require("./gateway");
56
+ var registry_2 = require("./registry");
57
+ Object.defineProperty(exports, "TurnRegistry", { enumerable: true, get: function () { return registry_2.TurnRegistry; } });
58
+ Object.defineProperty(exports, "hashNonce", { enumerable: true, get: function () { return registry_2.hashNonce; } });
59
+ Object.defineProperty(exports, "DEFAULT_TURN_DEADLINE_MS", { enumerable: true, get: function () { return registry_2.DEFAULT_TURN_DEADLINE_MS; } });
60
+ var broker_2 = require("./broker");
61
+ Object.defineProperty(exports, "CredentialBroker", { enumerable: true, get: function () { return broker_2.CredentialBroker; } });
62
+ Object.defineProperty(exports, "resolveProvider", { enumerable: true, get: function () { return broker_2.resolveProvider; } });
63
+ Object.defineProperty(exports, "passthroughProvider", { enumerable: true, get: function () { return broker_2.passthroughProvider; } });
64
+ Object.defineProperty(exports, "identityProvider", { enumerable: true, get: function () { return broker_2.identityProvider; } });
65
+ var connector_host_2 = require("./connector-host");
66
+ Object.defineProperty(exports, "ConnectorHost", { enumerable: true, get: function () { return connector_host_2.ConnectorHost; } });
67
+ Object.defineProperty(exports, "TURN_META_KEY", { enumerable: true, get: function () { return connector_host_2.TURN_META_KEY; } });
68
+ var gateway_2 = require("./gateway");
69
+ Object.defineProperty(exports, "startDataGateway", { enumerable: true, get: function () { return gateway_2.startDataGateway; } });
70
+ Object.defineProperty(exports, "DATA_MCP_URL_ENV", { enumerable: true, get: function () { return gateway_2.DATA_MCP_URL_ENV; } });
71
+ Object.defineProperty(exports, "TURN_NONCE_ENV", { enumerable: true, get: function () { return gateway_2.TURN_NONCE_ENV; } });
72
+ Object.defineProperty(exports, "TURN_NONCE_HEADER", { enumerable: true, get: function () { return gateway_2.TURN_NONCE_HEADER; } });
73
+ var egress_1 = require("./egress");
74
+ Object.defineProperty(exports, "startEgressBroker", { enumerable: true, get: function () { return egress_1.startEgressBroker; } });
75
+ Object.defineProperty(exports, "EGRESS_URL_ENV", { enumerable: true, get: function () { return egress_1.EGRESS_URL_ENV; } });
76
+ var sidecar_1 = require("./sidecar");
77
+ Object.defineProperty(exports, "startDataPlaneSidecar", { enumerable: true, get: function () { return sidecar_1.startDataPlaneSidecar; } });
78
+ var remote_1 = require("./remote");
79
+ Object.defineProperty(exports, "RemoteDataPlane", { enumerable: true, get: function () { return remote_1.RemoteDataPlane; } });
80
+ Object.defineProperty(exports, "resolveRemoteDataPlane", { enumerable: true, get: function () { return remote_1.resolveRemoteDataPlane; } });
81
+ Object.defineProperty(exports, "EdgeDataPlane", { enumerable: true, get: function () { return remote_1.EdgeDataPlane; } });
82
+ Object.defineProperty(exports, "resolveEdgeDataPlane", { enumerable: true, get: function () { return remote_1.resolveEdgeDataPlane; } });
83
+ /**
84
+ * Resolve the pack's data-plane declaration. The connector path resolves against the PACK
85
+ * SOURCE directory (operator-authored, broker trust domain) — not the agent-readable
86
+ * workspace copy.
87
+ */
88
+ function resolveDataPlaneConfig(spec, packPath) {
89
+ const dp = spec?.dataPlane;
90
+ const connector = dp?.connector?.trim();
91
+ if (!connector) {
92
+ return undefined;
93
+ }
94
+ return {
95
+ connectorPath: path.isAbsolute(connector) ? connector : path.join(packPath, connector),
96
+ provider: dp?.provider?.trim() || undefined,
97
+ };
98
+ }
99
+ class DataPlane {
100
+ url;
101
+ registry;
102
+ broker;
103
+ connector;
104
+ gateway;
105
+ constructor(url, registry, broker, connector, gateway) {
106
+ this.url = url;
107
+ this.registry = registry;
108
+ this.broker = broker;
109
+ this.connector = connector;
110
+ this.gateway = gateway;
111
+ }
112
+ static async start(cfg, opts) {
113
+ // Reuse serve's shared registry when given, so one nonce grants BOTH data and egress
114
+ // capabilities (secure-obo-design §7.6 step 2). Standalone (probe/tests) get their own.
115
+ const registry = opts?.registry ?? new registry_1.TurnRegistry();
116
+ const broker = new broker_1.CredentialBroker((0, broker_1.resolveProvider)(cfg.provider));
117
+ const connector = await connector_host_1.ConnectorHost.spawn({ connectorPath: cfg.connectorPath, logger: opts?.logger });
118
+ try {
119
+ const gateway = await (0, gateway_1.startDataGateway)({
120
+ registry, broker, connector, logger: opts?.logger, audit: opts?.audit,
121
+ host: opts?.gateway?.host, port: opts?.gateway?.port, advertiseHost: opts?.gateway?.advertiseHost,
122
+ });
123
+ return new DataPlane(gateway.url, registry, broker, connector, gateway);
124
+ }
125
+ catch (err) {
126
+ await connector.close();
127
+ throw err;
128
+ }
129
+ }
130
+ /** Register a turn directly in the local registry (used by the sidecar control API). */
131
+ registerTurnRaw(reg) {
132
+ return this.registry.register(reg);
133
+ }
134
+ /**
135
+ * Register a verified turn. Returns the per-spawn env for the engine and the release()
136
+ * to call in the turn's `finally`. The gateway URL itself is boot-stable and rides
137
+ * process.env (see serve), keeping codex's config.toml turn-invariant.
138
+ */
139
+ registerTurn(reg) {
140
+ const { nonce, release } = this.registry.register(reg);
141
+ return {
142
+ nonce,
143
+ env: { [gateway_1.TURN_NONCE_ENV]: nonce, [gateway_1.DATA_MCP_URL_ENV]: this.url },
144
+ release,
145
+ };
146
+ }
147
+ async close() {
148
+ await this.gateway.close();
149
+ await this.connector.close();
150
+ }
151
+ }
152
+ exports.DataPlane = DataPlane;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) AgentHippo.ai. All rights reserved.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TurnRegistry = exports.DEFAULT_TURN_DEADLINE_MS = void 0;
7
+ exports.hashNonce = hashNonce;
8
+ /**
9
+ * TurnRegistry — the per-turn capability table of the data-plane gateway
10
+ * (secure-obo-design §3.1). Replaces the on-disk token file entirely.
11
+ *
12
+ * Per turn: mint a random 128-bit nonce, store the turn's verified identity under
13
+ * `sha256(nonce)` (the raw nonce is never stored or used as a lookup key, so lookups leak
14
+ * no timing signal about live nonces), hand the nonce to the engine via per-spawn env only,
15
+ * and delete the entry in the same `finally` that ends the turn. `deadline` is the backstop
16
+ * for a stranded entry — a leaked nonce dies on its own.
17
+ *
18
+ * The registry holds NO data credential: the gateway selects the credential server-side at
19
+ * call time from the broker's per-user cache (§5 step 7) — nothing a client sends can name
20
+ * a user or a credential.
21
+ */
22
+ const crypto_1 = require("crypto");
23
+ /** Backstop lifetime for a turn's nonce. Turns are seconds-to-minutes; 30 min is generous. */
24
+ exports.DEFAULT_TURN_DEADLINE_MS = 30 * 60_000;
25
+ function hashNonce(nonce) {
26
+ return (0, crypto_1.createHash)('sha256').update(nonce).digest('hex');
27
+ }
28
+ class TurnRegistry {
29
+ entries = new Map();
30
+ /**
31
+ * Mint a nonce and register the turn. Call `release()` in the turn's `finally`.
32
+ */
33
+ register(reg) {
34
+ this.prune();
35
+ // Never derived from anything — pure randomness (secure-obo-design §3.1).
36
+ const nonce = (0, crypto_1.randomBytes)(16).toString('hex');
37
+ const key = hashNonce(nonce);
38
+ this.entries.set(key, {
39
+ userId: reg.userId,
40
+ sessionKey: reg.sessionKey,
41
+ inboundToken: reg.inboundToken,
42
+ egressStoreKey: reg.egressStoreKey,
43
+ deadline: reg.deadline ?? Date.now() + exports.DEFAULT_TURN_DEADLINE_MS,
44
+ });
45
+ let released = false;
46
+ return {
47
+ nonce,
48
+ release: () => {
49
+ if (!released) {
50
+ released = true;
51
+ this.entries.delete(key);
52
+ }
53
+ },
54
+ };
55
+ }
56
+ /**
57
+ * Release a turn by its nonce (idempotent). Lets a holder of the nonce end its own turn —
58
+ * used by the gateway's release endpoint for EDGE-registered turns, where serve holds the
59
+ * nonce but no registration authority. Strictly fail-closed: the worst a stolen nonce can
60
+ * do here is end the capability early.
61
+ */
62
+ releaseByNonce(nonce) {
63
+ return this.entries.delete(hashNonce(nonce));
64
+ }
65
+ /** Resolve a presented nonce to its turn, enforcing the deadline. Fail-closed: unknown/expired → undefined. */
66
+ resolve(nonce, now = Date.now()) {
67
+ const entry = this.entries.get(hashNonce(nonce));
68
+ if (!entry) {
69
+ return undefined;
70
+ }
71
+ if (now > entry.deadline) {
72
+ this.entries.delete(hashNonce(nonce));
73
+ return undefined;
74
+ }
75
+ return entry;
76
+ }
77
+ /** Drop expired entries (called on every register; cheap at realistic turn counts). */
78
+ prune(now = Date.now()) {
79
+ for (const [key, entry] of this.entries) {
80
+ if (now > entry.deadline) {
81
+ this.entries.delete(key);
82
+ }
83
+ }
84
+ }
85
+ get size() {
86
+ return this.entries.size;
87
+ }
88
+ }
89
+ exports.TurnRegistry = TurnRegistry;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) AgentHippo.ai. All rights reserved.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.RemoteDataPlane = exports.EdgeDataPlane = void 0;
7
+ exports.resolveEdgeDataPlane = resolveEdgeDataPlane;
8
+ exports.resolveRemoteDataPlane = resolveRemoteDataPlane;
9
+ /**
10
+ * RemoteDataPlane — serve's client to a data-plane SIDECAR (secure-obo-design §7.6).
11
+ *
12
+ * When the connector runs in its own container, serve (in the agent container) holds NO base
13
+ * credential. It registers each verified turn with the sidecar's authenticated control API and
14
+ * hands the engine the returned nonce plus the sidecar's gateway URL. The engine dials the
15
+ * sidecar gateway with the nonce; the sidecar resolves nonce → user and runs the connector.
16
+ *
17
+ * Presents the same `registerTurn()` shape as the in-process `DataPlane`, so serve's turn
18
+ * handling is identical whether the connector is in-process or a sidecar.
19
+ */
20
+ const gateway_1 = require("./gateway");
21
+ /** Read the edge-mode wiring from env. Returns null unless explicitly enabled. */
22
+ function resolveEdgeDataPlane(env = process.env) {
23
+ const enabled = env.AGENTHIPPO_DATA_PLANE_EDGE?.trim();
24
+ const gatewayUrl = env.AGENTHIPPO_DATA_MCP_URL?.trim();
25
+ if (!enabled || enabled === '0' || enabled.toLowerCase() === 'false' || !gatewayUrl) {
26
+ return null;
27
+ }
28
+ return { gatewayUrl };
29
+ }
30
+ class EdgeDataPlane {
31
+ cfg;
32
+ constructor(cfg) {
33
+ this.cfg = cfg;
34
+ }
35
+ /** The engine-facing gateway URL (boot-stable; set into process.env by serve like the local plane). */
36
+ get url() { return this.cfg.gatewayUrl; }
37
+ /**
38
+ * Adopt an edge-registered nonce for this turn. Release calls the gateway's
39
+ * nonce-authenticated `DELETE /turn` — the one operation a bare nonce may perform besides
40
+ * the tool surface, and strictly fail-closed (it only ends the capability early; the
41
+ * sidecar's registry deadline is the backstop when the call never lands).
42
+ */
43
+ adoptTurn(nonce) {
44
+ return {
45
+ nonce,
46
+ env: { [gateway_1.TURN_NONCE_ENV]: nonce, [gateway_1.DATA_MCP_URL_ENV]: this.cfg.gatewayUrl },
47
+ release: async () => {
48
+ try {
49
+ await fetch(new URL('/turn', this.cfg.gatewayUrl), {
50
+ method: 'DELETE',
51
+ headers: { Authorization: `Bearer ${nonce}` },
52
+ });
53
+ }
54
+ catch { /* best-effort release; the sidecar deadline is the backstop */ }
55
+ },
56
+ };
57
+ }
58
+ }
59
+ exports.EdgeDataPlane = EdgeDataPlane;
60
+ /** Read the sidecar wiring from env (set at deploy on the agent container). Returns null if absent. */
61
+ function resolveRemoteDataPlane(env = process.env) {
62
+ const controlUrl = env.AGENTHIPPO_DATA_PLANE_CONTROL_URL?.trim();
63
+ const registerToken = env.AGENTHIPPO_DATA_PLANE_TOKEN?.trim();
64
+ const gatewayUrl = env.AGENTHIPPO_DATA_MCP_URL?.trim();
65
+ if (!controlUrl || !registerToken || !gatewayUrl) {
66
+ return null;
67
+ }
68
+ return { controlUrl, registerToken, gatewayUrl };
69
+ }
70
+ class RemoteDataPlane {
71
+ cfg;
72
+ constructor(cfg) {
73
+ this.cfg = cfg;
74
+ }
75
+ /** The engine-facing gateway URL (boot-stable; set into process.env by serve like the local plane). */
76
+ get url() { return this.cfg.gatewayUrl; }
77
+ /**
78
+ * Register a turn with the sidecar. Returns the per-spawn env + release, matching DataPlane.
79
+ * The base credential never touches this process — only the nonce comes back.
80
+ */
81
+ async registerTurn(reg) {
82
+ const res = await fetch(`${this.cfg.controlUrl.replace(/\/$/, '')}/turns`, {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${this.cfg.registerToken}` },
85
+ body: JSON.stringify(reg),
86
+ });
87
+ if (!res.ok) {
88
+ throw new Error(`data-plane sidecar register failed: HTTP ${res.status}`);
89
+ }
90
+ const { nonce } = await res.json();
91
+ return {
92
+ nonce,
93
+ env: { [gateway_1.TURN_NONCE_ENV]: nonce, [gateway_1.DATA_MCP_URL_ENV]: this.cfg.gatewayUrl },
94
+ release: async () => {
95
+ try {
96
+ await fetch(`${this.cfg.controlUrl.replace(/\/$/, '')}/turns/${encodeURIComponent(nonce)}`, {
97
+ method: 'DELETE',
98
+ headers: { Authorization: `Bearer ${this.cfg.registerToken}` },
99
+ });
100
+ }
101
+ catch { /* best-effort release; the sidecar deadline is the backstop */ }
102
+ },
103
+ };
104
+ }
105
+ }
106
+ exports.RemoteDataPlane = RemoteDataPlane;