@mcoda/agents 0.1.40 → 0.1.42

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.
@@ -1 +1 @@
1
- {"version":3,"file":"AgentService.d.ts","sourceRoot":"","sources":["../../src/AgentService/AgentService.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EAKpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAY7C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAyMhG,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,OAAO;gBADP,IAAI,EAAE,gBAAgB,EACtB,OAAO,GAAE,mBAAwB;WAG9B,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAKtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAUhD,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAIrE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAInD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAIpD,kBAAkB;YAMlB,kBAAkB;IA6BhC,OAAO,CAAC,kBAAkB;IAqCpB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA4C/E,OAAO,CAAC,KAAK;YAIC,OAAO;YASP,UAAU;IAQxB,OAAO,CAAC,6BAA6B;YAOvB,mBAAmB;YAgCnB,uBAAuB;YAiBvB,gBAAgB;IAM9B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,8BAA8B;IAStC,OAAO,CAAC,iBAAiB;YAOX,oBAAoB;YA6BpB,oBAAoB;YA6CpB,sBAAsB;YAkBtB,mBAAmB;YAUnB,4BAA4B;IAkCpC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAmBlD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA6H9E,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;YAwP5F,mBAAmB;YAkBnB,uBAAuB;YAwBvB,mBAAmB;CAYlC"}
1
+ {"version":3,"file":"AgentService.d.ts","sourceRoot":"","sources":["../../src/AgentService/AgentService.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,EACL,iBAAiB,EACjB,WAAW,EACX,mBAAmB,EAKpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAY7C,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAsNhG,UAAU,mBAAmB;IAC3B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,sBAAsB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAChD,0BAA0B,CAAC,EAAE,MAAM,CAAC;CACrC;AAED,qBAAa,YAAY;IAErB,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,OAAO;gBADP,IAAI,EAAE,gBAAgB,EACtB,OAAO,GAAE,mBAAwB;WAG9B,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IAKtC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAUhD,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,SAAS,CAAC;IAIrE,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAInD,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;YAIpD,kBAAkB;YAMlB,kBAAkB;IA6BhC,OAAO,CAAC,kBAAkB;IA0CpB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IA4C/E,OAAO,CAAC,KAAK;YAIC,OAAO;YASP,UAAU;IAQxB,OAAO,CAAC,6BAA6B;YAOvB,mBAAmB;YAgCnB,uBAAuB;YAiBvB,gBAAgB;IAM9B,OAAO,CAAC,MAAM;IAId,OAAO,CAAC,qBAAqB;IAI7B,OAAO,CAAC,8BAA8B;IAStC,OAAO,CAAC,iBAAiB;YAOX,oBAAoB;YA6BpB,oBAAoB;YA6CpB,sBAAsB;YAkBtB,mBAAmB;YAUnB,4BAA4B;IAkCpC,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;IAmBlD,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA6H9E,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;YAwP5F,mBAAmB;YAkBnB,uBAAuB;YAwBvB,mBAAmB;CAYlC"}
@@ -56,16 +56,27 @@ const WINDOW_RESET_FALLBACK_MS = {
56
56
  weekly: 7 * 24 * 60 * 60 * 1000,
57
57
  other: 60 * 60 * 1000,
58
58
  };
59
- const isManagedMswarmCloudAgent = (agent) => {
59
+ const getManagedMswarmKind = (agent) => {
60
60
  const config = agent.config;
61
61
  if (!config || typeof config !== "object" || Array.isArray(config)) {
62
- return false;
62
+ return undefined;
63
+ }
64
+ const record = config;
65
+ const cloud = record.mswarmCloud;
66
+ if (cloud &&
67
+ typeof cloud === "object" &&
68
+ !Array.isArray(cloud) &&
69
+ cloud.managed === true) {
70
+ return "cloud";
71
+ }
72
+ const selfHosted = record.mswarmSelfHosted;
73
+ if (selfHosted &&
74
+ typeof selfHosted === "object" &&
75
+ !Array.isArray(selfHosted) &&
76
+ selfHosted.managed === true) {
77
+ return "self-hosted";
63
78
  }
64
- const managed = config.mswarmCloud;
65
- return Boolean(managed &&
66
- typeof managed === "object" &&
67
- !Array.isArray(managed) &&
68
- managed.managed === true);
79
+ return undefined;
69
80
  };
70
81
  const isIoEnabled = () => {
71
82
  const raw = process.env[IO_ENV];
@@ -277,9 +288,13 @@ export class AgentService {
277
288
  if (adapterType.endsWith("-api")) {
278
289
  if (hasSecret)
279
290
  return adapterType;
280
- if (adapterType === "openai-api" && isManagedMswarmCloudAgent(agent)) {
291
+ const managedMswarmKind = adapterType === "openai-api" ? getManagedMswarmKind(agent) : undefined;
292
+ if (managedMswarmKind) {
281
293
  const label = agent.slug ?? agent.id;
282
- throw new Error(`AUTH_REQUIRED: Managed mswarm cloud agent ${label} is missing the synced API key; run \`mcoda config set mswarm-api-key <KEY>\` and \`mcoda cloud agent sync\`.`);
294
+ const syncCommand = managedMswarmKind === "self-hosted"
295
+ ? "mcoda self-hosted agent sync"
296
+ : "mcoda cloud agent sync";
297
+ throw new Error(`AUTH_REQUIRED: Managed mswarm ${managedMswarmKind} agent ${label} is missing the synced API key; run \`mcoda config set mswarm-api-key <KEY>\` and \`${syncCommand}\`.`);
283
298
  }
284
299
  if (adapterType === "codex-api" || adapterType === "openai-api") {
285
300
  // Default to the codex CLI when API creds are missing.
@@ -1 +1 @@
1
- {"version":3,"file":"OpenAiAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAwHtG,KAAK,YAAY,GAAG,aAAa,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,YAAW,YAAY;IAMpC,OAAO,CAAC,MAAM;IAL1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,SAAS,CAAsC;gBAEnC,MAAM,EAAE,YAAY;IAUlC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IAgFnC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwC5D,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC;IAwFhG,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;CAU7B"}
1
+ {"version":3,"file":"OpenAiAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAiKtG,KAAK,YAAY,GAAG,aAAa,GAAG;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC,CAAC;AAEF,qBAAa,aAAc,YAAW,YAAY;IAMpC,OAAO,CAAC,MAAM;IAL1B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,WAAW,CAAqB;IACxC,OAAO,CAAC,SAAS,CAAsC;gBAEnC,MAAM,EAAE,YAAY;IAUlC,eAAe,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIpC,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC;IA6GnC,MAAM,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAwC5D,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,cAAc,CAAC,gBAAgB,EAAE,IAAI,EAAE,OAAO,CAAC;IAwFhG,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,aAAa;IAIrB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,oBAAoB;CAU7B"}
@@ -1,4 +1,13 @@
1
+ import { parseUsageLimitError } from "../../AgentService/UsageLimitParser.js";
1
2
  const DEFAULT_BASE_URL = "https://api.openai.com/v1";
3
+ const MAX_RESPONSE_DETAIL_CHARS = 500;
4
+ const RATE_LIMIT_HEADER_NAMES = [
5
+ "retry-after",
6
+ "x-ratelimit-reset-after",
7
+ "x-ratelimit-reset",
8
+ "x-ratelimit-reset-at",
9
+ "x-ratelimit-remaining",
10
+ ];
2
11
  const asString = (value) => (typeof value === "string" ? value : undefined);
3
12
  const resolveString = (value) => {
4
13
  const raw = asString(value)?.trim();
@@ -11,6 +20,37 @@ const normalizeBaseUrl = (value) => {
11
20
  return undefined;
12
21
  return str.endsWith("/") ? str.slice(0, -1) : str;
13
22
  };
23
+ const buildRateLimitProbeMessage = (response, responseText) => {
24
+ const parts = [`openai_probe http ${response.status}`];
25
+ const retryAfter = response.headers.get("retry-after")?.trim();
26
+ if (retryAfter) {
27
+ const retryAfterSeconds = Number.parseInt(retryAfter, 10);
28
+ parts.push(Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0
29
+ ? `Retry after ${retryAfterSeconds} seconds`
30
+ : `Retry after ${retryAfter}`);
31
+ }
32
+ for (const headerName of RATE_LIMIT_HEADER_NAMES) {
33
+ if (headerName === "retry-after")
34
+ continue;
35
+ const headerValue = response.headers.get(headerName)?.trim();
36
+ if (headerValue) {
37
+ parts.push(`${headerName}: ${headerValue}`);
38
+ }
39
+ }
40
+ const trimmedResponse = responseText.trim();
41
+ if (trimmedResponse) {
42
+ parts.push(trimmedResponse);
43
+ }
44
+ return parts.join(". ");
45
+ };
46
+ const resolveRetryAfterMs = (resetAt, nowMs) => {
47
+ if (!resetAt)
48
+ return undefined;
49
+ const timestampMs = Date.parse(resetAt);
50
+ if (!Number.isFinite(timestampMs))
51
+ return undefined;
52
+ return Math.max(0, timestampMs - nowMs);
53
+ };
14
54
  const resolveBaseUrl = (config) => {
15
55
  const anyConfig = config;
16
56
  const agentConfig = config.agent?.config;
@@ -148,12 +188,38 @@ export class OpenAiAdapter {
148
188
  body: JSON.stringify(this.buildHealthCheckBody(model)),
149
189
  });
150
190
  const responseText = await response.text().catch(() => "");
151
- const latencyMs = Date.now() - startedAt;
191
+ const checkedAtMs = Date.now();
192
+ const lastCheckedAt = new Date(checkedAtMs).toISOString();
193
+ const latencyMs = checkedAtMs - startedAt;
152
194
  if (!response.ok) {
195
+ if (response.status === 429) {
196
+ const parsedLimit = parseUsageLimitError(new Error(buildRateLimitProbeMessage(response, responseText)), checkedAtMs);
197
+ return {
198
+ agentId: this.config.agent.id,
199
+ status: "healthy",
200
+ lastCheckedAt,
201
+ latencyMs,
202
+ details: {
203
+ adapter: "openai-api",
204
+ source: "openai_probe",
205
+ model,
206
+ baseUrl: url,
207
+ reason: "rate_limited",
208
+ transient: true,
209
+ rateLimited: true,
210
+ httpStatus: response.status,
211
+ response: responseText.slice(0, MAX_RESPONSE_DETAIL_CHARS),
212
+ resetAt: parsedLimit?.resetAt,
213
+ resetAtSource: parsedLimit?.resetAtSource,
214
+ retryAfterMs: resolveRetryAfterMs(parsedLimit?.resetAt, checkedAtMs),
215
+ windowTypes: parsedLimit?.windowTypes,
216
+ },
217
+ };
218
+ }
153
219
  return {
154
220
  agentId: this.config.agent.id,
155
- status: response.status === 429 ? "degraded" : "unreachable",
156
- lastCheckedAt: new Date().toISOString(),
221
+ status: "unreachable",
222
+ lastCheckedAt,
157
223
  latencyMs,
158
224
  details: {
159
225
  adapter: "openai-api",
@@ -162,14 +228,14 @@ export class OpenAiAdapter {
162
228
  baseUrl: url,
163
229
  reason: "http_error",
164
230
  httpStatus: response.status,
165
- response: responseText.slice(0, 500),
231
+ response: responseText.slice(0, MAX_RESPONSE_DETAIL_CHARS),
166
232
  },
167
233
  };
168
234
  }
169
235
  return {
170
236
  agentId: this.config.agent.id,
171
237
  status: "healthy",
172
- lastCheckedAt: new Date().toISOString(),
238
+ lastCheckedAt,
173
239
  latencyMs,
174
240
  details: {
175
241
  adapter: "openai-api",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcoda/agents",
3
- "version": "0.1.40",
3
+ "version": "0.1.42",
4
4
  "description": "Agent registry and capabilities for mcoda.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -30,8 +30,8 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@mcoda/shared": "0.1.40",
34
- "@mcoda/db": "0.1.40"
33
+ "@mcoda/shared": "0.1.42",
34
+ "@mcoda/db": "0.1.42"
35
35
  },
36
36
  "scripts": {
37
37
  "build": "tsc -p tsconfig.json",