@agentfield/sdk 0.1.42 → 0.1.43-rc.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.
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import express from 'express';
2
- import crypto, { randomUUID } from 'crypto';
2
+ import rateLimit from 'express-rate-limit';
3
+ import crypto2, { randomUUID, createHash } from 'crypto';
3
4
  import { AsyncLocalStorage } from 'async_hooks';
4
5
  import { generateObject, generateText, streamText, embed, embedMany } from 'ai';
5
6
  import { createOpenAI } from '@ai-sdk/openai';
@@ -11,11 +12,11 @@ import { createXai } from '@ai-sdk/xai';
11
12
  import { createDeepSeek } from '@ai-sdk/deepseek';
12
13
  import { createCohere } from '@ai-sdk/cohere';
13
14
  import os from 'os';
14
- import axios, { isAxiosError } from 'axios';
15
+ import axios5, { isAxiosError } from 'axios';
15
16
  import http from 'http';
16
17
  import https from 'https';
17
18
  import WebSocket from 'ws';
18
- import { Buffer } from 'buffer';
19
+ import { Buffer as Buffer$1 } from 'buffer';
19
20
  import { zodToJsonSchema } from 'zod-to-json-schema';
20
21
 
21
22
  // src/agent/Agent.ts
@@ -248,7 +249,7 @@ var StatelessRateLimiter = class {
248
249
  }
249
250
  _getContainerSeed() {
250
251
  const identifier = `${os.hostname()}-${process.pid}`;
251
- const hash = crypto.createHash("md5").update(identifier).digest("hex");
252
+ const hash = crypto2.createHash("md5").update(identifier).digest("hex");
252
253
  return parseInt(hash.slice(0, 8), 16);
253
254
  }
254
255
  _isRateLimitError(error) {
@@ -634,15 +635,97 @@ var httpsAgent = new https.Agent({
634
635
  maxTotalSockets: 50,
635
636
  maxFreeSockets: 5
636
637
  });
638
+ var HEADER_CALLER_DID = "X-Caller-DID";
639
+ var HEADER_DID_SIGNATURE = "X-DID-Signature";
640
+ var HEADER_DID_TIMESTAMP = "X-DID-Timestamp";
641
+ var HEADER_DID_NONCE = "X-DID-Nonce";
642
+ var ED25519_PKCS8_PREFIX = Buffer.from([
643
+ 48,
644
+ 46,
645
+ 2,
646
+ 1,
647
+ 0,
648
+ 48,
649
+ 5,
650
+ 6,
651
+ 3,
652
+ 43,
653
+ 101,
654
+ 112,
655
+ 4,
656
+ 34,
657
+ 4,
658
+ 32
659
+ ]);
660
+ var DIDAuthenticator = class {
661
+ _did;
662
+ _privateKey;
663
+ constructor(did, privateKeyJwk) {
664
+ if (did && privateKeyJwk) {
665
+ this.setCredentials(did, privateKeyJwk);
666
+ }
667
+ }
668
+ get isConfigured() {
669
+ return this._did !== void 0 && this._privateKey !== void 0;
670
+ }
671
+ get did() {
672
+ return this._did;
673
+ }
674
+ signRequest(body) {
675
+ if (!this.isConfigured) {
676
+ return {};
677
+ }
678
+ const timestamp = Math.floor(Date.now() / 1e3).toString();
679
+ const nonce = crypto2.randomBytes(16).toString("hex");
680
+ const bodyHash = crypto2.createHash("sha256").update(body).digest("hex");
681
+ const payload = `${timestamp}:${nonce}:${bodyHash}`;
682
+ const signature = crypto2.sign(null, Buffer.from(payload), this._privateKey);
683
+ const signatureB64 = signature.toString("base64");
684
+ return {
685
+ [HEADER_CALLER_DID]: this._did,
686
+ [HEADER_DID_SIGNATURE]: signatureB64,
687
+ [HEADER_DID_TIMESTAMP]: timestamp,
688
+ [HEADER_DID_NONCE]: nonce
689
+ };
690
+ }
691
+ setCredentials(did, privateKeyJwk) {
692
+ this._privateKey = parsePrivateKeyJWK(privateKeyJwk);
693
+ this._did = did;
694
+ }
695
+ };
696
+ function parsePrivateKeyJWK(jwkJSON) {
697
+ let key;
698
+ try {
699
+ key = JSON.parse(jwkJSON);
700
+ } catch {
701
+ throw new Error("Invalid JWK format: failed to parse JSON");
702
+ }
703
+ if (key.kty !== "OKP" || key.crv !== "Ed25519") {
704
+ throw new Error("Invalid key type: expected Ed25519 OKP key");
705
+ }
706
+ if (!key.d) {
707
+ throw new Error("Missing 'd' (private key) in JWK");
708
+ }
709
+ const seedBytes = Buffer.from(key.d, "base64url");
710
+ if (seedBytes.length !== 32) {
711
+ throw new Error(`Invalid private key length: expected 32 bytes, got ${seedBytes.length}`);
712
+ }
713
+ return crypto2.createPrivateKey({
714
+ key: Buffer.concat([ED25519_PKCS8_PREFIX, seedBytes]),
715
+ format: "der",
716
+ type: "pkcs8"
717
+ });
718
+ }
637
719
 
638
720
  // src/client/AgentFieldClient.ts
639
721
  var AgentFieldClient = class {
640
722
  http;
641
723
  config;
642
724
  defaultHeaders;
725
+ didAuthenticator;
643
726
  constructor(config) {
644
727
  const baseURL = (config.agentFieldUrl ?? "http://localhost:8080").replace(/\/$/, "");
645
- this.http = axios.create({
728
+ this.http = axios5.create({
646
729
  baseURL,
647
730
  timeout: 3e4,
648
731
  httpAgent,
@@ -654,19 +737,30 @@ var AgentFieldClient = class {
654
737
  mergedHeaders["X-API-Key"] = config.apiKey;
655
738
  }
656
739
  this.defaultHeaders = this.sanitizeHeaders(mergedHeaders);
740
+ this.didAuthenticator = new DIDAuthenticator(config.did, config.privateKeyJwk);
657
741
  }
658
742
  async register(payload) {
659
- await this.http.post("/api/v1/nodes/register", payload, { headers: this.mergeHeaders() });
743
+ const bodyStr = JSON.stringify(payload);
744
+ const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
745
+ const res = await this.http.post("/api/v1/nodes/register", bodyStr, {
746
+ headers: this.mergeHeaders({ "Content-Type": "application/json", ...authHeaders })
747
+ });
748
+ return res.data;
749
+ }
750
+ async getNode(nodeId) {
751
+ const res = await this.http.get(`/api/v1/nodes/${encodeURIComponent(nodeId)}`, {
752
+ headers: this.mergeHeaders({})
753
+ });
754
+ return res.data;
660
755
  }
661
756
  async heartbeat(status = "ready") {
662
757
  const nodeId = this.config.nodeId;
758
+ const bodyStr = JSON.stringify({ status, version: this.config.version ?? "", timestamp: (/* @__PURE__ */ new Date()).toISOString() });
759
+ const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
663
760
  const res = await this.http.post(
664
761
  `/api/v1/nodes/${nodeId}/heartbeat`,
665
- {
666
- status,
667
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
668
- },
669
- { headers: this.mergeHeaders() }
762
+ bodyStr,
763
+ { headers: this.mergeHeaders({ "Content-Type": "application/json", ...authHeaders }) }
670
764
  );
671
765
  return res.data;
672
766
  }
@@ -681,14 +775,27 @@ var AgentFieldClient = class {
681
775
  if (metadata?.targetDid) headers["X-Target-DID"] = metadata.targetDid;
682
776
  if (metadata?.agentNodeDid) headers["X-Agent-Node-DID"] = metadata.agentNodeDid;
683
777
  if (metadata?.agentNodeId) headers["X-Agent-Node-ID"] = metadata.agentNodeId;
684
- const res = await this.http.post(
685
- `/api/v1/execute/${target}`,
686
- {
687
- input
688
- },
689
- { headers: this.mergeHeaders(headers) }
690
- );
691
- return res.data?.result ?? res.data;
778
+ const bodyStr = JSON.stringify({ input });
779
+ const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
780
+ try {
781
+ const res = await this.http.post(
782
+ `/api/v1/execute/${target}`,
783
+ bodyStr,
784
+ { headers: this.mergeHeaders({ "Content-Type": "application/json", ...headers, ...authHeaders }) }
785
+ );
786
+ return res.data?.result ?? res.data;
787
+ } catch (err) {
788
+ const respData = err?.response?.data;
789
+ if (respData) {
790
+ const status = err.response.status;
791
+ const msg = respData.message || respData.error || JSON.stringify(respData);
792
+ const enriched = new Error(`execute ${target} failed (${status}): ${msg}`);
793
+ enriched.status = status;
794
+ enriched.responseData = respData;
795
+ throw enriched;
796
+ }
797
+ throw err;
798
+ }
692
799
  }
693
800
  async publishWorkflowEvent(event) {
694
801
  const payload = {
@@ -706,8 +813,10 @@ var AgentFieldClient = class {
706
813
  error: event.error,
707
814
  duration_ms: event.durationMs
708
815
  };
709
- this.http.post("/api/v1/workflow/executions/events", payload, {
710
- headers: this.mergeHeaders(),
816
+ const bodyStr = JSON.stringify(payload);
817
+ const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
818
+ this.http.post("/api/v1/workflow/executions/events", bodyStr, {
819
+ headers: this.mergeHeaders({ "Content-Type": "application/json", ...authHeaders }),
711
820
  timeout: this.config.devMode ? 1e3 : void 0
712
821
  }).catch(() => {
713
822
  });
@@ -723,7 +832,11 @@ var AgentFieldClient = class {
723
832
  duration_ms: update.durationMs,
724
833
  progress: update.progress !== void 0 ? Math.round(update.progress) : void 0
725
834
  };
726
- await this.http.post(`/api/v1/executions/${executionId}/status`, payload, { headers: this.mergeHeaders() });
835
+ const bodyStr = JSON.stringify(payload);
836
+ const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
837
+ await this.http.post(`/api/v1/executions/${executionId}/status`, bodyStr, {
838
+ headers: this.mergeHeaders({ "Content-Type": "application/json", ...authHeaders })
839
+ });
727
840
  }
728
841
  async discoverCapabilities(options = {}) {
729
842
  const format = (options.format ?? "json").toLowerCase();
@@ -864,6 +977,15 @@ var AgentFieldClient = class {
864
977
  if (metadata.agentNodeId) headers["x-agent-node-id"] = metadata.agentNodeId;
865
978
  return headers;
866
979
  }
980
+ setDIDCredentials(did, privateKeyJwk) {
981
+ this.didAuthenticator.setCredentials(did, privateKeyJwk);
982
+ }
983
+ get didAuthConfigured() {
984
+ return this.didAuthenticator.isConfigured;
985
+ }
986
+ getDID() {
987
+ return this.didAuthenticator.did;
988
+ }
867
989
  sendNote(message, tags, agentNodeId, metadata, uiApiBaseUrl, devMode) {
868
990
  const payload = {
869
991
  message,
@@ -872,11 +994,14 @@ var AgentFieldClient = class {
872
994
  agent_node_id: agentNodeId
873
995
  };
874
996
  const executionHeaders = this.buildExecutionHeaders({ ...metadata, agentNodeId });
997
+ const bodyStr = JSON.stringify(payload);
998
+ const authHeaders = this.didAuthenticator.signRequest(Buffer.from(bodyStr));
875
999
  const headers = this.mergeHeaders({
876
- "content-type": "application/json",
877
- ...executionHeaders
1000
+ "Content-Type": "application/json",
1001
+ ...executionHeaders,
1002
+ ...authHeaders
878
1003
  });
879
- axios.post(`${uiApiBaseUrl}/executions/note`, payload, {
1004
+ axios5.post(`${uiApiBaseUrl}/executions/note`, bodyStr, {
880
1005
  headers,
881
1006
  timeout: devMode ? 5e3 : 1e4,
882
1007
  httpAgent,
@@ -889,7 +1014,7 @@ var MemoryClient = class {
889
1014
  http;
890
1015
  defaultHeaders;
891
1016
  constructor(baseUrl, defaultHeaders) {
892
- this.http = axios.create({
1017
+ this.http = axios5.create({
893
1018
  baseURL: baseUrl.replace(/\/$/, ""),
894
1019
  timeout: 3e4,
895
1020
  httpAgent,
@@ -1271,7 +1396,7 @@ var DidClient = class {
1271
1396
  http;
1272
1397
  defaultHeaders;
1273
1398
  constructor(baseUrl, defaultHeaders) {
1274
- this.http = axios.create({
1399
+ this.http = axios5.create({
1275
1400
  baseURL: baseUrl.replace(/\/$/, ""),
1276
1401
  timeout: 3e4,
1277
1402
  httpAgent,
@@ -1308,7 +1433,7 @@ var DidClient = class {
1308
1433
  parseIdentityPackage(pkg) {
1309
1434
  const parseIdentity = (data) => ({
1310
1435
  did: data?.did ?? "",
1311
- privateKeyJwk: data?.private_key_jwk ?? "",
1436
+ privateKeyJwk: data?.private_key_jwk,
1312
1437
  publicKeyJwk: data?.public_key_jwk ?? "",
1313
1438
  derivationPath: data?.derivation_path ?? "",
1314
1439
  componentType: data?.component_type ?? "",
@@ -1397,7 +1522,7 @@ var DidClient = class {
1397
1522
  if (typeof data === "string") {
1398
1523
  value = data;
1399
1524
  } else if (data instanceof Uint8Array) {
1400
- value = Buffer.from(data).toString("utf-8");
1525
+ value = Buffer$1.from(data).toString("utf-8");
1401
1526
  } else if (typeof data === "object") {
1402
1527
  try {
1403
1528
  value = JSON.stringify(data, Object.keys(data).sort());
@@ -1407,7 +1532,7 @@ var DidClient = class {
1407
1532
  } else {
1408
1533
  value = String(data);
1409
1534
  }
1410
- return Buffer.from(value, "utf-8").toString("base64");
1535
+ return Buffer$1.from(value, "utf-8").toString("base64");
1411
1536
  }
1412
1537
  mapExecutionCredential(data) {
1413
1538
  return {
@@ -1660,7 +1785,7 @@ var MCPClient = class {
1660
1785
  this.alias = config.alias;
1661
1786
  this.transport = config.transport ?? "http";
1662
1787
  this.baseUrl = (config.url ?? `http://localhost:${config.port}`).replace(/\/$/, "");
1663
- this.http = axios.create({
1788
+ this.http = axios5.create({
1664
1789
  baseURL: this.baseUrl,
1665
1790
  headers: config.headers,
1666
1791
  timeout: 3e4,
@@ -1874,6 +1999,227 @@ var MCPToolRegistrar = class {
1874
1999
  return collapsed || "mcp_tool";
1875
2000
  }
1876
2001
  };
2002
+ var LocalVerifier = class {
2003
+ agentFieldUrl;
2004
+ refreshInterval;
2005
+ timestampWindow;
2006
+ apiKey;
2007
+ policies = [];
2008
+ revokedDids = /* @__PURE__ */ new Set();
2009
+ registeredDids = /* @__PURE__ */ new Set();
2010
+ adminPublicKeyBytes = null;
2011
+ issuerDid = null;
2012
+ lastRefresh = 0;
2013
+ initialized = false;
2014
+ constructor(agentFieldUrl, refreshInterval = 300, timestampWindow = 300, apiKey) {
2015
+ this.agentFieldUrl = agentFieldUrl.replace(/\/+$/, "");
2016
+ this.refreshInterval = refreshInterval;
2017
+ this.timestampWindow = timestampWindow;
2018
+ this.apiKey = apiKey;
2019
+ }
2020
+ get needsRefresh() {
2021
+ return Date.now() / 1e3 - this.lastRefresh > this.refreshInterval;
2022
+ }
2023
+ async refresh() {
2024
+ const headers = {};
2025
+ if (this.apiKey) {
2026
+ headers["X-API-Key"] = this.apiKey;
2027
+ }
2028
+ let success = true;
2029
+ try {
2030
+ const resp = await axios5.get(`${this.agentFieldUrl}/api/v1/policies`, {
2031
+ headers,
2032
+ timeout: 1e4
2033
+ });
2034
+ if (resp.status !== 200) {
2035
+ success = false;
2036
+ } else {
2037
+ this.policies = resp.data?.policies ?? [];
2038
+ }
2039
+ } catch {
2040
+ success = false;
2041
+ }
2042
+ try {
2043
+ const resp = await axios5.get(`${this.agentFieldUrl}/api/v1/revocations`, {
2044
+ headers,
2045
+ timeout: 1e4
2046
+ });
2047
+ if (resp.status !== 200) {
2048
+ success = false;
2049
+ } else {
2050
+ this.revokedDids = new Set(resp.data?.revoked_dids ?? []);
2051
+ }
2052
+ } catch {
2053
+ success = false;
2054
+ }
2055
+ try {
2056
+ const resp = await axios5.get(`${this.agentFieldUrl}/api/v1/registered-dids`, {
2057
+ headers,
2058
+ timeout: 1e4
2059
+ });
2060
+ if (resp.status !== 200) {
2061
+ success = false;
2062
+ } else {
2063
+ this.registeredDids = new Set(resp.data?.registered_dids ?? []);
2064
+ }
2065
+ } catch {
2066
+ success = false;
2067
+ }
2068
+ try {
2069
+ const resp = await axios5.get(`${this.agentFieldUrl}/api/v1/admin/public-key`, {
2070
+ headers,
2071
+ timeout: 1e4
2072
+ });
2073
+ if (resp.status !== 200) {
2074
+ success = false;
2075
+ } else {
2076
+ const jwk = resp.data?.public_key_jwk;
2077
+ this.issuerDid = resp.data?.issuer_did ?? null;
2078
+ if (jwk?.x) {
2079
+ this.adminPublicKeyBytes = new Uint8Array(Buffer.from(jwk.x, "base64url"));
2080
+ }
2081
+ }
2082
+ } catch {
2083
+ success = false;
2084
+ }
2085
+ if (success) {
2086
+ this.lastRefresh = Date.now() / 1e3;
2087
+ this.initialized = true;
2088
+ }
2089
+ return success;
2090
+ }
2091
+ checkRevocation(callerDid) {
2092
+ return this.revokedDids.has(callerDid);
2093
+ }
2094
+ /**
2095
+ * Check if a caller DID is registered with the control plane.
2096
+ * Returns true if registered (known), false if unknown.
2097
+ * When the cache is empty (not yet loaded), returns true to avoid
2098
+ * blocking requests before the first refresh completes.
2099
+ */
2100
+ checkRegistration(callerDid) {
2101
+ if (this.registeredDids.size === 0) {
2102
+ return true;
2103
+ }
2104
+ return this.registeredDids.has(callerDid);
2105
+ }
2106
+ /**
2107
+ * Resolve the public key bytes from a DID.
2108
+ *
2109
+ * For did:key, the public key is self-contained in the identifier:
2110
+ * did:key:z<base64url(0xed01 + 32-byte-pubkey)>
2111
+ *
2112
+ * For other DID methods, falls back to the admin public key.
2113
+ */
2114
+ resolvePublicKey(callerDid) {
2115
+ if (callerDid.startsWith("did:key:z")) {
2116
+ try {
2117
+ const encoded = callerDid.slice("did:key:z".length);
2118
+ const decoded = Buffer.from(encoded, "base64url");
2119
+ if (decoded.length >= 34 && decoded[0] === 237 && decoded[1] === 1) {
2120
+ return new Uint8Array(decoded.subarray(2, 34));
2121
+ }
2122
+ return null;
2123
+ } catch {
2124
+ return null;
2125
+ }
2126
+ }
2127
+ return this.adminPublicKeyBytes;
2128
+ }
2129
+ async verifySignature(callerDid, signatureB64, timestamp, body, nonce) {
2130
+ const ts = parseInt(timestamp, 10);
2131
+ if (isNaN(ts)) return false;
2132
+ const now = Math.floor(Date.now() / 1e3);
2133
+ if (Math.abs(now - ts) > this.timestampWindow) return false;
2134
+ const publicKeyBytes = this.resolvePublicKey(callerDid);
2135
+ if (!publicKeyBytes || publicKeyBytes.length !== 32) {
2136
+ return false;
2137
+ }
2138
+ try {
2139
+ const { createPublicKey, verify } = await import('crypto');
2140
+ const bodyHash = createHash("sha256").update(body).digest("hex");
2141
+ const payloadStr = nonce ? `${timestamp}:${nonce}:${bodyHash}` : `${timestamp}:${bodyHash}`;
2142
+ const payload = Buffer.from(payloadStr, "utf-8");
2143
+ const signatureBytes = Buffer.from(signatureB64, "base64");
2144
+ const publicKey = createPublicKey({
2145
+ key: Buffer.concat([
2146
+ // Ed25519 DER prefix for a 32-byte public key
2147
+ Buffer.from("302a300506032b6570032100", "hex"),
2148
+ Buffer.from(publicKeyBytes)
2149
+ ]),
2150
+ format: "der",
2151
+ type: "spki"
2152
+ });
2153
+ return verify(null, payload, publicKey, signatureBytes);
2154
+ } catch {
2155
+ return false;
2156
+ }
2157
+ }
2158
+ evaluatePolicy(callerTags, targetTags, functionName, inputParams) {
2159
+ if (!this.policies || this.policies.length === 0) {
2160
+ return false;
2161
+ }
2162
+ const sorted = [...this.policies].sort((a, b) => (b.priority ?? 0) - (a.priority ?? 0));
2163
+ for (const policy of sorted) {
2164
+ if (policy.enabled === false) continue;
2165
+ if (policy.caller_tags?.length > 0) {
2166
+ if (!policy.caller_tags.some((t) => callerTags.includes(t))) continue;
2167
+ }
2168
+ if (policy.target_tags?.length > 0) {
2169
+ if (!policy.target_tags.some((t) => targetTags.includes(t))) continue;
2170
+ }
2171
+ if (policy.deny_functions?.length > 0 && functionMatches(functionName, policy.deny_functions)) {
2172
+ return false;
2173
+ }
2174
+ if (policy.allow_functions?.length > 0 && !functionMatches(functionName, policy.allow_functions)) {
2175
+ continue;
2176
+ }
2177
+ if (policy.constraints && inputParams) {
2178
+ if (!evaluateConstraints(policy.constraints, inputParams)) {
2179
+ return false;
2180
+ }
2181
+ }
2182
+ const action = policy.action || "allow";
2183
+ return action === "allow";
2184
+ }
2185
+ return true;
2186
+ }
2187
+ };
2188
+ function functionMatches(name, patterns) {
2189
+ for (const pattern of patterns) {
2190
+ if (pattern === "*") return true;
2191
+ if (pattern.endsWith("*") && name.startsWith(pattern.slice(0, -1))) return true;
2192
+ if (pattern.startsWith("*") && name.endsWith(pattern.slice(1))) return true;
2193
+ if (name === pattern) return true;
2194
+ }
2195
+ return false;
2196
+ }
2197
+ function evaluateConstraints(constraints, inputParams) {
2198
+ for (const [paramName, constraint] of Object.entries(constraints)) {
2199
+ if (!(paramName in inputParams)) continue;
2200
+ const value = Number(inputParams[paramName]);
2201
+ const threshold = Number(constraint.value);
2202
+ if (isNaN(value) || isNaN(threshold)) return false;
2203
+ switch (constraint.operator) {
2204
+ case "<=":
2205
+ if (value > threshold) return false;
2206
+ break;
2207
+ case ">=":
2208
+ if (value < threshold) return false;
2209
+ break;
2210
+ case "<":
2211
+ if (value >= threshold) return false;
2212
+ break;
2213
+ case ">":
2214
+ if (value <= threshold) return false;
2215
+ break;
2216
+ case "==":
2217
+ if (Math.abs(value - threshold) > 1e-9) return false;
2218
+ break;
2219
+ }
2220
+ }
2221
+ return true;
2222
+ }
1877
2223
 
1878
2224
  // src/agent/Agent.ts
1879
2225
  var TargetNotFoundError = class extends Error {
@@ -1894,6 +2240,8 @@ var Agent = class {
1894
2240
  memoryWatchers = [];
1895
2241
  mcpClientRegistry;
1896
2242
  mcpToolRegistrar;
2243
+ localVerifier;
2244
+ realtimeValidationFunctions = /* @__PURE__ */ new Set();
1897
2245
  constructor(config) {
1898
2246
  const mcp = config.mcp ? {
1899
2247
  autoRegisterTools: config.mcp.autoRegisterTools ?? true,
@@ -1926,14 +2274,28 @@ var Agent = class {
1926
2274
  });
1927
2275
  this.mcpToolRegistrar.registerServers(this.config.mcp.servers);
1928
2276
  }
2277
+ if (this.config.localVerification && this.config.agentFieldUrl) {
2278
+ this.localVerifier = new LocalVerifier(
2279
+ this.config.agentFieldUrl,
2280
+ this.config.verificationRefreshInterval ?? 300,
2281
+ 300,
2282
+ this.config.apiKey
2283
+ );
2284
+ }
1929
2285
  this.registerDefaultRoutes();
1930
2286
  }
1931
2287
  reasoner(name, handler, options) {
1932
2288
  this.reasoners.register(name, handler, options);
2289
+ if (options?.requireRealtimeValidation) {
2290
+ this.realtimeValidationFunctions.add(name);
2291
+ }
1933
2292
  return this;
1934
2293
  }
1935
2294
  skill(name, handler, options) {
1936
2295
  this.skills.register(name, handler, options);
2296
+ if (options?.requireRealtimeValidation) {
2297
+ this.realtimeValidationFunctions.add(name);
2298
+ }
1937
2299
  return this;
1938
2300
  }
1939
2301
  includeRouter(router) {
@@ -2035,6 +2397,16 @@ var Agent = class {
2035
2397
  }
2036
2398
  }
2037
2399
  await this.registerWithControlPlane();
2400
+ if (this.localVerifier) {
2401
+ try {
2402
+ const ok = await this.localVerifier.refresh();
2403
+ if (!ok) {
2404
+ console.warn("[LocalVerifier] Initial refresh partially failed \u2014 some verification data may be stale");
2405
+ }
2406
+ } catch (err) {
2407
+ console.warn("[LocalVerifier] Initial refresh failed:", err);
2408
+ }
2409
+ }
2038
2410
  const port = this.config.port ?? 8001;
2039
2411
  const host = this.config.host ?? "0.0.0.0";
2040
2412
  await this.agentFieldClient.heartbeat("starting");
@@ -2179,6 +2551,111 @@ var Agent = class {
2179
2551
  this.app.get("/skills", (_req, res) => {
2180
2552
  res.json(this.skills.all().map((s) => s.name));
2181
2553
  });
2554
+ if (this.localVerifier) {
2555
+ const verifier = this.localVerifier;
2556
+ const realtimeFunctions = this.realtimeValidationFunctions;
2557
+ const authRateLimiter = rateLimit({
2558
+ windowMs: 6e4,
2559
+ max: 30,
2560
+ standardHeaders: true,
2561
+ legacyHeaders: false,
2562
+ keyGenerator: (req) => {
2563
+ const callerDID = req.headers["x-caller-did"];
2564
+ if (typeof callerDID === "string" && callerDID.length > 0) {
2565
+ return callerDID;
2566
+ }
2567
+ return req.ip ?? "unknown";
2568
+ },
2569
+ message: { error: "rate_limit_exceeded", message: "Too many authentication attempts. Try again later." },
2570
+ skip: (req) => {
2571
+ const path = req.path;
2572
+ if (!path.startsWith("/reasoners/") && !path.startsWith("/skills/") && !path.startsWith("/execute") && !path.startsWith("/api/v1/reasoners/") && !path.startsWith("/api/v1/skills/")) {
2573
+ return true;
2574
+ }
2575
+ const parts = path.replace(/^\/+/, "").split("/");
2576
+ const funcName = parts[parts.length - 1] ?? "";
2577
+ return realtimeFunctions.has(funcName);
2578
+ }
2579
+ });
2580
+ this.app.use(authRateLimiter);
2581
+ this.app.use(async (req, res, next) => {
2582
+ const path = req.path;
2583
+ if (!path.startsWith("/reasoners/") && !path.startsWith("/skills/") && !path.startsWith("/execute") && !path.startsWith("/api/v1/reasoners/") && !path.startsWith("/api/v1/skills/")) {
2584
+ return next();
2585
+ }
2586
+ const parts = path.replace(/^\/+/, "").split("/");
2587
+ const funcName = parts[parts.length - 1] ?? "";
2588
+ if (realtimeFunctions.has(funcName)) {
2589
+ return next();
2590
+ }
2591
+ if (verifier.needsRefresh) {
2592
+ try {
2593
+ await verifier.refresh();
2594
+ } catch (err) {
2595
+ console.warn("[LocalVerifier] Cache refresh failed:", err);
2596
+ }
2597
+ }
2598
+ const callerDid = req.headers["x-caller-did"];
2599
+ const signature = req.headers["x-did-signature"];
2600
+ const timestamp = req.headers["x-did-timestamp"];
2601
+ const nonce = req.headers["x-did-nonce"];
2602
+ if (!callerDid) {
2603
+ return res.status(401).json({
2604
+ error: "did_auth_required",
2605
+ message: "DID authentication required"
2606
+ });
2607
+ }
2608
+ if (verifier.checkRevocation(callerDid)) {
2609
+ return res.status(403).json({
2610
+ error: "did_revoked",
2611
+ message: `Caller DID ${callerDid} has been revoked`
2612
+ });
2613
+ }
2614
+ if (!verifier.checkRegistration(callerDid)) {
2615
+ return res.status(403).json({
2616
+ error: "did_not_registered",
2617
+ message: `Caller DID ${callerDid} is not registered with the control plane`
2618
+ });
2619
+ }
2620
+ if (!signature) {
2621
+ return res.status(401).json({
2622
+ error: "signature_required",
2623
+ message: "DID signature required"
2624
+ });
2625
+ }
2626
+ if (timestamp) {
2627
+ const body = Buffer.isBuffer(req.body) ? req.body : Buffer.from(JSON.stringify(req.body));
2628
+ const valid = await verifier.verifySignature(callerDid, signature, timestamp, body, nonce);
2629
+ if (!valid) {
2630
+ return res.status(401).json({
2631
+ error: "signature_invalid",
2632
+ message: "DID signature verification failed"
2633
+ });
2634
+ }
2635
+ } else {
2636
+ return res.status(401).json({
2637
+ error: "signature_invalid",
2638
+ message: "DID signature verification failed: missing timestamp"
2639
+ });
2640
+ }
2641
+ const agentTags = this.config.tags ?? [];
2642
+ const allowed = verifier.evaluatePolicy(
2643
+ [],
2644
+ // caller tags (not resolvable without control plane)
2645
+ agentTags,
2646
+ // target tags (this agent's own tags)
2647
+ funcName,
2648
+ typeof req.body === "object" && req.body !== null ? req.body : {}
2649
+ );
2650
+ if (!allowed) {
2651
+ return res.status(403).json({
2652
+ error: "policy_denied",
2653
+ message: "Access denied by policy"
2654
+ });
2655
+ }
2656
+ next();
2657
+ });
2658
+ }
2182
2659
  this.app.post("/api/v1/reasoners/*", (req, res) => this.executeReasoner(req, res, req.params[0]));
2183
2660
  this.app.post("/reasoners/:name", (req, res) => this.executeReasoner(req, res, req.params.name));
2184
2661
  this.app.post("/api/v1/skills/*", (req, res) => this.executeSkill(req, res, req.params[0]));
@@ -2201,7 +2678,10 @@ var Agent = class {
2201
2678
  if (err instanceof TargetNotFoundError) {
2202
2679
  res.status(404).json({ error: err.message });
2203
2680
  } else {
2204
- res.status(500).json({ error: err?.message ?? "Execution failed" });
2681
+ const body = { error: err?.message ?? "Execution failed" };
2682
+ if (err?.responseData) body.error_details = err.responseData;
2683
+ const statusCode = err?.status >= 400 ? err.status : 500;
2684
+ res.status(statusCode).json(body);
2205
2685
  }
2206
2686
  }
2207
2687
  }
@@ -2220,7 +2700,10 @@ var Agent = class {
2220
2700
  if (err instanceof TargetNotFoundError) {
2221
2701
  res.status(404).json({ error: err.message });
2222
2702
  } else {
2223
- res.status(500).json({ error: err?.message ?? "Execution failed" });
2703
+ const body = { error: err?.message ?? "Execution failed" };
2704
+ if (err?.responseData) body.error_details = err.responseData;
2705
+ const statusCode = err?.status >= 400 ? err.status : 500;
2706
+ res.status(statusCode).json(body);
2224
2707
  }
2225
2708
  }
2226
2709
  }
@@ -2255,7 +2738,10 @@ var Agent = class {
2255
2738
  if (err instanceof TargetNotFoundError) {
2256
2739
  res.status(404).json({ error: err.message });
2257
2740
  } else {
2258
- res.status(500).json({ error: err?.message ?? "Execution failed" });
2741
+ const body = { error: err?.message ?? "Execution failed" };
2742
+ if (err?.responseData) body.error_details = err.responseData;
2743
+ const statusCode = err?.status >= 400 ? err.status : 500;
2744
+ res.status(statusCode).json(body);
2259
2745
  }
2260
2746
  }
2261
2747
  }
@@ -2440,24 +2926,32 @@ var Agent = class {
2440
2926
  return void 0;
2441
2927
  }
2442
2928
  reasonerDefinitions() {
2443
- return this.reasoners.all().map((r) => ({
2444
- id: r.name,
2445
- input_schema: toJsonSchema(r.options?.inputSchema),
2446
- output_schema: toJsonSchema(r.options?.outputSchema),
2447
- memory_config: r.options?.memoryConfig ?? {
2448
- auto_inject: [],
2449
- memory_retention: "",
2450
- cache_results: false
2451
- },
2452
- tags: r.options?.tags ?? []
2453
- }));
2929
+ return this.reasoners.all().map((r) => {
2930
+ const tags = r.options?.tags ?? [];
2931
+ return {
2932
+ id: r.name,
2933
+ input_schema: toJsonSchema(r.options?.inputSchema),
2934
+ output_schema: toJsonSchema(r.options?.outputSchema),
2935
+ memory_config: r.options?.memoryConfig ?? {
2936
+ auto_inject: [],
2937
+ memory_retention: "",
2938
+ cache_results: false
2939
+ },
2940
+ tags,
2941
+ proposed_tags: tags
2942
+ };
2943
+ });
2454
2944
  }
2455
2945
  skillDefinitions() {
2456
- return this.skills.all().map((s) => ({
2457
- id: s.name,
2458
- input_schema: toJsonSchema(s.options?.inputSchema),
2459
- tags: s.options?.tags ?? []
2460
- }));
2946
+ return this.skills.all().map((s) => {
2947
+ const tags = s.options?.tags ?? [];
2948
+ return {
2949
+ id: s.name,
2950
+ input_schema: toJsonSchema(s.options?.inputSchema),
2951
+ tags,
2952
+ proposed_tags: tags
2953
+ };
2954
+ });
2461
2955
  }
2462
2956
  discoveryPayload(deploymentType) {
2463
2957
  return {
@@ -2526,7 +3020,10 @@ var Agent = class {
2526
3020
  return result;
2527
3021
  } catch (err) {
2528
3022
  if (params.respond && params.res) {
2529
- params.res.status(500).json({ error: err?.message ?? "Execution failed" });
3023
+ const body = { error: err?.message ?? "Execution failed" };
3024
+ if (err?.responseData) body.error_details = err.responseData;
3025
+ const statusCode = err?.status >= 400 ? err.status : err?.statusCode >= 400 ? err.statusCode : 500;
3026
+ params.res.status(statusCode).json(body);
2530
3027
  return;
2531
3028
  }
2532
3029
  throw err;
@@ -2565,7 +3062,10 @@ var Agent = class {
2565
3062
  return result;
2566
3063
  } catch (err) {
2567
3064
  if (params.respond && params.res) {
2568
- params.res.status(500).json({ error: err?.message ?? "Execution failed" });
3065
+ const body = { error: err?.message ?? "Execution failed" };
3066
+ if (err?.responseData) body.error_details = err.responseData;
3067
+ const statusCode = err?.status >= 400 ? err.status : err?.statusCode >= 400 ? err.statusCode : 500;
3068
+ params.res.status(statusCode).json(body);
2569
3069
  return;
2570
3070
  }
2571
3071
  throw err;
@@ -2579,15 +3079,24 @@ var Agent = class {
2579
3079
  const port = this.config.port ?? 8001;
2580
3080
  const hostForUrl = this.config.publicUrl ? void 0 : this.config.host && this.config.host !== "0.0.0.0" ? this.config.host : "127.0.0.1";
2581
3081
  const publicUrl = this.config.publicUrl ?? `http://${hostForUrl ?? "127.0.0.1"}:${port}`;
2582
- await this.agentFieldClient.register({
3082
+ const agentTags = this.config.tags ?? [];
3083
+ const regResponse = await this.agentFieldClient.register({
2583
3084
  id: this.config.nodeId,
2584
- version: this.config.version,
3085
+ version: this.config.version ?? "",
2585
3086
  base_url: publicUrl,
2586
3087
  public_url: publicUrl,
2587
3088
  deployment_type: this.config.deploymentType ?? "long_running",
2588
3089
  reasoners,
2589
- skills
3090
+ skills,
3091
+ proposed_tags: agentTags,
3092
+ tags: agentTags
2590
3093
  });
3094
+ if (regResponse?.status === "pending_approval") {
3095
+ const pendingTags = regResponse.pending_tags ?? [];
3096
+ console.log(`[AgentField] Node ${this.config.nodeId} registered but awaiting tag approval (pending tags: ${pendingTags.join(", ")})`);
3097
+ await this.waitForApproval();
3098
+ console.log(`[AgentField] Node ${this.config.nodeId} tag approval granted`);
3099
+ }
2591
3100
  if (this.config.didEnabled) {
2592
3101
  try {
2593
3102
  const didRegistered = await this.didManager.registerAgent(reasoners, skills);
@@ -2595,6 +3104,10 @@ var Agent = class {
2595
3104
  const summary = this.didManager.getIdentitySummary();
2596
3105
  console.log(`[DID] Agent registered with DID: ${summary.agentDid}`);
2597
3106
  console.log(`[DID] Reasoner DIDs: ${summary.reasonerCount}, Skill DIDs: ${summary.skillCount}`);
3107
+ const pkg = this.didManager.getIdentityPackage();
3108
+ if (pkg?.agentDid?.did && pkg?.agentDid?.privateKeyJwk) {
3109
+ this.agentFieldClient.setDIDCredentials(pkg.agentDid.did, pkg.agentDid.privateKeyJwk);
3110
+ }
2598
3111
  }
2599
3112
  } catch (didErr) {
2600
3113
  if (!this.config.devMode) {
@@ -2609,6 +3122,27 @@ var Agent = class {
2609
3122
  console.warn("Control plane registration failed (devMode=true), continuing locally", err);
2610
3123
  }
2611
3124
  }
3125
+ async waitForApproval() {
3126
+ const pollInterval = 5e3;
3127
+ const timeoutMs = 5 * 60 * 1e3;
3128
+ const deadline = Date.now() + timeoutMs;
3129
+ while (Date.now() < deadline) {
3130
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
3131
+ try {
3132
+ const node = await this.agentFieldClient.getNode(this.config.nodeId);
3133
+ const status = node?.lifecycle_status;
3134
+ if (status && status !== "pending_approval") {
3135
+ return;
3136
+ }
3137
+ console.log(`[AgentField] Node ${this.config.nodeId} still pending approval...`);
3138
+ } catch (err) {
3139
+ console.warn("[AgentField] Polling for approval status failed:", err);
3140
+ }
3141
+ }
3142
+ throw new Error(
3143
+ `[AgentField] Node ${this.config.nodeId} approval timed out after ${timeoutMs / 1e3}s`
3144
+ );
3145
+ }
2612
3146
  startHeartbeat() {
2613
3147
  const interval = this.config.heartbeatIntervalMs ?? 3e4;
2614
3148
  if (interval <= 0) return;
@@ -2685,6 +3219,6 @@ function sanitize(value) {
2685
3219
  return value.replace(/[^0-9a-zA-Z]+/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "");
2686
3220
  }
2687
3221
 
2688
- export { AIClient, Agent, AgentRouter, DidClient, DidInterface, DidManager, ExecutionContext, MCPClient, MCPClientRegistry, MCPToolRegistrar, MemoryClient, MemoryEventClient, MemoryInterface, RateLimitError, ReasonerContext, SkillContext, StatelessRateLimiter, WorkflowReporter, getCurrentContext, getCurrentSkillContext };
3222
+ export { AIClient, Agent, AgentRouter, DIDAuthenticator, DidClient, DidInterface, DidManager, ExecutionContext, HEADER_CALLER_DID, HEADER_DID_NONCE, HEADER_DID_SIGNATURE, HEADER_DID_TIMESTAMP, MCPClient, MCPClientRegistry, MCPToolRegistrar, MemoryClient, MemoryEventClient, MemoryInterface, RateLimitError, ReasonerContext, SkillContext, StatelessRateLimiter, WorkflowReporter, getCurrentContext, getCurrentSkillContext };
2689
3223
  //# sourceMappingURL=index.js.map
2690
3224
  //# sourceMappingURL=index.js.map