panoma 0.1.8 → 0.1.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 (102) hide show
  1. package/app/BUILD-INFO.json +2 -2
  2. package/app/apps/web/.next-bundle/BUILD_ID +1 -1
  3. package/app/apps/web/.next-bundle/app-build-manifest.json +123 -123
  4. package/app/apps/web/.next-bundle/app-path-routes-manifest.json +30 -30
  5. package/app/apps/web/.next-bundle/build-manifest.json +2 -2
  6. package/app/apps/web/.next-bundle/prerender-manifest.json +3 -3
  7. package/app/apps/web/.next-bundle/server/app/(app)/agents/page_client-reference-manifest.js +1 -1
  8. package/app/apps/web/.next-bundle/server/app/(app)/ai/page_client-reference-manifest.js +1 -1
  9. package/app/apps/web/.next-bundle/server/app/(app)/bridge/page_client-reference-manifest.js +1 -1
  10. package/app/apps/web/.next-bundle/server/app/(app)/copies/page_client-reference-manifest.js +1 -1
  11. package/app/apps/web/.next-bundle/server/app/(app)/credentials/page_client-reference-manifest.js +1 -1
  12. package/app/apps/web/.next-bundle/server/app/(app)/disk/page_client-reference-manifest.js +1 -1
  13. package/app/apps/web/.next-bundle/server/app/(app)/hidden/page_client-reference-manifest.js +1 -1
  14. package/app/apps/web/.next-bundle/server/app/(app)/p/[slug]/page_client-reference-manifest.js +1 -1
  15. package/app/apps/web/.next-bundle/server/app/(app)/packages/page_client-reference-manifest.js +1 -1
  16. package/app/apps/web/.next-bundle/server/app/(app)/page_client-reference-manifest.js +1 -1
  17. package/app/apps/web/.next-bundle/server/app/(app)/runs/[id]/page_client-reference-manifest.js +1 -1
  18. package/app/apps/web/.next-bundle/server/app/(app)/runs/page_client-reference-manifest.js +1 -1
  19. package/app/apps/web/.next-bundle/server/app/(app)/search/page_client-reference-manifest.js +1 -1
  20. package/app/apps/web/.next-bundle/server/app/(app)/twin/look/page_client-reference-manifest.js +1 -1
  21. package/app/apps/web/.next-bundle/server/app/(app)/twin/page_client-reference-manifest.js +1 -1
  22. package/app/apps/web/.next-bundle/server/app/(app)/unsaved/page_client-reference-manifest.js +1 -1
  23. package/app/apps/web/.next-bundle/server/app/_not-found/page_client-reference-manifest.js +1 -1
  24. package/app/apps/web/.next-bundle/server/app/api/accounts/route_client-reference-manifest.js +1 -1
  25. package/app/apps/web/.next-bundle/server/app/api/agent/consult/route_client-reference-manifest.js +1 -1
  26. package/app/apps/web/.next-bundle/server/app/api/agent/context/route_client-reference-manifest.js +1 -1
  27. package/app/apps/web/.next-bundle/server/app/api/agent/hello/route_client-reference-manifest.js +1 -1
  28. package/app/apps/web/.next-bundle/server/app/api/agent/journal/route_client-reference-manifest.js +1 -1
  29. package/app/apps/web/.next-bundle/server/app/api/agent/keys/route_client-reference-manifest.js +1 -1
  30. package/app/apps/web/.next-bundle/server/app/api/agent/log/route_client-reference-manifest.js +1 -1
  31. package/app/apps/web/.next-bundle/server/app/api/agent/mcp/route_client-reference-manifest.js +1 -1
  32. package/app/apps/web/.next-bundle/server/app/api/agent/notes/route_client-reference-manifest.js +1 -1
  33. package/app/apps/web/.next-bundle/server/app/api/agent/tasks/[id]/route_client-reference-manifest.js +1 -1
  34. package/app/apps/web/.next-bundle/server/app/api/agent/tasks/route_client-reference-manifest.js +1 -1
  35. package/app/apps/web/.next-bundle/server/app/api/ai/route.js +1 -1
  36. package/app/apps/web/.next-bundle/server/app/api/ai/route_client-reference-manifest.js +1 -1
  37. package/app/apps/web/.next-bundle/server/app/api/assets/route_client-reference-manifest.js +1 -1
  38. package/app/apps/web/.next-bundle/server/app/api/assignments/launch/route_client-reference-manifest.js +1 -1
  39. package/app/apps/web/.next-bundle/server/app/api/assignments/route_client-reference-manifest.js +1 -1
  40. package/app/apps/web/.next-bundle/server/app/api/catalog/route_client-reference-manifest.js +1 -1
  41. package/app/apps/web/.next-bundle/server/app/api/check/route_client-reference-manifest.js +1 -1
  42. package/app/apps/web/.next-bundle/server/app/api/consultations/route_client-reference-manifest.js +1 -1
  43. package/app/apps/web/.next-bundle/server/app/api/describe/route_client-reference-manifest.js +1 -1
  44. package/app/apps/web/.next-bundle/server/app/api/disk/route_client-reference-manifest.js +1 -1
  45. package/app/apps/web/.next-bundle/server/app/api/enrich/route_client-reference-manifest.js +1 -1
  46. package/app/apps/web/.next-bundle/server/app/api/environment/route_client-reference-manifest.js +1 -1
  47. package/app/apps/web/.next-bundle/server/app/api/hooks/route_client-reference-manifest.js +1 -1
  48. package/app/apps/web/.next-bundle/server/app/api/ingest/route_client-reference-manifest.js +1 -1
  49. package/app/apps/web/.next-bundle/server/app/api/md/apply/route_client-reference-manifest.js +1 -1
  50. package/app/apps/web/.next-bundle/server/app/api/md/context/route_client-reference-manifest.js +1 -1
  51. package/app/apps/web/.next-bundle/server/app/api/md/inspect/route_client-reference-manifest.js +1 -1
  52. package/app/apps/web/.next-bundle/server/app/api/md/repair/route_client-reference-manifest.js +1 -1
  53. package/app/apps/web/.next-bundle/server/app/api/md/review/route_client-reference-manifest.js +1 -1
  54. package/app/apps/web/.next-bundle/server/app/api/north/route_client-reference-manifest.js +1 -1
  55. package/app/apps/web/.next-bundle/server/app/api/notes/route_client-reference-manifest.js +1 -1
  56. package/app/apps/web/.next-bundle/server/app/api/open/route_client-reference-manifest.js +1 -1
  57. package/app/apps/web/.next-bundle/server/app/api/project/route_client-reference-manifest.js +1 -1
  58. package/app/apps/web/.next-bundle/server/app/api/rescan/route_client-reference-manifest.js +1 -1
  59. package/app/apps/web/.next-bundle/server/app/api/roots/route_client-reference-manifest.js +1 -1
  60. package/app/apps/web/.next-bundle/server/app/api/runs/[id]/route_client-reference-manifest.js +1 -1
  61. package/app/apps/web/.next-bundle/server/app/api/runs/route_client-reference-manifest.js +1 -1
  62. package/app/apps/web/.next-bundle/server/app/api/scale/route_client-reference-manifest.js +1 -1
  63. package/app/apps/web/.next-bundle/server/app/api/search/route_client-reference-manifest.js +1 -1
  64. package/app/apps/web/.next-bundle/server/app/api/secrets/route_client-reference-manifest.js +1 -1
  65. package/app/apps/web/.next-bundle/server/app/api/tasks/route_client-reference-manifest.js +1 -1
  66. package/app/apps/web/.next-bundle/server/app/api/today/route_client-reference-manifest.js +1 -1
  67. package/app/apps/web/.next-bundle/server/app/api/twin/assign/route_client-reference-manifest.js +1 -1
  68. package/app/apps/web/.next-bundle/server/app/api/twin/classify/route_client-reference-manifest.js +1 -1
  69. package/app/apps/web/.next-bundle/server/app/api/twin/critique/route_client-reference-manifest.js +1 -1
  70. package/app/apps/web/.next-bundle/server/app/api/twin/design/route_client-reference-manifest.js +1 -1
  71. package/app/apps/web/.next-bundle/server/app/api/twin/distill/route_client-reference-manifest.js +1 -1
  72. package/app/apps/web/.next-bundle/server/app/api/twin/look/route_client-reference-manifest.js +1 -1
  73. package/app/apps/web/.next-bundle/server/app/api/twin/mine/route_client-reference-manifest.js +1 -1
  74. package/app/apps/web/.next-bundle/server/app/api/twin/score/route_client-reference-manifest.js +1 -1
  75. package/app/apps/web/.next-bundle/server/app/api/twin/shot/route_client-reference-manifest.js +1 -1
  76. package/app/apps/web/.next-bundle/server/app/api/twin/sources/route_client-reference-manifest.js +1 -1
  77. package/app/apps/web/.next-bundle/server/app/api/twin/synthesize/route_client-reference-manifest.js +1 -1
  78. package/app/apps/web/.next-bundle/server/app/api/twin/taste/route_client-reference-manifest.js +1 -1
  79. package/app/apps/web/.next-bundle/server/app/api/twin/verdicts/route_client-reference-manifest.js +1 -1
  80. package/app/apps/web/.next-bundle/server/app/api/watch/route_client-reference-manifest.js +1 -1
  81. package/app/apps/web/.next-bundle/server/app/icon/[id]/route_client-reference-manifest.js +1 -1
  82. package/app/apps/web/.next-bundle/server/app-paths-manifest.json +30 -30
  83. package/app/apps/web/.next-bundle/server/chunks/2930.js +1 -1
  84. package/app/apps/web/.next-bundle/server/chunks/3610.js +6 -6
  85. package/app/apps/web/.next-bundle/server/chunks/3677.js +1 -1
  86. package/app/apps/web/.next-bundle/server/chunks/7876.js +1 -1
  87. package/app/apps/web/.next-bundle/server/functions-config-manifest.json +9 -9
  88. package/app/apps/web/.next-bundle/server/middleware-manifest.json +5 -5
  89. package/app/apps/web/.next-bundle/server/pages/500.html +1 -1
  90. package/app/apps/web/.next-bundle/server/server-reference-manifest.json +1 -1
  91. package/app/apps/web/.next-bundle/static/chunks/7911-1f2ce7063b83e142.js +1 -0
  92. package/app/node_modules/@panoma/ai/dist/index.d.ts +100 -1
  93. package/app/node_modules/@panoma/ai/dist/index.js +118 -52
  94. package/dist/{ai-command-2LSCDGUE.js → ai-command-W6FV3QW5.js} +2 -2
  95. package/dist/{chunk-RC6OXHI6.js → chunk-ROPR3LUO.js} +116 -52
  96. package/dist/{dist-ON3THUVO.js → dist-XTIHMRQM.js} +5 -1
  97. package/dist/index.js +1 -1
  98. package/npm-shrinkwrap.json +2 -2
  99. package/package.json +1 -1
  100. package/app/apps/web/.next-bundle/static/chunks/7911-1730eba8c196e698.js +0 -1
  101. /package/app/apps/web/.next-bundle/static/{IoWYBo4jj6B8WTfswm-wr → GWFC4d9IgsD38wa3aUlsQ}/_buildManifest.js +0 -0
  102. /package/app/apps/web/.next-bundle/static/{IoWYBo4jj6B8WTfswm-wr → GWFC4d9IgsD38wa3aUlsQ}/_ssgManifest.js +0 -0
@@ -1,3 +1,102 @@
1
+ /**
2
+ * What went wrong, said once and readable by both audiences.
3
+ *
4
+ * This package threw sentences in fixed Spanish, and both of its readers were wrong ones. The
5
+ * terminal prints `error.message` raw and is a machine surface, where the house rule is English.
6
+ * The browser is bilingual and got Spanish whichever language it was showing. `model-errors.ts`
7
+ * had already patched the two worst cases, and its own comment admits how: one by a typed error,
8
+ * the other by matching a Spanish substring — a sentence used as a key, which stops working the
9
+ * day somebody rewords it and says nothing.
10
+ *
11
+ * So the failure travels as data. `message` is English, because that is what a machine reads and
12
+ * what the CLI prints; `failure` carries the code and its parts, so the web can say the same thing
13
+ * in the language in front of it without parsing prose.
14
+ *
15
+ * What is NOT here: anything a provider said. A 429, a refusal, the text of somebody else's API —
16
+ * those are quotes, and translating a quote is inventing one. They travel inside `detail`, whole.
17
+ */
18
+ type AiFailure = {
19
+ code: "noCommand";
20
+ provider: string;
21
+ } | {
22
+ code: "launchFailed";
23
+ command: string;
24
+ reason: string;
25
+ } | {
26
+ code: "exited";
27
+ provider: string;
28
+ status: number;
29
+ output: string;
30
+ } | {
31
+ code: "emptyBody";
32
+ provider: string;
33
+ } | {
34
+ code: "providerRefused";
35
+ provider: string;
36
+ status: number;
37
+ detail: string;
38
+ } | {
39
+ code: "tokenRefused";
40
+ provider: string;
41
+ status: number;
42
+ detail: string;
43
+ } | {
44
+ code: "neverAnswered";
45
+ provider: string;
46
+ last: string;
47
+ attempts: number;
48
+ } | {
49
+ code: "configShape";
50
+ } | {
51
+ code: "configLocked";
52
+ lock: string;
53
+ holder: string | undefined;
54
+ } | {
55
+ code: "noProvider";
56
+ } | {
57
+ code: "unknownProvider";
58
+ id: string;
59
+ } | {
60
+ code: "oauthTimeout";
61
+ } | {
62
+ code: "noOauth";
63
+ provider: string;
64
+ } | {
65
+ code: "badUrl";
66
+ provider: string;
67
+ value: string;
68
+ where: string;
69
+ } | {
70
+ code: "notHttp";
71
+ provider: string;
72
+ } | {
73
+ code: "urlHasCredentials";
74
+ provider: string;
75
+ } | {
76
+ code: "insecureHost";
77
+ provider: string;
78
+ host: string;
79
+ };
80
+ /**
81
+ * The English sentence for a failure, written in one place.
82
+ *
83
+ * One place because two would drift: the whole point of carrying the code is that the message and
84
+ * the translation cannot disagree about what happened.
85
+ */
86
+ declare function failureMessage(failure: AiFailure): string;
87
+ /**
88
+ * An error this package raised about itself.
89
+ *
90
+ * `instanceof` rather than a string check, so that whoever translates it is reading a type and not
91
+ * guessing at prose. The two typed errors that already existed —`NoCredentialError` and
92
+ * `ConfigCorruptError`— keep their own classes: they carry more than a code and were already
93
+ * being read that way.
94
+ */
95
+ declare class AiError extends Error {
96
+ readonly failure: AiFailure;
97
+ constructor(failure: AiFailure);
98
+ }
99
+
1
100
  /**
2
101
  * AI suppliers catalog.
3
102
  *
@@ -487,4 +586,4 @@ interface CompleteResult {
487
586
  }
488
587
  declare function complete(request: CompleteRequest): Promise<CompleteResult>;
489
588
 
490
- export { ATTEMPTS, type AgentAvailability, type AiConfig, type ApiFamily, type AuthType, type CallOptions, type Challenge, type CompleteImage, type CompleteRequest, type CompleteResult, ConfigCorruptError, NoCredentialError, type OauthConfig, type OauthToken, PROVIDERS, type Provider, REDACTED, type ResolvedCredential, VisionUnsupportedError, accountFrom, authorizeUrl, awaitCallback, callProvider, checkBaseUrl, complete, completeWithCliAgent, configPath, createChallenge, detectCliAgents, exchangeCode, expired, findProvider, forgetCredential, listModels, maskKey, pointsElsewhere, providersByAuth, readConfig, redact, redirectUri, refresh, resolveCredential, saveKey, saveToken, transportFailure, updateConfig, writeConfig };
589
+ export { ATTEMPTS, type AgentAvailability, type AiConfig, AiError, type AiFailure, type ApiFamily, type AuthType, type CallOptions, type Challenge, type CompleteImage, type CompleteRequest, type CompleteResult, ConfigCorruptError, NoCredentialError, type OauthConfig, type OauthToken, PROVIDERS, type Provider, REDACTED, type ResolvedCredential, VisionUnsupportedError, accountFrom, authorizeUrl, awaitCallback, callProvider, checkBaseUrl, complete, completeWithCliAgent, configPath, createChallenge, detectCliAgents, exchangeCode, expired, failureMessage, findProvider, forgetCredential, listModels, maskKey, pointsElsewhere, providersByAuth, readConfig, redact, redirectUri, refresh, resolveCredential, saveKey, saveToken, transportFailure, updateConfig, writeConfig };
@@ -1,3 +1,53 @@
1
+ // src/failures.ts
2
+ function failureMessage(failure) {
3
+ switch (failure.code) {
4
+ case "noCommand":
5
+ return `${failure.provider} declares no command to run.`;
6
+ case "launchFailed":
7
+ return `Could not launch ${failure.command}: ${failure.reason}`;
8
+ case "exited":
9
+ return `${failure.provider} exited with code ${failure.status}. ${failure.output}`.trim();
10
+ case "emptyBody":
11
+ return `${failure.provider} answered with no body.`;
12
+ case "providerRefused":
13
+ return `${failure.provider} answered ${failure.status}: ${failure.detail}`;
14
+ case "tokenRefused":
15
+ return `${failure.provider} refused the token request (${failure.status}): ${failure.detail}`;
16
+ case "neverAnswered":
17
+ return `${failure.provider} never answered: ${failure.last}. Attempts: ${failure.attempts}. The model did not say no \u2014 the request never left this machine.`;
18
+ case "configShape":
19
+ return "the contents are not shaped like a panoma configuration";
20
+ case "configLocked":
21
+ return `Another panoma process is writing the configuration${failure.holder ? ` (${failure.holder})` : ""}.
22
+ If none is running, the lock was left behind by an earlier run:
23
+ rm ${failure.lock}`;
24
+ case "noProvider":
25
+ return "No AI provider is configured. Run 'panoma ai' to see the options.";
26
+ case "unknownProvider":
27
+ return `Unknown provider: ${failure.id}`;
28
+ case "oauthTimeout":
29
+ return "Timed out waiting for you to come back from the browser.";
30
+ case "noOauth":
31
+ return `${failure.provider} does not use sign-in.`;
32
+ case "badUrl":
33
+ return `${failure.provider}'s address is not a valid URL: \xAB${failure.value}\xBB. Check ${failure.where}.`;
34
+ case "notHttp":
35
+ return `${failure.provider}'s address has to be http or https.`;
36
+ case "urlHasCredentials":
37
+ return `${failure.provider}'s address carries a user or a password inside. Take them out and use the key.`;
38
+ case "insecureHost":
39
+ return `panoma will not send ${failure.provider}'s credential unencrypted to ${failure.host}. Use https, or a server on your own machine.`;
40
+ }
41
+ }
42
+ var AiError = class extends Error {
43
+ failure;
44
+ constructor(failure) {
45
+ super(failureMessage(failure));
46
+ this.name = "AiError";
47
+ this.failure = failure;
48
+ }
49
+ };
50
+
1
51
  // src/providers.ts
2
52
  import { homedir } from "os";
3
53
  import { join } from "path";
@@ -443,7 +493,7 @@ function awaitCallback(provider, challenge) {
443
493
  const close = () => server?.close();
444
494
  const deadline = setTimeout(() => {
445
495
  close();
446
- reject(new Error("Se agot\xF3 el plazo esperando a que volvieras del navegador."));
496
+ reject(new AiError({ code: "oauthTimeout" }));
447
497
  }, DEADLINE_MS);
448
498
  const finish = (error, code) => {
449
499
  clearTimeout(deadline);
@@ -522,9 +572,13 @@ async function requestToken(provider, fields) {
522
572
  });
523
573
  const body = await response.json().catch(() => ({}));
524
574
  if (!response.ok || !body.access_token) {
525
- throw new Error(
526
- `${provider.name} rechaz\xF3 la petici\xF3n de token (${response.status}): ${body.error_description ?? body.error ?? response.statusText}`
527
- );
575
+ throw new AiError({
576
+ code: "tokenRefused",
577
+ provider: provider.name,
578
+ status: response.status,
579
+ /* The provider's own words, whole: translating somebody else's refusal invents one. */
580
+ detail: body.error_description ?? body.error ?? response.statusText
581
+ });
528
582
  }
529
583
  return {
530
584
  access: body.access_token,
@@ -551,7 +605,7 @@ function expired(token) {
551
605
  return token.expiresAt !== void 0 && Date.now() > token.expiresAt - MARGIN_MS;
552
606
  }
553
607
  function requireOauth(provider) {
554
- if (!provider.oauth) throw new Error(`${provider.name} no usa inicio de sesi\xF3n.`);
608
+ if (!provider.oauth) throw new AiError({ code: "noOauth", provider: provider.name });
555
609
  return provider.oauth;
556
610
  }
557
611
 
@@ -561,22 +615,25 @@ function checkBaseUrl(provider, value) {
561
615
  try {
562
616
  url = new URL(value);
563
617
  } catch {
564
- throw new Error(
565
- `La direcci\xF3n de ${provider.name} no es una URL v\xE1lida: \xAB${value}\xBB. Revisa ${provider.baseUrlEnvVar ?? "la configuraci\xF3n"}.`
566
- );
618
+ throw new AiError({
619
+ code: "badUrl",
620
+ provider: provider.name,
621
+ value,
622
+ where: provider.baseUrlEnvVar ?? "the configuration"
623
+ });
567
624
  }
568
625
  if (url.protocol !== "http:" && url.protocol !== "https:") {
569
- throw new Error(`La direcci\xF3n de ${provider.name} tiene que ser http o https.`);
626
+ throw new AiError({ code: "notHttp", provider: provider.name });
570
627
  }
571
628
  if (url.username || url.password) {
572
- throw new Error(
573
- `La direcci\xF3n de ${provider.name} lleva usuario o contrase\xF1a dentro. Qu\xEDtalos y usa la clave.`
574
- );
629
+ throw new AiError({ code: "urlHasCredentials", provider: provider.name });
575
630
  }
576
631
  if (url.protocol === "http:" && !isLocal(url.hostname)) {
577
- throw new Error(
578
- `Panoma no manda la credencial de ${provider.name} sin cifrar a ${url.hostname}. Usa https, o un servidor en tu propia m\xE1quina.`
579
- );
632
+ throw new AiError({
633
+ code: "insecureHost",
634
+ provider: provider.name,
635
+ host: url.hostname
636
+ });
580
637
  }
581
638
  return value;
582
639
  }
@@ -624,17 +681,21 @@ function backupPath() {
624
681
  }
625
682
  var ConfigCorruptError = class extends Error {
626
683
  constructor(path, problem, recovery) {
627
- const lines = [`No se pudo leer la configuraci\xF3n de IA en ${path}: ${problem.message}`];
684
+ const lines = [`Could not read the AI configuration at ${path}: ${problem.message}`];
628
685
  if (recovery) {
629
686
  lines.push(
630
- `Hay una copia anterior en ${recovery.path} con ${recovery.keys} clave${recovery.keys === 1 ? "" : "s"} dentro. Recup\xE9rala con:`,
687
+ /*
688
+ Both forms written whole rather than an `s` glued to the figure. The house rule: never
689
+ attach an inflected word to a digit, and this line only ever reads wrong at one.
690
+ */
691
+ recovery.keys === 1 ? `There is an earlier copy at ${recovery.path} holding one key. Recover it with:` : `There is an earlier copy at ${recovery.path} holding ${recovery.keys} keys. Recover it with:`,
631
692
  ` mv ${recovery.path} ${path}`,
632
- "No ejecutes 'panoma ai key' antes: sobrescribir\xEDa el fichero y con \xE9l lo que quede."
693
+ "Do not run 'panoma ai key' first: it would overwrite the file and whatever is left in it."
633
694
  );
634
695
  } else {
635
696
  lines.push(
636
- "No hay copia anterior. \xC1brelo y arr\xE9glalo a mano, o b\xF3rralo si prefieres empezar",
637
- "de cero \u2014 perder\xE1s las claves que hubiera guardadas dentro."
697
+ "There is no earlier copy. Open it and fix it by hand, or delete it to start over \u2014",
698
+ "you will lose whatever keys were saved inside."
638
699
  );
639
700
  }
640
701
  super(lines.join("\n"), { cause: problem });
@@ -676,7 +737,7 @@ async function readFileConfig(path) {
676
737
  }
677
738
  const parsed = JSON.parse(raw);
678
739
  if (!isConfig(parsed)) {
679
- throw new Error("el contenido no tiene la forma de una configuraci\xF3n de Panoma");
740
+ throw new AiError({ code: "configShape" });
680
741
  }
681
742
  return parsed;
682
743
  }
@@ -764,12 +825,11 @@ async function withLock(action) {
764
825
  if (error.code !== "EEXIST") throw error;
765
826
  if (Date.now() > deadline) {
766
827
  const owner = await readFile(lock, "utf8").catch(() => "");
767
- throw new Error(
768
- `Otro proceso de Panoma est\xE1 escribiendo la configuraci\xF3n${owner.trim() ? ` (pid ${owner.trim()})` : ""}.
769
- Si no hay ninguno corriendo, el cerrojo se qued\xF3 de una ejecuci\xF3n anterior:
770
- rm ${lock}`,
771
- { cause: error }
772
- );
828
+ throw new AiError({
829
+ code: "configLocked",
830
+ lock,
831
+ holder: owner.trim() ? `pid ${owner.trim()}` : void 0
832
+ });
773
833
  }
774
834
  await new Promise((resolve) => setTimeout(resolve, 50));
775
835
  }
@@ -783,8 +843,8 @@ Si no hay ninguno corriendo, el cerrojo se qued\xF3 de una ejecuci\xF3n anterior
783
843
  }
784
844
  var NoCredentialError = class extends Error {
785
845
  constructor(provider) {
786
- const how = provider.auth === "cli" ? `Instala ${provider.name} e inicia sesi\xF3n; Panoma llamar\xE1 a '${provider.command}'.` : provider.auth === "oauth" ? `Inicia sesi\xF3n en ${provider.name} desde la p\xE1gina del modelo en Panoma.` : `Ejecuta 'panoma ai key ${provider.id}' o exporta ${provider.apiKeyEnvVars?.[0]}. La clave se saca en ${provider.signupUrl}.`;
787
- super(`Sin credencial para ${provider.name}. ${how}`);
846
+ const how = provider.auth === "cli" ? `Install ${provider.name} and sign in; panoma will call '${provider.command}'.` : provider.auth === "oauth" ? `Sign in to ${provider.name} from the model page in panoma.` : `Run 'panoma ai key ${provider.id}' or export ${provider.apiKeyEnvVars?.[0]}. The key comes from ${provider.signupUrl}.`;
847
+ super(`No credential for ${provider.name}. ${how}`);
788
848
  this.provider = provider;
789
849
  this.name = "NoCredentialError";
790
850
  }
@@ -794,12 +854,10 @@ async function resolveCredential(providerId, config) {
794
854
  const settings = config ?? await readConfig();
795
855
  const id = providerId ?? settings.provider;
796
856
  if (!id) {
797
- throw new Error(
798
- "No hay proveedor de IA configurado. Ejecuta 'panoma ai' para ver las opciones."
799
- );
857
+ throw new AiError({ code: "noProvider" });
800
858
  }
801
859
  const provider = findProvider(id);
802
- if (!provider) throw new Error(`Proveedor desconocido: ${id}`);
860
+ if (!provider) throw new AiError({ code: "unknownProvider", id });
803
861
  if (provider.auth === "cli") {
804
862
  return { provider, model: settings.model ?? "", source: "agent-session" };
805
863
  }
@@ -926,7 +984,7 @@ async function detectCliAgents(providers) {
926
984
  var MAX_OUTPUT = 2e5;
927
985
  async function completeWithCliAgent(provider, prompt, options = {}) {
928
986
  const command = options.command ?? provider.command;
929
- if (!command) throw new Error(`${provider.name} no declara ning\xFAn comando.`);
987
+ if (!command) throw new AiError({ code: "noCommand", provider: provider.name });
930
988
  return new Promise((resolve, reject) => {
931
989
  const launch = resolveExecutable(command, provider.args ?? []);
932
990
  const child = spawn(launch.file, launch.args, {
@@ -946,7 +1004,7 @@ async function completeWithCliAgent(provider, prompt, options = {}) {
946
1004
  const timer = setTimeout(() => child.kill("SIGKILL"), options.timeoutMs ?? 18e4);
947
1005
  child.on("error", (error) => {
948
1006
  clearTimeout(timer);
949
- reject(new Error(`No se pudo lanzar ${command}: ${error.message}`));
1007
+ reject(new AiError({ code: "launchFailed", command, reason: error.message }));
950
1008
  });
951
1009
  child.on("close", (code) => {
952
1010
  clearTimeout(timer);
@@ -954,7 +1012,12 @@ async function completeWithCliAgent(provider, prompt, options = {}) {
954
1012
  reject(
955
1013
  new Error(
956
1014
  redact(
957
- `${provider.name} termin\xF3 con c\xF3digo ${code}. ${stderr.trim().slice(0, 600)}`.trim()
1015
+ failureMessage({
1016
+ code: "exited",
1017
+ provider: provider.name,
1018
+ status: code ?? -1,
1019
+ output: stderr.trim().slice(0, 600)
1020
+ })
958
1021
  )
959
1022
  )
960
1023
  );
@@ -1037,9 +1100,7 @@ async function callProvider(name, url, init, options = {}) {
1037
1100
  if (attempt < attempts) await wait(WAITS[Math.min(attempt - 1, WAITS.length - 1)] ?? 0);
1038
1101
  }
1039
1102
  }
1040
- throw new Error(
1041
- `${name} no lleg\xF3 a contestar: ${last}. Intentos: ${attempts}. No es que el modelo dijera que no: la petici\xF3n no sali\xF3 de esta m\xE1quina.`
1042
- );
1103
+ throw new AiError({ code: "neverAnswered", provider: name, last: String(last), attempts });
1043
1104
  }
1044
1105
  function sleep(ms) {
1045
1106
  return new Promise((resolve) => setTimeout(resolve, ms));
@@ -1049,7 +1110,7 @@ function sleep(ms) {
1049
1110
  var VisionUnsupportedError = class extends Error {
1050
1111
  constructor(provider) {
1051
1112
  super(
1052
- `${provider} no sabe recibir im\xE1genes. Configura un proveedor con clave \u2014'panoma ai key'\u2014 o p\xE1sale uno con --provider.`
1113
+ `${provider} cannot take images. Configure a provider with a key \u2014'panoma ai key'\u2014 or pass one with --provider.`
1053
1114
  );
1054
1115
  this.provider = provider;
1055
1116
  this.name = "VisionUnsupportedError";
@@ -1066,7 +1127,7 @@ async function complete(request) {
1066
1127
 
1067
1128
  ${request.prompt}` : request.prompt;
1068
1129
  const text = await completeWithCliAgent(credential.provider, prompt);
1069
- return { text, provider: credential.provider.id, model: credential.model || "sesi\xF3n" };
1130
+ return { text, provider: credential.provider.id, model: credential.model || "session" };
1070
1131
  }
1071
1132
  if (credential.provider.api === "anthropic") return completeAnthropic(credential, request);
1072
1133
  if (credential.provider.api === "codex") return completeCodex(credential, request);
@@ -1147,14 +1208,17 @@ async function completeCodex(credential, request) {
1147
1208
  });
1148
1209
  if (!response.ok) {
1149
1210
  const failure = await response.json().catch(() => ({}));
1150
- throw new Error(
1151
- redact(
1152
- `${credential.provider.name} respondi\xF3 ${response.status}: ${failure.error?.message ?? failure.detail ?? response.statusText}`,
1211
+ throw new AiError({
1212
+ code: "providerRefused",
1213
+ provider: credential.provider.name,
1214
+ status: response.status,
1215
+ detail: redact(
1216
+ String(failure.error?.message ?? failure.detail ?? response.statusText),
1153
1217
  [credential.apiKey]
1154
1218
  )
1155
- );
1219
+ });
1156
1220
  }
1157
- if (!response.body) throw new Error(`${credential.provider.name} contest\xF3 sin cuerpo.`);
1221
+ if (!response.body) throw new AiError({ code: "emptyBody", provider: credential.provider.name });
1158
1222
  const { text, final } = await readCodexStream(response.body);
1159
1223
  const outputText = (final?.output ?? []).flatMap((block) => block.content ?? []).flatMap((chunk) => chunk.text ? [chunk.text] : []).join("\n");
1160
1224
  const shortcut = Array.isArray(final?.output_text) ? final.output_text.join("\n") : final?.output_text;
@@ -1243,12 +1307,12 @@ async function completeOpenAi(credential, request) {
1243
1307
  );
1244
1308
  const body = await response.json().catch(() => ({}));
1245
1309
  if (!response.ok) {
1246
- throw new Error(
1247
- redact(
1248
- `${credential.provider.name} respondi\xF3 ${response.status}: ${body.error?.message ?? response.statusText}`,
1249
- [credential.apiKey]
1250
- )
1251
- );
1310
+ throw new AiError({
1311
+ code: "providerRefused",
1312
+ provider: credential.provider.name,
1313
+ status: response.status,
1314
+ detail: redact(String(body.error?.message ?? response.statusText), [credential.apiKey])
1315
+ });
1252
1316
  }
1253
1317
  return {
1254
1318
  text: (body.choices?.[0]?.message?.content ?? "").trim(),
@@ -1262,6 +1326,7 @@ function dataUrl(image) {
1262
1326
  }
1263
1327
  export {
1264
1328
  ATTEMPTS,
1329
+ AiError,
1265
1330
  ConfigCorruptError,
1266
1331
  NoCredentialError,
1267
1332
  PROVIDERS,
@@ -1279,6 +1344,7 @@ export {
1279
1344
  detectCliAgents,
1280
1345
  exchangeCode,
1281
1346
  expired,
1347
+ failureMessage,
1282
1348
  findProvider,
1283
1349
  forgetCredential,
1284
1350
  listModels,
@@ -10,7 +10,7 @@ import {
10
10
  readConfig,
11
11
  resolveCredential,
12
12
  updateConfig
13
- } from "./chunk-RC6OXHI6.js";
13
+ } from "./chunk-ROPR3LUO.js";
14
14
  import {
15
15
  say
16
16
  } from "./chunk-FN63LYZH.js";
@@ -154,7 +154,7 @@ ${say("ai.keyEmpty")}
154
154
  `));
155
155
  return 1;
156
156
  }
157
- const { saveKey } = await import("./dist-ON3THUVO.js");
157
+ const { saveKey } = await import("./dist-XTIHMRQM.js");
158
158
  await saveKey(provider.id, key);
159
159
  process.stdout.write(
160
160
  `