@axplusb/kepler 2.3.2 → 2.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axplusb/kepler",
3
- "version": "2.3.2",
3
+ "version": "2.3.3",
4
4
  "description": "Kepler — AI coding agent with operating brief, preflight planning, and sub-agents. SWE-bench Lite evaluated.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -221,6 +221,7 @@ export function formatAgentErrorGuidance(data = {}) {
221
221
  const providerMeta = provider || compact(data.provider || data.gateway || data.gateway_type);
222
222
  const taskId = compact(data.task_id);
223
223
  const retryAfter = data.retry_after != null ? compact(data.retry_after) : '';
224
+ const retryLabel = retryAfter ? formatRetryAfterLabel(retryAfter) : '';
224
225
  const retryable = data.retryable === true;
225
226
 
226
227
  if (isBedrockMissingCredentials(data)) {
@@ -237,7 +238,16 @@ export function formatAgentErrorGuidance(data = {}) {
237
238
 
238
239
  const lines = [];
239
240
  const providerGuidance = PROVIDER_GUIDANCE[provider];
240
- if (phase === 'gateway' || code.includes('gateway')) {
241
+ if (code === 'credit_balance_exhausted') {
242
+ lines.push('Add credits or upgrade your plan to continue using platform-hosted models.');
243
+ lines.push('If you have your own provider key, switch to BYOK in Settings to keep working without Kepler credit charges.');
244
+ if (data.pricing_url) lines.push(`Open ${data.pricing_url} to top up or compare plans.`);
245
+ } else if (code === 'message_limit_reached') {
246
+ const retry = retryLabel ? ` Wait ${retryLabel} for the rolling window to recover.` : ' Wait for the rolling message window to reset.';
247
+ lines.push(`You reached the message limit for this plan.${retry}`);
248
+ lines.push('Upgrade your plan for a larger 5-hour message window, or switch to BYOK if available.');
249
+ if (data.pricing_url) lines.push(`Open ${data.pricing_url} to upgrade.`);
250
+ } else if (phase === 'gateway' || code.includes('gateway')) {
241
251
  const label = providerGuidance?.label || 'provider';
242
252
  lines.push(`The ${label} gateway failed before the agent could respond.`);
243
253
  if (providerGuidance) {
@@ -266,6 +276,17 @@ export function formatAgentErrorGuidance(data = {}) {
266
276
  };
267
277
  }
268
278
 
279
+ function formatRetryAfterLabel(value) {
280
+ const secs = Math.max(0, Math.ceil(Number(value) || 0));
281
+ if (!secs) return '';
282
+ const hours = Math.floor(secs / 3600);
283
+ const minutes = Math.ceil((secs % 3600) / 60);
284
+ if (hours > 0 && minutes > 0) return `${hours}h ${minutes}m`;
285
+ if (hours > 0) return `${hours}h`;
286
+ if (minutes > 0) return `${minutes}m`;
287
+ return `${secs}s`;
288
+ }
289
+
269
290
  function normalizeProviderToken(value) {
270
291
  const raw = lower(value);
271
292
  if (!raw) return '';
@@ -83,13 +83,28 @@ export function lowWindowStatus(rateLimit) {
83
83
  }
84
84
 
85
85
  export function rateLimitErrorMessage(payload, fallback = 'Message limit reached.') {
86
- const detail = payload?.detail && typeof payload.detail === 'object' ? payload.detail : payload;
86
+ const detail = quotaErrorDetail(payload);
87
87
  const retryAfter = detail?.retry_after ?? detail?.rate_limit?.retry_after ?? detail?.rate_limit?.retry_after_seconds;
88
+ if (detail?.code === 'credit_balance_exhausted') {
89
+ return detail.message || 'Credit balance exhausted — add credits, upgrade your plan, or switch to BYOK in Settings.';
90
+ }
91
+ if (detail?.code === 'message_limit_reached') {
92
+ if (detail.message) return detail.message;
93
+ if (retryAfter != null) return `Message window exhausted — try again in ${formatRetryAfter(retryAfter)}, or upgrade your plan.`;
94
+ return 'Message window exhausted — wait for the window to reset or upgrade your plan.';
95
+ }
88
96
  if (detail?.message) return detail.message;
89
- if (retryAfter != null) return `Message limit reached — try again in ${formatRetryAfter(retryAfter)}.`;
97
+ if (retryAfter != null) return `Message window exhausted — try again in ${formatRetryAfter(retryAfter)}, or upgrade your plan.`;
90
98
  return fallback;
91
99
  }
92
100
 
101
+ export function quotaErrorDetail(payload) {
102
+ if (!payload || typeof payload !== 'object') return {};
103
+ if (typeof payload.detail === 'string') return { message: payload.detail };
104
+ if (payload.detail && typeof payload.detail === 'object') return payload.detail;
105
+ return payload;
106
+ }
107
+
93
108
  function numberOrNull(value) {
94
109
  if (value == null) return null;
95
110
  const n = Number(value);
@@ -11,7 +11,7 @@
11
11
 
12
12
  import { sendCallback, sendSkippedCallback, sendApprovalDecision } from './callback-client.mjs';
13
13
  import { ApprovalManager } from './approval.mjs';
14
- import { rateLimitErrorMessage } from './rate-limit-display.mjs';
14
+ import { quotaErrorDetail, rateLimitErrorMessage } from './rate-limit-display.mjs';
15
15
 
16
16
  export const EVENT_TYPES = Object.freeze({
17
17
  // Phase 1 — handled
@@ -134,7 +134,7 @@ export class TarangStreamClient {
134
134
  } catch {
135
135
  payload = { detail: { message: text } };
136
136
  }
137
- const detail = payload?.detail && typeof payload.detail === 'object' ? payload.detail : payload;
137
+ const detail = quotaErrorDetail(payload);
138
138
  yield {
139
139
  type: EVENT_TYPES.ERROR,
140
140
  data: {
@@ -142,6 +142,8 @@ export class TarangStreamClient {
142
142
  code: detail?.code || 'rate_limited',
143
143
  retry_after: detail?.retry_after ?? detail?.rate_limit?.retry_after,
144
144
  rate_limit: detail?.rate_limit || null,
145
+ action: detail?.action || null,
146
+ pricing_url: detail?.pricing_url || null,
145
147
  fatal: true,
146
148
  },
147
149
  };