fix(runtime): normalize provider cooldown state path
Provider cooldown persistence now follows AGENT_STATUS_DIR, then legacy CLAWDIE_VAR_DIR, and otherwise defaults to repo-local tmp/state instead of ~/.clawdie/state. Updates docs to match the live behavior. --- Build: pass | Tests: pass — 28 passed (2 files) --- Build: pass | Tests: pass — Tests 1961 passed (1961)
This commit is contained in:
parent
32e671c802
commit
6983415357
3 changed files with 62 additions and 14 deletions
|
|
@ -88,18 +88,14 @@ one more cost surface to monitor.
|
|||
4. The cooldown auto-expires at the reset timestamp. Next run uses the primary
|
||||
again.
|
||||
|
||||
The cooldown file lives at `$CLAWDIE_VAR_DIR/provider-cooldowns.json` (default
|
||||
`$HOME/.clawdie/state/provider-cooldowns.json`). Expired entries are dropped
|
||||
on load.
|
||||
The cooldown file now resolves with the same project-local precedence used by
|
||||
other operator artifacts:
|
||||
|
||||
> **Path convention note.** The cooldown file currently uses the legacy
|
||||
> `$CLAWDIE_VAR_DIR` / `$HOME/.clawdie/state/` resolution. The newer
|
||||
> [test/build status files](./structured-reports/#test-build-pipeline)
|
||||
> moved to repo-local `tmp/` to align with `AGENTS.md` § "Temporary File
|
||||
> Storage". A future code change should harmonize provider-fallback to the
|
||||
> same precedence (`AGENT_STATUS_DIR` → `CLAWDIE_VAR_DIR` → `tmp/state/`).
|
||||
> Until then, if you set `AGENT_STATUS_DIR`, also set `CLAWDIE_VAR_DIR` to
|
||||
> the same path so both subsystems agree.
|
||||
- `AGENT_STATUS_DIR/provider-cooldowns.json`
|
||||
- otherwise `CLAWDIE_VAR_DIR/provider-cooldowns.json` (legacy compatibility)
|
||||
- otherwise repo-local `tmp/state/provider-cooldowns.json`
|
||||
|
||||
Expired entries are dropped on load.
|
||||
|
||||
## Inspecting State
|
||||
|
||||
|
|
|
|||
|
|
@ -20,6 +20,59 @@ beforeEach(() => {
|
|||
resetProviderCooldownsForTests();
|
||||
});
|
||||
|
||||
describe('default persistence path', () => {
|
||||
const previousAgentStatusDir = process.env.AGENT_STATUS_DIR;
|
||||
const previousLegacyVarDir = process.env.CLAWDIE_VAR_DIR;
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.AGENT_STATUS_DIR;
|
||||
delete process.env.CLAWDIE_VAR_DIR;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (previousAgentStatusDir === undefined) delete process.env.AGENT_STATUS_DIR;
|
||||
else process.env.AGENT_STATUS_DIR = previousAgentStatusDir;
|
||||
if (previousLegacyVarDir === undefined) delete process.env.CLAWDIE_VAR_DIR;
|
||||
else process.env.CLAWDIE_VAR_DIR = previousLegacyVarDir;
|
||||
});
|
||||
|
||||
it('defaults to repo-local tmp/state when no env override is set', async () => {
|
||||
const now = new Date('2026-04-25T18:00:00');
|
||||
markProviderBlocked('zai', new Date('2026-04-25T19:00:00'), 'cap', now);
|
||||
await persistProviderCooldowns();
|
||||
const expected = path.resolve(
|
||||
process.cwd(),
|
||||
'tmp',
|
||||
'state',
|
||||
'provider-cooldowns.json',
|
||||
);
|
||||
const raw = await fs.readFile(expected, 'utf8');
|
||||
expect(raw).toContain('"provider": "zai"');
|
||||
await fs.rm(expected, { force: true });
|
||||
});
|
||||
|
||||
it('prefers AGENT_STATUS_DIR over the legacy CLAWDIE_VAR_DIR', async () => {
|
||||
process.env.AGENT_STATUS_DIR = path.resolve(process.cwd(), 'tmp', 'status-a');
|
||||
process.env.CLAWDIE_VAR_DIR = path.resolve(process.cwd(), 'tmp', 'status-b');
|
||||
const now = new Date('2026-04-25T18:00:00');
|
||||
markProviderBlocked('zai', new Date('2026-04-25T19:00:00'), 'cap', now);
|
||||
await persistProviderCooldowns();
|
||||
const preferred = path.join(
|
||||
process.env.AGENT_STATUS_DIR,
|
||||
'provider-cooldowns.json',
|
||||
);
|
||||
const legacy = path.join(
|
||||
process.env.CLAWDIE_VAR_DIR,
|
||||
'provider-cooldowns.json',
|
||||
);
|
||||
await expect(fs.readFile(preferred, 'utf8')).resolves.toContain(
|
||||
'"provider": "zai"',
|
||||
);
|
||||
await expect(fs.access(legacy)).rejects.toBeDefined();
|
||||
await fs.rm(preferred, { force: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseProviderCapError', () => {
|
||||
it('extracts the reset timestamp from a zAI cap error', () => {
|
||||
const now = new Date('2026-04-25T18:00:00');
|
||||
|
|
|
|||
|
|
@ -43,12 +43,11 @@ const ZAI_CAP_PATTERN_NO_RESET = /\b429\s+Usage limit reached\b/iu;
|
|||
const DEFAULT_COOLDOWN_SECONDS = 60 * 60; // 1 hour fallback when no reset stamp
|
||||
|
||||
function defaultPersistencePath(): string {
|
||||
const explicit = process.env.CLAWDIE_VAR_DIR;
|
||||
const explicit = process.env.AGENT_STATUS_DIR || process.env.CLAWDIE_VAR_DIR;
|
||||
if (explicit && explicit.trim()) {
|
||||
return path.join(explicit.trim(), 'provider-cooldowns.json');
|
||||
}
|
||||
const home = process.env.HOME || '/var/db/clawdie';
|
||||
return path.join(home, '.clawdie', 'state', 'provider-cooldowns.json');
|
||||
return path.resolve(process.cwd(), 'tmp', 'state', 'provider-cooldowns.json');
|
||||
}
|
||||
|
||||
export function getFallbackPolicy(): FallbackPolicy {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue