@teamvibe/poller 0.8.7 → 0.8.9-rc.1
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/README.md +5 -0
- package/bin/tv-git-credential.mjs +11 -6
- package/dist/brain-manager.js +4 -4
- package/dist/claude-spawner.d.ts +4 -2
- package/dist/claude-spawner.js +50 -16
- package/dist/config.d.ts +4 -0
- package/dist/config.js +1 -0
- package/dist/connections-client.d.ts +16 -4
- package/dist/connections-client.js +42 -11
- package/dist/end-of-turn-sentinel.d.ts +2 -0
- package/dist/end-of-turn-sentinel.js +2 -0
- package/dist/git-credential-bindings.d.ts +2 -0
- package/dist/git-credential-bindings.js +4 -0
- package/dist/git-credential-env.d.ts +2 -2
- package/dist/git-credential-env.js +22 -9
- package/dist/git-credential-session.d.ts +6 -6
- package/dist/git-credential-session.js +24 -4
- package/dist/inbox-watcher.d.ts +2 -2
- package/dist/inbox-watcher.js +20 -13
- package/dist/poller.d.ts +4 -3
- package/dist/poller.js +63 -11
- package/dist/redact-text.js +1 -1
- package/dist/slack-client.d.ts +3 -1
- package/dist/slack-client.js +84 -12
- package/dist/subscription-runtime.d.ts +2 -0
- package/dist/subscription-runtime.js +6 -2
- package/dist/subscription-selection.d.ts +21 -2
- package/dist/subscription-selection.js +91 -14
- package/dist/subscription-usage-store.d.ts +6 -1
- package/dist/subscription-usage-store.js +246 -13
- package/dist/thread-routing.d.ts +7 -0
- package/dist/thread-routing.js +7 -0
- package/dist/types.d.ts +2 -0
- package/dist/usage-limit-messages.d.ts +3 -0
- package/dist/usage-limit-messages.js +22 -0
- package/dist/usage-probe-job.js +31 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { USAGE_THRESHOLD_PERCENT } from './subscription-selection.js';
|
|
2
2
|
/** Owner-facing account policy location. */
|
|
3
3
|
export const FULL_QUOTA_SETTINGS_PATH = 'Connections (account menu → Use until exhausted)';
|
|
4
|
+
/** Quota notices include a calendar date and zone across multi-day holds. */
|
|
5
|
+
function formatQuotaTime(at, timeZone) {
|
|
6
|
+
try {
|
|
7
|
+
const parts = new Intl.DateTimeFormat('en-GB', { timeZone: timeZone || 'UTC', weekday: 'short', day: '2-digit', month: 'short', hour: '2-digit', minute: '2-digit', hourCycle: 'h23', timeZoneName: 'short' }).formatToParts(at);
|
|
8
|
+
const part = (type) => parts.find(p => p.type === type)?.value;
|
|
9
|
+
return `${part('weekday')} ${part('day')} ${part('month')} ${part('hour')}:${part('minute')} ${part('timeZoneName')}`;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return formatQuotaTime(at, 'UTC');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
4
15
|
export function formatUsageReset(input) {
|
|
5
16
|
if (input.resetAt === undefined || !Number.isFinite(input.resetAt))
|
|
6
17
|
return undefined;
|
|
@@ -43,6 +54,17 @@ export function formatPoolUsageLimitMessage(input) {
|
|
|
43
54
|
const labels = headroom.filter(a => a.label && a.remaining > 0).map(a => `${a.label} ${Number(a.remaining.toFixed(2))} %`).join(', ');
|
|
44
55
|
return `⏳ All Claude accounts in this workspace are above ${USAGE_THRESHOLD_PERCENT} % of their usage limit, so I'm waiting for the next reset${reset ? ` at ${reset}` : ''}. Your message is saved and will run automatically.\n💡 There is still some capacity left${labels ? ` (${labels})` : ''}. A workspace owner can allow the full quota per account in ${FULL_QUOTA_SETTINGS_PATH}.`;
|
|
45
56
|
}
|
|
57
|
+
if (input.blockedWindows?.length) {
|
|
58
|
+
const windows = [...new Map(input.blockedWindows.map(w => [`${w.window}:${w.scope}`, w])).values()];
|
|
59
|
+
const names = windows.map(w => `${w.scope === 'global' ? 'global' : w.scope === 'uncertain' ? 'Claude' : w.scope[0].toUpperCase() + w.scope.slice(1)} ${w.window === 'seven_day' ? 'weekly' : 'five-hour'} quota`);
|
|
60
|
+
const global = windows.some(w => w.scope === 'global' || w.scope === 'uncertain');
|
|
61
|
+
const resets = input.resetAt !== undefined ? `resets ${formatQuotaTime(input.resetAt, input.timeZone)}` : 'reset time unavailable';
|
|
62
|
+
const retry = input.eligibleAt !== undefined ? `I'll retry automatically at ${formatQuotaTime(input.eligibleAt, input.timeZone)}` : "I'll retry automatically after the next reset";
|
|
63
|
+
const blocked = windows.length === 1 && !global
|
|
64
|
+
? `${names[0]} is exhausted on every account in this workspace (${resets}).`
|
|
65
|
+
: `All Claude accounts in this workspace have hit their usage limit (${names.join(', ')}; ${resets}).`;
|
|
66
|
+
return `⏳ ${blocked} Your message is saved. ${retry}${global ? '.' : '; other models keep working.'}`;
|
|
67
|
+
}
|
|
46
68
|
return `⏳ All Claude accounts in this workspace have hit their usage limit. Your message is saved and will run automatically after ${reset ? `the earliest reset at ${reset}` : 'the next reset'}.`;
|
|
47
69
|
}
|
|
48
70
|
export function formatLegacyUsageLimitMessage(input) {
|
package/dist/usage-probe-job.js
CHANGED
|
@@ -2,7 +2,7 @@ import { spawn } from 'node:child_process';
|
|
|
2
2
|
import { mkdtemp, rm, mkdir } from 'node:fs/promises';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { selectSubscriptionAccount, normalizePassiveUsage } from './subscription-selection.js';
|
|
5
|
+
import { selectSubscriptionAccount, normalizePassiveUsage, reactiveUsageWindow } from './subscription-selection.js';
|
|
6
6
|
import { safeRateLimitInfo, usageStateForSelection, } from './subscription-usage-store.js';
|
|
7
7
|
import { parseClaudeRateLimitInfo, } from './claude-rate-limit.js';
|
|
8
8
|
import { formatPoolUsageWindows, poolInfo, poolLabel } from './pool-logging.js';
|
|
@@ -210,18 +210,46 @@ export async function runUsageProbe(account, options, context = {}) {
|
|
|
210
210
|
return result;
|
|
211
211
|
const now = Date.now();
|
|
212
212
|
const safe = safeRateLimitInfo(info);
|
|
213
|
+
const normalized = normalizePassiveUsage(safe, now);
|
|
213
214
|
const usage = {
|
|
214
|
-
...
|
|
215
|
+
...normalized,
|
|
215
216
|
source: 'probe',
|
|
217
|
+
windows: normalized.windows.map(w => ({ ...w,
|
|
218
|
+
...(w.status === undefined && w.percent !== undefined && (safe.status === 'allowed' || safe.status === 'allowed_warning') ? { status: safe.status } : {}),
|
|
219
|
+
})),
|
|
216
220
|
};
|
|
217
221
|
measuredWindows = formatPoolUsageWindows(usage.windows);
|
|
222
|
+
const before = usageStateForSelection(await options.store.read(account), now);
|
|
218
223
|
await options.store.finishProbe(account, {
|
|
219
224
|
status: 'ok',
|
|
220
225
|
observedAt: now,
|
|
221
226
|
usage,
|
|
222
227
|
});
|
|
223
228
|
const stored = await options.store.read(account);
|
|
224
|
-
|
|
229
|
+
const after = usageStateForSelection(stored, now);
|
|
230
|
+
// Keep the combined comparison, and compare each rejected scope/window
|
|
231
|
+
// independently so an unrelated, later hold cannot hide an improvement.
|
|
232
|
+
const pairs = new Map();
|
|
233
|
+
for (const state of [before, after])
|
|
234
|
+
for (const w of [...(state.usage?.windows ?? []), ...(state.reactive ?? []).map(reactiveUsageWindow)]) {
|
|
235
|
+
if (w.status === 'rejected')
|
|
236
|
+
pairs.set(`${w.scope}.${w.window}`, w);
|
|
237
|
+
}
|
|
238
|
+
const eligibility = (state, pair) => {
|
|
239
|
+
const matches = (w) => !pair || w.scope === pair.scope && w.window === pair.window;
|
|
240
|
+
const scoped = pair ? {
|
|
241
|
+
usage: state.usage ? { ...state.usage, windows: state.usage.windows.filter(matches) } : undefined,
|
|
242
|
+
reactive: state.reactive?.filter(r => matches(reactiveUsageWindow(r))),
|
|
243
|
+
} : state;
|
|
244
|
+
return selectSubscriptionAccount({ pool: { kind: 'ready', accounts: [account] }, states: { [account.accountId]: scoped }, model: pair?.scope, now });
|
|
245
|
+
};
|
|
246
|
+
const improved = [undefined, ...pairs.values()].some(pair => {
|
|
247
|
+
const previous = eligibility(before, pair);
|
|
248
|
+
const current = eligibility(after, pair);
|
|
249
|
+
return current.kind === 'selected' && (!pair || previous.kind === 'deferred') ||
|
|
250
|
+
current.kind === 'deferred' && previous.kind === 'deferred' && current.eligibleAt < previous.eligibleAt;
|
|
251
|
+
});
|
|
252
|
+
if (improved) {
|
|
225
253
|
try {
|
|
226
254
|
options.onHeadroom?.(account.workspaceId);
|
|
227
255
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@teamvibe/poller",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9-rc.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@teamvibe/poller",
|
|
9
|
-
"version": "0.8.
|
|
9
|
+
"version": "0.8.9-rc.1",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@aws-sdk/client-dynamodb": "3.1108.0",
|
|
12
12
|
"@aws-sdk/client-sqs": "3.1108.0",
|