@mnemom/mnemom 0.17.9-next.0 → 0.17.10

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.
@@ -21,8 +21,8 @@
21
21
  * consumes and tolerate everything else (`[key: string]: unknown`) so a manifest
22
22
  * bump that adds fields never breaks the CLI.
23
23
  */
24
- import { MnemomApiError, readApiError } from "./api.js";
25
- import { getApiUrl } from "./config.js";
24
+ import { MnemomApiError, normalizeApiError, readApiError } from "./api.js";
25
+ import { getApiUrl, getWebsiteUrl } from "./config.js";
26
26
  // ── resolve (zero-auth; the token IS the credential) ─────────────────────────
27
27
  const TOKEN_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}$/;
28
28
  /** True iff `token` is shaped like a try-me token (tryme_… / a bare slug). */
@@ -212,7 +212,28 @@ export async function birthAgent(manifest, name, opts = {}) {
212
212
  const agentId = response.headers.get(manifest.gateway.agent_header.toLowerCase());
213
213
  if (!response.ok && !agentId) {
214
214
  const text = await response.text().catch(() => "");
215
- throw new MnemomApiError(response.status, `Birth call failed (${response.status}) and no agent id was minted: ${text.slice(0, 300)}`);
215
+ let raw;
216
+ try {
217
+ raw = JSON.parse(text);
218
+ }
219
+ catch {
220
+ raw = null;
221
+ }
222
+ const parsed = normalizeApiError(raw);
223
+ const outOfMu = describeOutOfMu({
224
+ status: response.status,
225
+ code: parsed.code,
226
+ message: parsed.message,
227
+ details: parsed.details,
228
+ });
229
+ if (outOfMu) {
230
+ throw new MnemomApiError(response.status, outOfMu, {
231
+ code: parsed.code,
232
+ details: parsed.details,
233
+ });
234
+ }
235
+ throw new MnemomApiError(response.status, `Birth call failed (${response.status}) and no agent id was minted: ` +
236
+ (parsed.message ?? text.slice(0, 300)), { code: parsed.code, details: parsed.details });
216
237
  }
217
238
  if (!agentId || !/^mnm-/.test(agentId)) {
218
239
  throw new Error(`Birth succeeded (${response.status}) but the gateway returned no mnm- agent id on the ` +
@@ -220,6 +241,52 @@ export async function birthAgent(manifest, name, opts = {}) {
220
241
  }
221
242
  return { agentId, status: response.status };
222
243
  }
244
+ /**
245
+ * Error codes that mean "Dojo play needs µ": the gateway's 402 `billing_error`
246
+ * (balance_depleted / dojo_billing_account_required) and mnemom-api's Dojo and
247
+ * card-publish balance refusals.
248
+ */
249
+ const OUT_OF_MU_CODES = new Set([
250
+ "billing_error",
251
+ "balance_depleted",
252
+ "balance_required",
253
+ "dojo_balance_depleted",
254
+ "dojo_billing_unconfigured",
255
+ ]);
256
+ /**
257
+ * Where to recharge: the server's `details.top_up_url` (absolute, or a path on
258
+ * the website), else the website's billing page.
259
+ */
260
+ export function rechargeUrl(details, websiteBase = getWebsiteUrl()) {
261
+ const base = websiteBase.replace(/\/$/, "");
262
+ const fromServer = details && typeof details === "object"
263
+ ? details.top_up_url
264
+ : undefined;
265
+ if (typeof fromServer === "string") {
266
+ if (/^https?:\/\//.test(fromServer))
267
+ return fromServer;
268
+ if (fromServer.startsWith("/"))
269
+ return `${base}${fromServer}`;
270
+ }
271
+ return `${base}/settings/billing`;
272
+ }
273
+ /**
274
+ * The human message for a refusal that means the org is out of µ (or has no
275
+ * billing account): what happened, where to recharge, and how to resume.
276
+ * Undefined for any other refusal.
277
+ */
278
+ export function describeOutOfMu(refusal, websiteBase = getWebsiteUrl()) {
279
+ const isOutOfMu = refusal.status === 402 || (refusal.code !== undefined && OUT_OF_MU_CODES.has(refusal.code));
280
+ if (!isOutOfMu)
281
+ return undefined;
282
+ const url = rechargeUrl(refusal.details, websiteBase);
283
+ const said = refusal.message?.trim();
284
+ const lead = said || "Your organization's µ balance is used up.";
285
+ const withUrl = lead.includes(url) ? lead : `${lead} Recharge at ${url}.`;
286
+ const headline = /^out of µ/i.test(withUrl) ? withUrl : `Out of µ — ${withUrl}`;
287
+ return (`${headline} Dojo play is charged to your organization's µ balance; ` +
288
+ "once it is recharged, re-run this command to keep going.");
289
+ }
223
290
  /**
224
291
  * Read the PUBLIC (no-auth) agent projection used to detect the human's claim.
225
292
  * Never throws — a transient 404/5xx (the agent not yet queryable, or a blip)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mnemom/mnemom",
3
- "version": "0.17.9-next.0",
3
+ "version": "0.17.10",
4
4
  "description": "Transparent AI agent tracing",
5
5
  "type": "module",
6
6
  "bin": {