@relayburn/mcp 1.3.1 → 1.5.0
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/CHANGELOG.md +6 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/tools/archive-backed.test.js +2 -71
- package/dist/tools/archive-backed.test.js.map +1 -1
- package/package.json +4 -4
- package/dist/tools/current-block.d.ts +0 -34
- package/dist/tools/current-block.d.ts.map +0 -1
- package/dist/tools/current-block.js +0 -243
- package/dist/tools/current-block.js.map +0 -1
- package/dist/tools/current-block.test.d.ts +0 -2
- package/dist/tools/current-block.test.d.ts.map +0 -1
- package/dist/tools/current-block.test.js +0 -119
- package/dist/tools/current-block.test.js.map +0 -1
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -5,6 +5,4 @@ export type { StartStdioServerOptions, RunningServer } from './server.js';
|
|
|
5
5
|
export type { ToolDefinition, ToolHandler, ToolInputSchema } from './types.js';
|
|
6
6
|
export { createSessionCostTool } from './tools/session-cost.js';
|
|
7
7
|
export type { SessionCostDeps, SessionCostInput, SessionCostResult } from './tools/session-cost.js';
|
|
8
|
-
export { createCurrentBlockTool } from './tools/current-block.js';
|
|
9
|
-
export type { CurrentBlockAdvice, CurrentBlockDeps, CurrentBlockResult, } from './tools/current-block.js';
|
|
10
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC1E,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC/E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAG/C,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -4,11 +4,10 @@ import { tmpdir } from 'node:os';
|
|
|
4
4
|
import * as path from 'node:path';
|
|
5
5
|
import { after, before, beforeEach, describe, it } from 'node:test';
|
|
6
6
|
import { __resetIndexCacheForTesting, appendTurns, archivePath, buildArchive, } from '@relayburn/ledger';
|
|
7
|
-
import { createCurrentBlockTool } from './current-block.js';
|
|
8
7
|
import { createSessionCostTool } from './session-cost.js';
|
|
9
8
|
// Verifies the archive-backed default `queryTurns` path that issue #97 wires
|
|
10
|
-
// onto
|
|
11
|
-
//
|
|
9
|
+
// onto MCP tool handlers — including the transparent fallback to `queryAll`
|
|
10
|
+
// when the archive cannot be opened or queried.
|
|
12
11
|
function fakeTurn(overrides = {}) {
|
|
13
12
|
return {
|
|
14
13
|
v: 1,
|
|
@@ -148,73 +147,5 @@ describe('MCP tool handlers backed by archive (issue #97)', () => {
|
|
|
148
147
|
assert.ok(logs.some((m) => /sessionCost: archive query failed/.test(m)), `expected an archive-fallback log line, got: ${JSON.stringify(logs)}`);
|
|
149
148
|
});
|
|
150
149
|
});
|
|
151
|
-
describe('burn__currentBlock', () => {
|
|
152
|
-
it('reads ledger-derived burn rate from the materialized archive on the hot path', async () => {
|
|
153
|
-
// 60k input tokens, treated as having accrued in the 2h elapsed of a
|
|
154
|
-
// 5h window starting at 10:00. This mirrors the existing unit test for
|
|
155
|
-
// the same calculation but exercises the real archive-backed code
|
|
156
|
-
// path instead of an injected `queryTurns`.
|
|
157
|
-
await appendTurns([
|
|
158
|
-
fakeTurn({
|
|
159
|
-
sessionId: 's-block',
|
|
160
|
-
messageId: 'mb-1',
|
|
161
|
-
ts: '2026-04-24T10:30:00.000Z',
|
|
162
|
-
usage: {
|
|
163
|
-
input: 60_000,
|
|
164
|
-
output: 0,
|
|
165
|
-
reasoning: 0,
|
|
166
|
-
cacheRead: 0,
|
|
167
|
-
cacheCreate5m: 0,
|
|
168
|
-
cacheCreate1h: 0,
|
|
169
|
-
},
|
|
170
|
-
}),
|
|
171
|
-
]);
|
|
172
|
-
await buildArchive();
|
|
173
|
-
const NOW = new Date('2026-04-24T12:00:00.000Z');
|
|
174
|
-
const RESET_AT = '2026-04-24T15:00:00.000Z';
|
|
175
|
-
const tool = createCurrentBlockTool({
|
|
176
|
-
now: () => NOW,
|
|
177
|
-
loadOauthToken: async () => 'tok',
|
|
178
|
-
fetchUsage: async () => ({ five_hour: { percent_used: 20, reset_at: RESET_AT } }),
|
|
179
|
-
});
|
|
180
|
-
const result = (await tool.handler({}));
|
|
181
|
-
assert.equal(result.percentUsed, 20);
|
|
182
|
-
// 60k tokens / 120 minutes = 500 tok/min.
|
|
183
|
-
assert.equal(result.burnRateTokensPerMin, 500);
|
|
184
|
-
assert.equal(result.projectedBlockTotal, 150_000);
|
|
185
|
-
});
|
|
186
|
-
it('falls back to queryAll and logs when the archive query throws', async () => {
|
|
187
|
-
await appendTurns([
|
|
188
|
-
fakeTurn({
|
|
189
|
-
sessionId: 's-block',
|
|
190
|
-
messageId: 'mb-1',
|
|
191
|
-
ts: '2026-04-24T10:30:00.000Z',
|
|
192
|
-
usage: {
|
|
193
|
-
input: 60_000,
|
|
194
|
-
output: 0,
|
|
195
|
-
reasoning: 0,
|
|
196
|
-
cacheRead: 0,
|
|
197
|
-
cacheCreate5m: 0,
|
|
198
|
-
cacheCreate1h: 0,
|
|
199
|
-
},
|
|
200
|
-
}),
|
|
201
|
-
]);
|
|
202
|
-
// Corrupt archive file → openArchive throws; tool should fall through
|
|
203
|
-
// to queryAll and still produce the same forecast.
|
|
204
|
-
await writeFile(archivePath(), 'not a sqlite db', 'utf8');
|
|
205
|
-
const NOW = new Date('2026-04-24T12:00:00.000Z');
|
|
206
|
-
const RESET_AT = '2026-04-24T15:00:00.000Z';
|
|
207
|
-
const logs = [];
|
|
208
|
-
const tool = createCurrentBlockTool({
|
|
209
|
-
now: () => NOW,
|
|
210
|
-
loadOauthToken: async () => 'tok',
|
|
211
|
-
fetchUsage: async () => ({ five_hour: { percent_used: 20, reset_at: RESET_AT } }),
|
|
212
|
-
onLog: (m) => logs.push(m),
|
|
213
|
-
});
|
|
214
|
-
const result = (await tool.handler({}));
|
|
215
|
-
assert.equal(result.burnRateTokensPerMin, 500);
|
|
216
|
-
assert.ok(logs.some((m) => /currentBlock: archive query failed/.test(m)), `expected an archive-fallback log line, got: ${JSON.stringify(logs)}`);
|
|
217
|
-
});
|
|
218
|
-
});
|
|
219
150
|
});
|
|
220
151
|
//# sourceMappingURL=archive-backed.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"archive-backed.test.js","sourceRoot":"","sources":["../../src/tools/archive-backed.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EACL,2BAA2B,EAC3B,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"archive-backed.test.js","sourceRoot":"","sources":["../../src/tools/archive-backed.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,WAAW,CAAC;AAEpE,OAAO,EACL,2BAA2B,EAC3B,WAAW,EACX,WAAW,EACX,YAAY,GACb,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,qBAAqB,EAA0B,MAAM,mBAAmB,CAAC;AAElF,6EAA6E;AAC7E,4EAA4E;AAC5E,gDAAgD;AAEhD,SAAS,QAAQ,CAAC,YAAiC,EAAE;IACnD,OAAO;QACL,CAAC,EAAE,CAAC;QACJ,MAAM,EAAE,aAAa;QACrB,SAAS,EAAE,QAAQ;QACnB,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,CAAC;QACZ,EAAE,EAAE,0BAA0B;QAC9B,KAAK,EAAE,mBAAmB;QAC1B,KAAK,EAAE;YACL,KAAK,EAAE,SAAS;YAChB,MAAM,EAAE,CAAC;YACT,SAAS,EAAE,CAAC;YACZ,SAAS,EAAE,CAAC;YACZ,aAAa,EAAE,CAAC;YAChB,aAAa,EAAE,CAAC;SACjB;QACD,SAAS,EAAE,EAAE;QACb,OAAO,EAAE,cAAc;QACvB,GAAG,SAAS;KACb,CAAC;AACJ,CAAC;AAED,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE,CAAC,CAAC;IAClC,mBAAmB,EAAE;QACnB,KAAK,EAAE,CAAC;QACR,MAAM,EAAE,EAAE;QACV,SAAS,EAAE,GAAG;QACd,UAAU,EAAE,IAAI;QAChB,aAAa,EAAE,gBAAyB;KACzC;CACF,CAAC,CAAC;AAEH,QAAQ,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC/D,IAAI,MAAc,CAAC;IACnB,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAEnD,MAAM,CAAC,KAAK,IAAI,EAAE;QAChB,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,KAAK,IAAI,EAAE;QACpB,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACnD,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,wBAAwB,CAAC,CAAC,CAAC;QACtE,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,MAAM,CAAC;QACvC,2BAA2B,EAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,KAAK,IAAI,EAAE;QACf,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,GAAG,YAAY,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACvC,CAAC;QACD,MAAM,EAAE,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;YACtF,mEAAmE;YACnE,gEAAgE;YAChE,MAAM,WAAW,CAAC;gBAChB,QAAQ,EAAE;gBACV,QAAQ,CAAC;oBACP,SAAS,EAAE,KAAK;oBAChB,SAAS,EAAE,CAAC;oBACZ,KAAK,EAAE;wBACL,KAAK,EAAE,CAAC;wBACR,MAAM,EAAE,SAAS;wBACjB,SAAS,EAAE,CAAC;wBACZ,SAAS,EAAE,CAAC;wBACZ,aAAa,EAAE,CAAC;wBAChB,aAAa,EAAE,CAAC;qBACjB;iBACF,CAAC;aACH,CAAC,CAAC;YACH,MAAM,YAAY,EAAE,CAAC;YAErB,MAAM,IAAI,GAAG,qBAAqB,CAAC;gBACjC,gBAAgB,EAAE,QAAQ;gBAC1B,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAsB,CAAC;YAC7D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACzC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC5C,6CAA6C;YAC7C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YAClC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC;QACzD,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gEAAgE,EAAE,KAAK,IAAI,EAAE;YAC9E,wEAAwE;YACxE,iEAAiE;YACjE,oEAAoE;YACpE,2DAA2D;YAC3D,MAAM,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAChC,MAAM,YAAY,EAAE,CAAC;YAErB,uEAAuE;YACvE,4DAA4D;YAC5D,MAAM,WAAW,CAAC;gBAChB,QAAQ,CAAC;oBACP,SAAS,EAAE,KAAK;oBAChB,SAAS,EAAE,CAAC;oBACZ,EAAE,EAAE,0BAA0B;oBAC9B,KAAK,EAAE;wBACL,KAAK,EAAE,CAAC;wBACR,MAAM,EAAE,SAAS;wBACjB,SAAS,EAAE,CAAC;wBACZ,SAAS,EAAE,CAAC;wBACZ,aAAa,EAAE,CAAC;wBAChB,aAAa,EAAE,CAAC;qBACjB;iBACF,CAAC;aACH,CAAC,CAAC;YAEH,MAAM,IAAI,GAAG,qBAAqB,CAAC;gBACjC,gBAAgB,EAAE,QAAQ;gBAC1B,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAsB,CAAC;YAC7D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC5C,6CAA6C;YAC7C,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;YACjF,MAAM,WAAW,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YAChC,4DAA4D;YAC5D,oEAAoE;YACpE,qEAAqE;YACrE,oBAAoB;YACpB,MAAM,SAAS,CAAC,WAAW,EAAE,EAAE,+BAA+B,EAAE,MAAM,CAAC,CAAC;YAExE,MAAM,IAAI,GAAa,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,qBAAqB,CAAC;gBACjC,gBAAgB,EAAE,QAAQ;gBAC1B,WAAW,EAAE,cAAc;gBAC3B,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;aAC3B,CAAC,CAAC;YACH,MAAM,MAAM,GAAG,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAsB,CAAC;YAC7D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;YAClC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC5C,wBAAwB;YACxB,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;YACjC,MAAM,CAAC,EAAE,CACP,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mCAAmC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAC7D,+CAA+C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CACtE,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@relayburn/mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "MCP (Model Context Protocol) server exposing read-only relayburn ledger queries for in-session self-query",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -19,9 +19,9 @@
|
|
|
19
19
|
"node": ">=22"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@relayburn/analyze": "1.
|
|
23
|
-
"@relayburn/reader": "1.
|
|
24
|
-
"@relayburn/ledger": "1.
|
|
22
|
+
"@relayburn/analyze": "1.5.0",
|
|
23
|
+
"@relayburn/reader": "1.5.0",
|
|
24
|
+
"@relayburn/ledger": "1.5.0"
|
|
25
25
|
},
|
|
26
26
|
"repository": {
|
|
27
27
|
"type": "git",
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import type { EnrichedTurn } from '@relayburn/ledger';
|
|
2
|
-
import type { ToolDefinition } from '../types.js';
|
|
3
|
-
interface UsageWindow {
|
|
4
|
-
percent_used: number;
|
|
5
|
-
reset_at: string;
|
|
6
|
-
}
|
|
7
|
-
interface UsageResponse {
|
|
8
|
-
five_hour?: UsageWindow;
|
|
9
|
-
}
|
|
10
|
-
export type CurrentBlockAdvice = 'on-track' | 'at-risk' | 'over-budget' | 'unknown';
|
|
11
|
-
export interface CurrentBlockResult {
|
|
12
|
-
percentUsed: number | null;
|
|
13
|
-
burnRateTokensPerMin: number | null;
|
|
14
|
-
projectedBlockTotal: number | null;
|
|
15
|
-
minutesToReset: number | null;
|
|
16
|
-
advice: CurrentBlockAdvice;
|
|
17
|
-
note?: string;
|
|
18
|
-
}
|
|
19
|
-
export interface CurrentBlockDeps {
|
|
20
|
-
loadOauthToken?: () => Promise<string | null>;
|
|
21
|
-
fetchUsage?: (token: string) => Promise<UsageResponse>;
|
|
22
|
-
queryTurns?: (windowStartMs: number) => Promise<EnrichedTurn[]>;
|
|
23
|
-
now?: () => Date;
|
|
24
|
-
/**
|
|
25
|
-
* Called when the default archive-backed `queryTurns` falls through to the
|
|
26
|
-
* ledger-walking `queryAll` because the archive open / query threw. Defaults
|
|
27
|
-
* to no-op; the CLI server wires this to stderr so failures are visible.
|
|
28
|
-
*/
|
|
29
|
-
onLog?: (msg: string) => void;
|
|
30
|
-
}
|
|
31
|
-
export declare function createCurrentBlockTool(deps?: CurrentBlockDeps): ToolDefinition;
|
|
32
|
-
export declare function loadOauthToken(): Promise<string | null>;
|
|
33
|
-
export {};
|
|
34
|
-
//# sourceMappingURL=current-block.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"current-block.d.ts","sourceRoot":"","sources":["../../src/tools/current-block.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAMlD,UAAU,WAAW;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,aAAa;IACrB,SAAS,CAAC,EAAE,WAAW,CAAC;CACzB;AAED,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;AAEpF,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,kBAAkB,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC9C,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;IACvD,UAAU,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAChE,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B;AAED,wBAAgB,sBAAsB,CAAC,IAAI,GAAE,gBAAqB,GAAG,cAAc,CAsFlF;AAoED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAY7D"}
|
|
@@ -1,243 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import * as path from 'node:path';
|
|
5
|
-
import { buildArchive, queryAll, queryTurnsFromArchive } from '@relayburn/ledger';
|
|
6
|
-
const USAGE_ENDPOINT = 'https://api.anthropic.com/api/oauth/usage';
|
|
7
|
-
const ANTHROPIC_OAUTH_BETA = 'oauth-2025-04-20';
|
|
8
|
-
const SESSION_DURATION_MS = 5 * 60 * 60 * 1000;
|
|
9
|
-
export function createCurrentBlockTool(deps = {}) {
|
|
10
|
-
const loadToken = deps.loadOauthToken ?? loadOauthToken;
|
|
11
|
-
const fetchUsage = deps.fetchUsage ?? fetchUsageFromApi;
|
|
12
|
-
const log = deps.onLog ?? (() => { });
|
|
13
|
-
const queryTurns = deps.queryTurns ??
|
|
14
|
-
(async (start) => {
|
|
15
|
-
const since = new Date(start).toISOString();
|
|
16
|
-
// Hooks append new turns to the JSONL ledger throughout the session,
|
|
17
|
-
// but the archive is only materialized when something explicitly calls
|
|
18
|
-
// `buildArchive`. Run an incremental build before each query so the
|
|
19
|
-
// tool reflects fresh data. The build is
|
|
20
|
-
// idempotent + cursor-driven, so it's a no-op when nothing has changed
|
|
21
|
-
// since the last call.
|
|
22
|
-
try {
|
|
23
|
-
await buildArchive();
|
|
24
|
-
return await queryTurnsFromArchive({ since, source: 'claude-code' });
|
|
25
|
-
}
|
|
26
|
-
catch (err) {
|
|
27
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
28
|
-
log(`currentBlock: archive query failed, falling back to ledger walk: ${msg}`);
|
|
29
|
-
return queryAll({ since, source: 'claude-code' });
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
const now = deps.now ?? (() => new Date());
|
|
33
|
-
return {
|
|
34
|
-
name: 'burn__currentBlock',
|
|
35
|
-
description: "Return the Claude 5-hour quota window's current percent-used and a " +
|
|
36
|
-
'locally-forecast burn rate + projection. Combines the OAuth-reported ' +
|
|
37
|
-
'window state with ledger-derived token totals so an agent can decide ' +
|
|
38
|
-
'whether to downgrade models mid-session. Read-only.',
|
|
39
|
-
inputSchema: {
|
|
40
|
-
type: 'object',
|
|
41
|
-
properties: {
|
|
42
|
-
sessionId: {
|
|
43
|
-
type: 'string',
|
|
44
|
-
description: 'Accepted but not used — current-block is account-wide, not per-session.',
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
required: [],
|
|
48
|
-
additionalProperties: false,
|
|
49
|
-
},
|
|
50
|
-
handler: async () => {
|
|
51
|
-
const nowDate = now();
|
|
52
|
-
const nowMs = nowDate.getTime();
|
|
53
|
-
const token = await loadToken();
|
|
54
|
-
let usage = null;
|
|
55
|
-
let usageError = null;
|
|
56
|
-
if (token) {
|
|
57
|
-
try {
|
|
58
|
-
usage = await fetchUsage(token);
|
|
59
|
-
}
|
|
60
|
-
catch (err) {
|
|
61
|
-
usageError = err instanceof Error ? err.message : String(err);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
else {
|
|
65
|
-
usageError = 'no Claude OAuth token found';
|
|
66
|
-
}
|
|
67
|
-
const windowStartMs = forecastWindowStartMs(usage?.five_hour, nowMs);
|
|
68
|
-
const turns = await queryTurns(windowStartMs);
|
|
69
|
-
const tokensSoFar = sumTokens(turns);
|
|
70
|
-
const elapsedMs = Math.max(0, nowMs - windowStartMs);
|
|
71
|
-
const remainingMs = Math.max(0, windowStartMs + SESSION_DURATION_MS - nowMs);
|
|
72
|
-
const burnRate = elapsedMs > 0 ? tokensSoFar / (elapsedMs / 60_000) : null;
|
|
73
|
-
const projectedBlockTotal = burnRate !== null ? Math.round(burnRate * (SESSION_DURATION_MS / 60_000)) : null;
|
|
74
|
-
const minutesToReset = Math.round(remainingMs / 60_000);
|
|
75
|
-
const pct = normalizePercent(usage?.five_hour?.percent_used);
|
|
76
|
-
const projectedPct = projectPercentAtReset(pct, elapsedMs, remainingMs);
|
|
77
|
-
const advice = deriveAdvice(pct, projectedPct);
|
|
78
|
-
const result = {
|
|
79
|
-
percentUsed: pct,
|
|
80
|
-
burnRateTokensPerMin: burnRate !== null ? Math.round(burnRate) : null,
|
|
81
|
-
projectedBlockTotal,
|
|
82
|
-
minutesToReset,
|
|
83
|
-
advice,
|
|
84
|
-
};
|
|
85
|
-
if (usageError)
|
|
86
|
-
result.note = `oauth usage unavailable: ${usageError}`;
|
|
87
|
-
return result;
|
|
88
|
-
},
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
function sumTokens(turns) {
|
|
92
|
-
let total = 0;
|
|
93
|
-
for (const t of turns) {
|
|
94
|
-
const u = t.usage;
|
|
95
|
-
total +=
|
|
96
|
-
(u.input ?? 0) +
|
|
97
|
-
(u.output ?? 0) +
|
|
98
|
-
(u.reasoning ?? 0) +
|
|
99
|
-
(u.cacheRead ?? 0) +
|
|
100
|
-
(u.cacheCreate5m ?? 0) +
|
|
101
|
-
(u.cacheCreate1h ?? 0);
|
|
102
|
-
}
|
|
103
|
-
return total;
|
|
104
|
-
}
|
|
105
|
-
function normalizePercent(raw) {
|
|
106
|
-
if (raw === undefined || !Number.isFinite(raw))
|
|
107
|
-
return null;
|
|
108
|
-
// Anthropic's OAuth usage endpoint documents and returns the 0..100 scale
|
|
109
|
-
// (e.g. percent_used: 34 means 34%). Pass it through unchanged. An earlier
|
|
110
|
-
// version tried to auto-detect 0..1 vs 0..100 with a > 1.5 threshold, but
|
|
111
|
-
// that misclassifies legitimately-low values like 1% as 0..1 scale and
|
|
112
|
-
// inflates them 100x — turning "1% used" into "100% used → over-budget"
|
|
113
|
-
// and causing false alarms early in a quota window.
|
|
114
|
-
return raw;
|
|
115
|
-
}
|
|
116
|
-
function projectPercentAtReset(currentPercent, elapsedMs, remainingMs) {
|
|
117
|
-
if (currentPercent === null)
|
|
118
|
-
return null;
|
|
119
|
-
if (elapsedMs <= 0)
|
|
120
|
-
return null;
|
|
121
|
-
const totalMs = elapsedMs + remainingMs;
|
|
122
|
-
if (totalMs <= 0)
|
|
123
|
-
return null;
|
|
124
|
-
const elapsedFraction = elapsedMs / totalMs;
|
|
125
|
-
if (elapsedFraction <= 0)
|
|
126
|
-
return null;
|
|
127
|
-
return Math.min(100, currentPercent / elapsedFraction);
|
|
128
|
-
}
|
|
129
|
-
function deriveAdvice(current, projected) {
|
|
130
|
-
if (current === null && projected === null)
|
|
131
|
-
return 'unknown';
|
|
132
|
-
if (current !== null && current >= 100)
|
|
133
|
-
return 'over-budget';
|
|
134
|
-
if (projected !== null && projected >= 100)
|
|
135
|
-
return 'over-budget';
|
|
136
|
-
if (projected !== null && projected >= 80)
|
|
137
|
-
return 'at-risk';
|
|
138
|
-
if (current !== null && current >= 80)
|
|
139
|
-
return 'at-risk';
|
|
140
|
-
return 'on-track';
|
|
141
|
-
}
|
|
142
|
-
function forecastWindowStartMs(fiveHour, nowMs) {
|
|
143
|
-
if (fiveHour) {
|
|
144
|
-
const reset = Date.parse(fiveHour.reset_at);
|
|
145
|
-
if (Number.isFinite(reset))
|
|
146
|
-
return reset - SESSION_DURATION_MS;
|
|
147
|
-
}
|
|
148
|
-
return nowMs - SESSION_DURATION_MS;
|
|
149
|
-
}
|
|
150
|
-
// --------------------------------------------------------------------------
|
|
151
|
-
// OAuth token loading. Copied verbatim from packages/cli/src/commands/budget.ts
|
|
152
|
-
// (extracting to a shared location would pull token I/O into @relayburn/ledger,
|
|
153
|
-
// which has no transport deps today — punting that refactor to a follow-up).
|
|
154
|
-
// --------------------------------------------------------------------------
|
|
155
|
-
export async function loadOauthToken() {
|
|
156
|
-
const env = process.env['CLAUDE_CODE_OAUTH_TOKEN'];
|
|
157
|
-
if (env && env.length > 0)
|
|
158
|
-
return env;
|
|
159
|
-
const fromFile = await readCredentialsFile();
|
|
160
|
-
if (fromFile)
|
|
161
|
-
return fromFile;
|
|
162
|
-
if (process.platform === 'darwin') {
|
|
163
|
-
const fromKeychain = await readMacOsKeychain();
|
|
164
|
-
if (fromKeychain)
|
|
165
|
-
return fromKeychain;
|
|
166
|
-
}
|
|
167
|
-
return null;
|
|
168
|
-
}
|
|
169
|
-
async function readCredentialsFile() {
|
|
170
|
-
const candidates = [
|
|
171
|
-
path.join(homedir(), '.claude', '.credentials.json'),
|
|
172
|
-
path.join(homedir(), '.claude', 'credentials.json'),
|
|
173
|
-
];
|
|
174
|
-
for (const p of candidates) {
|
|
175
|
-
try {
|
|
176
|
-
const raw = await readFile(p, 'utf8');
|
|
177
|
-
const parsed = JSON.parse(raw);
|
|
178
|
-
const token = extractTokenFromCredentials(parsed);
|
|
179
|
-
if (token)
|
|
180
|
-
return token;
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
// fall through to next candidate
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
function extractTokenFromCredentials(parsed) {
|
|
189
|
-
if (!parsed || typeof parsed !== 'object')
|
|
190
|
-
return null;
|
|
191
|
-
const obj = parsed;
|
|
192
|
-
const oauth = obj['claudeAiOauth'];
|
|
193
|
-
if (oauth && typeof oauth === 'object') {
|
|
194
|
-
const access = oauth['accessToken'];
|
|
195
|
-
if (typeof access === 'string' && access.length > 0)
|
|
196
|
-
return access;
|
|
197
|
-
}
|
|
198
|
-
const direct = obj['accessToken'];
|
|
199
|
-
if (typeof direct === 'string' && direct.length > 0)
|
|
200
|
-
return direct;
|
|
201
|
-
return null;
|
|
202
|
-
}
|
|
203
|
-
function readMacOsKeychain() {
|
|
204
|
-
return new Promise((resolve) => {
|
|
205
|
-
const child = spawn('security', ['find-generic-password', '-s', 'Claude Code-credentials', '-w'], { stdio: ['ignore', 'pipe', 'ignore'] });
|
|
206
|
-
let out = '';
|
|
207
|
-
child.stdout.on('data', (chunk) => {
|
|
208
|
-
out += chunk.toString('utf8');
|
|
209
|
-
});
|
|
210
|
-
child.on('error', () => resolve(null));
|
|
211
|
-
child.on('exit', (code) => {
|
|
212
|
-
if (code !== 0)
|
|
213
|
-
return resolve(null);
|
|
214
|
-
const trimmed = out.trim();
|
|
215
|
-
if (!trimmed)
|
|
216
|
-
return resolve(null);
|
|
217
|
-
try {
|
|
218
|
-
const parsed = JSON.parse(trimmed);
|
|
219
|
-
const fromJson = extractTokenFromCredentials(parsed);
|
|
220
|
-
if (fromJson)
|
|
221
|
-
return resolve(fromJson);
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
// keychain entry is the bare token, not JSON
|
|
225
|
-
}
|
|
226
|
-
resolve(trimmed);
|
|
227
|
-
});
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
async function fetchUsageFromApi(token) {
|
|
231
|
-
const res = await fetch(USAGE_ENDPOINT, {
|
|
232
|
-
headers: {
|
|
233
|
-
Authorization: `Bearer ${token}`,
|
|
234
|
-
'anthropic-beta': ANTHROPIC_OAUTH_BETA,
|
|
235
|
-
Accept: 'application/json',
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
if (!res.ok) {
|
|
239
|
-
throw new Error(`usage endpoint ${res.status}`);
|
|
240
|
-
}
|
|
241
|
-
return (await res.json());
|
|
242
|
-
}
|
|
243
|
-
//# sourceMappingURL=current-block.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"current-block.js","sourceRoot":"","sources":["../../src/tools/current-block.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"current-block.test.d.ts","sourceRoot":"","sources":["../../src/tools/current-block.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { strict as assert } from 'node:assert';
|
|
2
|
-
import { describe, it } from 'node:test';
|
|
3
|
-
import { createCurrentBlockTool } from './current-block.js';
|
|
4
|
-
function turn(usage = {}, ts = '2026-04-24T10:00:00.000Z') {
|
|
5
|
-
return {
|
|
6
|
-
v: 1,
|
|
7
|
-
source: 'claude-code',
|
|
8
|
-
sessionId: 's1',
|
|
9
|
-
messageId: 'm1',
|
|
10
|
-
turnIndex: 0,
|
|
11
|
-
ts,
|
|
12
|
-
model: 'claude-sonnet-4-5',
|
|
13
|
-
usage: {
|
|
14
|
-
input: 0,
|
|
15
|
-
output: 0,
|
|
16
|
-
reasoning: 0,
|
|
17
|
-
cacheRead: 0,
|
|
18
|
-
cacheCreate5m: 0,
|
|
19
|
-
cacheCreate1h: 0,
|
|
20
|
-
...usage,
|
|
21
|
-
},
|
|
22
|
-
toolCalls: [],
|
|
23
|
-
enrichment: {},
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
describe('createCurrentBlockTool', () => {
|
|
27
|
-
// 2026-04-24 12:00 UTC = midpoint of a 5h window starting at 10:00 with reset at 15:00
|
|
28
|
-
const NOW = new Date('2026-04-24T12:00:00.000Z');
|
|
29
|
-
const RESET_AT = '2026-04-24T15:00:00.000Z'; // 3 hours away
|
|
30
|
-
it('combines OAuth percent_used with locally-derived burn rate', async () => {
|
|
31
|
-
// 60k tokens consumed in 2h elapsed → 500 tok/min → 5h projection = 150_000.
|
|
32
|
-
// OAuth says 20% used at the 2h mark of a 5h window, projecting to 50% at
|
|
33
|
-
// reset → on-track on both axes.
|
|
34
|
-
const tool = createCurrentBlockTool({
|
|
35
|
-
now: () => NOW,
|
|
36
|
-
loadOauthToken: async () => 'tok',
|
|
37
|
-
fetchUsage: async () => ({ five_hour: { percent_used: 20, reset_at: RESET_AT } }),
|
|
38
|
-
queryTurns: async () => [turn({ input: 60_000 })],
|
|
39
|
-
});
|
|
40
|
-
const result = (await tool.handler({}));
|
|
41
|
-
assert.equal(result.percentUsed, 20);
|
|
42
|
-
assert.equal(result.burnRateTokensPerMin, 500);
|
|
43
|
-
assert.equal(result.projectedBlockTotal, 150_000);
|
|
44
|
-
assert.equal(result.minutesToReset, 180);
|
|
45
|
-
assert.equal(result.advice, 'on-track');
|
|
46
|
-
});
|
|
47
|
-
it('flags at-risk when projected reaches 80%+ at reset', async () => {
|
|
48
|
-
// 40% used at 1h elapsed of a 5h window → projects to 200% at reset.
|
|
49
|
-
const now = new Date('2026-04-24T11:00:00.000Z');
|
|
50
|
-
const tool = createCurrentBlockTool({
|
|
51
|
-
now: () => now,
|
|
52
|
-
loadOauthToken: async () => 'tok',
|
|
53
|
-
fetchUsage: async () => ({ five_hour: { percent_used: 40, reset_at: RESET_AT } }),
|
|
54
|
-
queryTurns: async () => [],
|
|
55
|
-
});
|
|
56
|
-
const result = (await tool.handler({}));
|
|
57
|
-
assert.equal(result.advice, 'over-budget');
|
|
58
|
-
});
|
|
59
|
-
it('flags over-budget when current percent already >= 100', async () => {
|
|
60
|
-
const tool = createCurrentBlockTool({
|
|
61
|
-
now: () => NOW,
|
|
62
|
-
loadOauthToken: async () => 'tok',
|
|
63
|
-
fetchUsage: async () => ({ five_hour: { percent_used: 100, reset_at: RESET_AT } }),
|
|
64
|
-
queryTurns: async () => [],
|
|
65
|
-
});
|
|
66
|
-
const result = (await tool.handler({}));
|
|
67
|
-
assert.equal(result.advice, 'over-budget');
|
|
68
|
-
});
|
|
69
|
-
it('returns null percentUsed and a note when no OAuth token is available', async () => {
|
|
70
|
-
const tool = createCurrentBlockTool({
|
|
71
|
-
now: () => NOW,
|
|
72
|
-
loadOauthToken: async () => null,
|
|
73
|
-
fetchUsage: async () => {
|
|
74
|
-
throw new Error('should not be called');
|
|
75
|
-
},
|
|
76
|
-
queryTurns: async () => [turn({ input: 30_000 })],
|
|
77
|
-
});
|
|
78
|
-
const result = (await tool.handler({}));
|
|
79
|
-
assert.equal(result.percentUsed, null);
|
|
80
|
-
assert.match(result.note ?? '', /no Claude OAuth token/);
|
|
81
|
-
// Local forecast still flows even without OAuth — that's the entire
|
|
82
|
-
// point of the dual-source design.
|
|
83
|
-
assert.ok(result.burnRateTokensPerMin !== null);
|
|
84
|
-
});
|
|
85
|
-
it('keeps a local forecast when the OAuth fetch errors', async () => {
|
|
86
|
-
const tool = createCurrentBlockTool({
|
|
87
|
-
now: () => NOW,
|
|
88
|
-
loadOauthToken: async () => 'tok',
|
|
89
|
-
fetchUsage: async () => {
|
|
90
|
-
throw new Error('502');
|
|
91
|
-
},
|
|
92
|
-
queryTurns: async () => [turn({ input: 30_000 })],
|
|
93
|
-
});
|
|
94
|
-
const result = (await tool.handler({}));
|
|
95
|
-
assert.equal(result.percentUsed, null);
|
|
96
|
-
assert.match(result.note ?? '', /oauth usage unavailable/);
|
|
97
|
-
assert.notEqual(result.burnRateTokensPerMin, null);
|
|
98
|
-
});
|
|
99
|
-
it('preserves low percent_used values as-is on the 0..100 scale (regression: Devin review on #67)', async () => {
|
|
100
|
-
// 1% used early in the window must stay 1%, not get inflated to 100%
|
|
101
|
-
// by an over-eager 0..1 normalization heuristic.
|
|
102
|
-
const tool = createCurrentBlockTool({
|
|
103
|
-
now: () => NOW,
|
|
104
|
-
loadOauthToken: async () => 'tok',
|
|
105
|
-
fetchUsage: async () => ({ five_hour: { percent_used: 1, reset_at: RESET_AT } }),
|
|
106
|
-
queryTurns: async () => [],
|
|
107
|
-
});
|
|
108
|
-
const result = (await tool.handler({}));
|
|
109
|
-
assert.equal(result.percentUsed, 1);
|
|
110
|
-
assert.equal(result.advice, 'on-track');
|
|
111
|
-
});
|
|
112
|
-
it('declares its tool surface (name, description, schema)', () => {
|
|
113
|
-
const tool = createCurrentBlockTool({});
|
|
114
|
-
assert.equal(tool.name, 'burn__currentBlock');
|
|
115
|
-
assert.ok(tool.description.length > 0);
|
|
116
|
-
assert.equal(tool.inputSchema.type, 'object');
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
//# sourceMappingURL=current-block.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"current-block.test.js","sourceRoot":"","sources":["../../src/tools/current-block.test.ts"],"names":[],"mappings":"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"}
|