@teamvibe/poller 0.8.6-rc.1 → 0.8.6-rc.2

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,7 +1,7 @@
1
1
  import type { PoolFetchDiagnostics } from './subscription-pool-client.js';
2
2
  import type { SessionLogger } from './logger.js';
3
3
  import type { ClaudeRateLimitInfo } from './claude-rate-limit.js';
4
- import { type AccountIdentity, type AccountUsageState, type PoolFetchResult, type SelectionOutcome } from './subscription-selection.js';
4
+ import { type AccountIdentity, type AccountUsageState, type PoolFetchResult, type SelectionOutcome, type UsageWindow } from './subscription-selection.js';
5
5
  export type PoolInfoLogger = Pick<SessionLogger, 'info'>;
6
6
  type LabeledAccount = AccountIdentity & {
7
7
  label?: string;
@@ -15,6 +15,7 @@ export declare function poolErrorClass(error: unknown): string;
15
15
  export declare function poolErrorDiagnostic(error: unknown): string;
16
16
  export declare function logPoolFetch(log: PoolInfoLogger, pool: PoolFetchResult & PoolFetchDiagnostics): void;
17
17
  export declare function logPoolSelection<A extends LabeledAccount>(log: PoolInfoLogger, pool: PoolFetchResult<A>, states: Readonly<Record<string, AccountUsageState>>, model: string | undefined, now: number, selected: SelectionOutcome<A>, logAllExhausted?: boolean): void;
18
+ export declare function formatPoolUsageWindows(windows: readonly UsageWindow[], includeScope?: boolean): string;
18
19
  /** Log the real store promise without changing its rejection or retry behavior. */
19
20
  export declare function logPoolUsageWrite(log: PoolInfoLogger, account: {
20
21
  label?: string;
@@ -87,9 +87,12 @@ export function logPoolSelection(log, pool, states, model, now, selected, logAll
87
87
  poolInfo(log, `selected account=${poolLabel(selected.account)} model=${JSON.stringify(model ?? 'CLI default')} score=${score}`);
88
88
  }
89
89
  }
90
+ export function formatPoolUsageWindows(windows, includeScope = true) {
91
+ return windows.map(w => `${w.window}${includeScope ? `/${w.scope}` : ''}:${w.percent === undefined ? 'unknown' : `${Number(w.percent.toFixed(2))}%`}`).join(',') || 'none';
92
+ }
90
93
  /** Log the real store promise without changing its rejection or retry behavior. */
91
94
  export async function logPoolUsageWrite(log, account, info, write, source = 'passive') {
92
- const windows = info ? normalizePassiveUsage(safeRateLimitInfo(info), Date.now()).windows.map(w => `${w.window}${source === 'passive' ? `/${w.scope}` : ''}:${w.percent === undefined ? 'unknown' : `${Number(w.percent.toFixed(2))}%`}`).join(',') : '';
95
+ const windows = formatPoolUsageWindows(info ? normalizePassiveUsage(safeRateLimitInfo(info), Date.now()).windows : [], source === 'passive');
93
96
  try {
94
97
  await write();
95
98
  poolInfo(log, `usage write account=${poolLabel(account)} result=ok windows=${windows || 'none'} source=${source}`);
@@ -93,7 +93,7 @@ function probeUnknownAccounts(accounts, selectedAccountId, resolved, store, opti
93
93
  poolInfo({ info: options.log }, `local probe account=${poolLabel(account)} result=cli_at_capacity`);
94
94
  return;
95
95
  }
96
- await runUsageProbe(account, { ...options, timeoutMs: LOCAL_PROBE_TIMEOUT_MS }, {
96
+ await runUsageProbe(account, { ...options, model: resolved.model, timeoutMs: LOCAL_PROBE_TIMEOUT_MS }, {
97
97
  channelId, signal: probe.controller.signal,
98
98
  onMeasurementComplete: () => {
99
99
  if (localProbesInFlight.get(key) === probe)
@@ -5,6 +5,7 @@ export declare const PIN_HEADROOM_SWITCH_RELATIVE = 0.25;
5
5
  export declare const USAGE_THRESHOLD_PERCENT = 90;
6
6
  /** Fallback freshness for passive windows without a reset time. */
7
7
  export declare const PASSIVE_USAGE_TTL_MS: number;
8
+ /** Fallback freshness for probe windows without a reset time. */
8
9
  export declare const PROBE_USAGE_TTL_MS = 60000;
9
10
  export declare const POOL_RECHECK_MS: number;
10
11
  /** Safety hold from the rejection observation when the provider gives no reset. */
@@ -51,7 +52,7 @@ export interface UsageObservation {
51
52
  observedAt: number;
52
53
  windows: readonly UsageWindow[];
53
54
  }
54
- /** Passive percentages remain lower bounds until reset; probes retain their short TTL. */
55
+ /** Passive and probe percentages remain lower bounds until each window resets. */
55
56
  export declare function usageWindowExpiresAt(usage: UsageObservation, window: UsageWindow): number;
56
57
  /** Latest measurements plus durable rejections; omission or success never clears a hold. */
57
58
  export declare function mergeUsageObservations(observations: readonly (UsageObservation | undefined)[], now: number): UsageObservation | undefined;
@@ -3,14 +3,15 @@ export const PIN_HEADROOM_SWITCH_RELATIVE = 0.25;
3
3
  export const USAGE_THRESHOLD_PERCENT = 90;
4
4
  /** Fallback freshness for passive windows without a reset time. */
5
5
  export const PASSIVE_USAGE_TTL_MS = 5 * 60_000;
6
+ /** Fallback freshness for probe windows without a reset time. */
6
7
  export const PROBE_USAGE_TTL_MS = 60_000;
7
8
  export const POOL_RECHECK_MS = 15 * 60_000;
8
9
  /** Safety hold from the rejection observation when the provider gives no reset. */
9
10
  export const REJECTED_BLOCK_MS = 15 * 60_000;
10
- /** Passive percentages remain lower bounds until reset; probes retain their short TTL. */
11
+ /** Passive and probe percentages remain lower bounds until each window resets. */
11
12
  export function usageWindowExpiresAt(usage, window) {
12
13
  const ttlExpiry = usage.observedAt + (usage.source === 'passive' ? PASSIVE_USAGE_TTL_MS : PROBE_USAGE_TTL_MS);
13
- return usage.source === 'passive' ? window.resetAt ?? ttlExpiry : Math.min(window.resetAt ?? ttlExpiry, ttlExpiry);
14
+ return window.resetAt ?? ttlExpiry;
14
15
  }
15
16
  /** Latest measurements plus durable rejections; omission or success never clears a hold. */
16
17
  export function mergeUsageObservations(observations, now) {
@@ -8,6 +8,8 @@ type ProbeResult = Reason | 'ok' | 'cancelled';
8
8
  export interface UsageProbeOptions {
9
9
  store: SubscriptionUsageStore;
10
10
  cliPath: string;
11
+ /** Use the session's resolved model for local probes; platform jobs default to CLI behavior. */
12
+ model?: string;
11
13
  timeoutMs?: number;
12
14
  /** Injectable child for deterministic tests; production uses node spawn. */
13
15
  spawn?: typeof spawn;
@@ -5,7 +5,7 @@ import { join } from 'node:path';
5
5
  import { normalizePassiveUsage } from './subscription-selection.js';
6
6
  import { safeRateLimitInfo, usageStateForSelection, } from './subscription-usage-store.js';
7
7
  import { parseClaudeRateLimitInfo, } from './claude-rate-limit.js';
8
- import { poolInfo, poolLabel } from './pool-logging.js';
8
+ import { formatPoolUsageWindows, poolInfo, poolLabel } from './pool-logging.js';
9
9
  export function isUsageProbe(value) {
10
10
  return (!!value &&
11
11
  typeof value === 'object' &&
@@ -84,6 +84,7 @@ export async function handleUsageProbe(job, options) {
84
84
  export async function runUsageProbe(account, options, context = {}) {
85
85
  let result = 'unknown';
86
86
  let cwd;
87
+ let measuredWindows;
87
88
  const cancelled = () => context.signal?.aborted === true;
88
89
  try {
89
90
  if (cancelled())
@@ -109,6 +110,7 @@ export async function runUsageProbe(account, options, context = {}) {
109
110
  const child = (options.spawn ?? spawn)(options.cliPath, [
110
111
  '-p',
111
112
  'Reply OK.',
113
+ ...(options.model ? ['--model', options.model] : []),
112
114
  '--output-format',
113
115
  'stream-json',
114
116
  '--verbose',
@@ -212,6 +214,7 @@ export async function runUsageProbe(account, options, context = {}) {
212
214
  ...normalizePassiveUsage(safe, now),
213
215
  source: 'probe',
214
216
  };
217
+ measuredWindows = formatPoolUsageWindows(usage.windows);
215
218
  await options.store.finishProbe(account, {
216
219
  status: 'ok',
217
220
  observedAt: now,
@@ -245,6 +248,6 @@ export async function runUsageProbe(account, options, context = {}) {
245
248
  const description = context.requestId
246
249
  ? `usage probe request=${context.requestId}`
247
250
  : `local probe account=${poolLabel(account)}`;
248
- poolInfo({ info: options.log }, `${description} result=${result}${result === 'cancelled' ? ' reason=account selected' : ''}`);
251
+ poolInfo({ info: options.log }, `${description} result=${result}${result === 'ok' && !context.requestId ? ` windows=${measuredWindows}` : ''}${result === 'cancelled' ? ' reason=account selected' : ''}`);
249
252
  }
250
253
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@teamvibe/poller",
3
- "version": "0.8.6-rc.1",
3
+ "version": "0.8.6-rc.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@teamvibe/poller",
9
- "version": "0.8.6-rc.1",
9
+ "version": "0.8.6-rc.2",
10
10
  "dependencies": {
11
11
  "@aws-sdk/client-dynamodb": "3.1108.0",
12
12
  "@aws-sdk/client-sqs": "3.1108.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamvibe/poller",
3
- "version": "0.8.6-rc.1",
3
+ "version": "0.8.6-rc.2",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "bin": {