maestro-core 0.2.1 → 0.2.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/dist/cost.d.ts +60 -0
- package/dist/cost.d.ts.map +1 -0
- package/dist/cost.js +68 -0
- package/dist/cost.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +69 -0
- package/dist/models.d.ts.map +1 -0
- package/dist/models.js +113 -0
- package/dist/models.js.map +1 -0
- package/dist/ports/quota-store.d.ts +2 -0
- package/dist/ports/quota-store.d.ts.map +1 -1
- package/dist/runtime/empty-recovery.d.ts +62 -0
- package/dist/runtime/empty-recovery.d.ts.map +1 -0
- package/dist/runtime/empty-recovery.js +34 -0
- package/dist/runtime/empty-recovery.js.map +1 -0
- package/dist/runtime/index.d.ts +6 -0
- package/dist/runtime/index.d.ts.map +1 -0
- package/dist/runtime/index.js +6 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/memory.d.ts +32 -0
- package/dist/runtime/memory.d.ts.map +1 -0
- package/dist/runtime/memory.js +15 -0
- package/dist/runtime/memory.js.map +1 -0
- package/dist/runtime/providers.d.ts +53 -0
- package/dist/runtime/providers.d.ts.map +1 -0
- package/dist/runtime/providers.js +124 -0
- package/dist/runtime/providers.js.map +1 -0
- package/dist/runtime/quota.d.ts +66 -0
- package/dist/runtime/quota.d.ts.map +1 -0
- package/dist/runtime/quota.js +102 -0
- package/dist/runtime/quota.js.map +1 -0
- package/dist/runtime/run-chat-turn.d.ts +145 -0
- package/dist/runtime/run-chat-turn.d.ts.map +1 -0
- package/dist/runtime/run-chat-turn.js +341 -0
- package/dist/runtime/run-chat-turn.js.map +1 -0
- package/dist/windows.d.ts +49 -0
- package/dist/windows.d.ts.map +1 -0
- package/dist/windows.js +63 -0
- package/dist/windows.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
2
|
+
import { convertToModelMessages, stepCountIs, streamText } from 'ai';
|
|
3
|
+
import { buildAiSdkTools } from '../adapters/ai-sdk.js';
|
|
4
|
+
import { applyCacheBreakpoints } from '../cache-control.js';
|
|
5
|
+
import { estimateCost } from '../cost.js';
|
|
6
|
+
import { selectChatModel } from '../models.js';
|
|
7
|
+
import { SystemClock } from '../ports/clock.js';
|
|
8
|
+
import { SilentLogger } from '../ports/logger.js';
|
|
9
|
+
import { NoopTelemetrySink } from '../ports/telemetry-sink.js';
|
|
10
|
+
import { loadMemoryBlock } from './memory.js';
|
|
11
|
+
import { AiQuotaDeniedError, checkAndEnforce } from './quota.js';
|
|
12
|
+
export async function runChatTurn(args) {
|
|
13
|
+
const clock = args.ports.clock ?? new SystemClock();
|
|
14
|
+
const logger = args.ports.logger ?? new SilentLogger();
|
|
15
|
+
const telemetry = args.ports.telemetry ?? new NoopTelemetrySink();
|
|
16
|
+
const failOpenOnQuotaError = args.failOpenOnQuotaError ?? true;
|
|
17
|
+
// ── 0. Quota pre-call gate ──────────────────────────────────────
|
|
18
|
+
// Runs before any LLM/key/storage work so a denied tenant pays
|
|
19
|
+
// zero side-effects. `AiQuotaDeniedError` is the only error this
|
|
20
|
+
// block intentionally surfaces; everything else is fail-open by
|
|
21
|
+
// default so a Redis/Postgres hiccup never blocks paying tenants.
|
|
22
|
+
if (args.ports.quotaStore) {
|
|
23
|
+
try {
|
|
24
|
+
await checkAndEnforce({
|
|
25
|
+
quotaStore: args.ports.quotaStore,
|
|
26
|
+
tenantId: args.ctx.tenantId,
|
|
27
|
+
surface: args.ctx.transport,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
if (e instanceof AiQuotaDeniedError) {
|
|
32
|
+
// Fire-and-forget telemetry for the deny — it landed
|
|
33
|
+
// in the audit log via the host's own `record` call
|
|
34
|
+
// when the over-cap call originally went through;
|
|
35
|
+
// this just surfaces the deny event for dashboards.
|
|
36
|
+
void telemetry.emit([
|
|
37
|
+
{
|
|
38
|
+
type: 'quota.consumed',
|
|
39
|
+
tenantId: args.ctx.tenantId,
|
|
40
|
+
surface: args.ctx.transport,
|
|
41
|
+
window: 'day',
|
|
42
|
+
used: e.payload.current,
|
|
43
|
+
ceiling: e.payload.ceiling,
|
|
44
|
+
denied: true,
|
|
45
|
+
occurredAt: clock.now(),
|
|
46
|
+
},
|
|
47
|
+
]);
|
|
48
|
+
throw e;
|
|
49
|
+
}
|
|
50
|
+
if (failOpenOnQuotaError) {
|
|
51
|
+
logger.warn('runChatTurn quotaStore.check failed; failing open', {
|
|
52
|
+
tenantId: args.ctx.tenantId,
|
|
53
|
+
surface: args.ctx.transport,
|
|
54
|
+
error: e instanceof Error ? e.message : String(e),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
throw e;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ── 1. Model selection ──────────────────────────────────────────
|
|
63
|
+
const lastUserMessage = [...args.messages].reverse().find((m) => m.role === 'user');
|
|
64
|
+
const lastUserText = extractText(lastUserMessage);
|
|
65
|
+
const turnIndex = args.messages.filter((m) => m.role === 'user').length;
|
|
66
|
+
const selection = selectChatModel({
|
|
67
|
+
userMessage: lastUserText,
|
|
68
|
+
turnIndex,
|
|
69
|
+
forceTier: args.modelHint?.tier,
|
|
70
|
+
models: args.models,
|
|
71
|
+
});
|
|
72
|
+
// ── 2. Provider key + model instance ────────────────────────────
|
|
73
|
+
const apiKey = await args.ports.keyProvider.getKey('anthropic', args.ctx.tenantId);
|
|
74
|
+
const anthropic = createAnthropic({ apiKey });
|
|
75
|
+
const model = anthropic(selection.modelId);
|
|
76
|
+
// ── 3. Tools (adapter handles per-call audit) ───────────────────
|
|
77
|
+
const rawTools = buildAiSdkTools({
|
|
78
|
+
registry: args.tools,
|
|
79
|
+
ctx: args.ctx,
|
|
80
|
+
audit: args.ports.auditStore,
|
|
81
|
+
clock,
|
|
82
|
+
});
|
|
83
|
+
// ── 3b. Memory pull (optional) ──────────────────────────────────
|
|
84
|
+
// Load + format memory facts BEFORE the cache split so the facts
|
|
85
|
+
// land in the dynamic (uncached) segment. Memory varies per
|
|
86
|
+
// principal — caching it would split the prompt cache by user and
|
|
87
|
+
// kill cross-tenant reuse.
|
|
88
|
+
let memoryBlock = '';
|
|
89
|
+
if (args.ports.memoryStore && args.ctx.principal) {
|
|
90
|
+
try {
|
|
91
|
+
memoryBlock = await loadMemoryBlock({
|
|
92
|
+
memoryStore: args.ports.memoryStore,
|
|
93
|
+
scope: {
|
|
94
|
+
tenantId: args.ctx.tenantId,
|
|
95
|
+
principalId: args.ctx.principal.id,
|
|
96
|
+
namespace: args.memoryNamespace,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
// Fail-open — memory is a UX enhancement, not a correctness
|
|
102
|
+
// requirement. Log and proceed without it.
|
|
103
|
+
logger.warn('runChatTurn memoryStore.load failed; proceeding without memory', {
|
|
104
|
+
tenantId: args.ctx.tenantId,
|
|
105
|
+
error: e instanceof Error ? e.message : String(e),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
// ── 3c. Cache split ─────────────────────────────────────────────
|
|
110
|
+
// applyCacheBreakpoints renders `system` as a two-element array:
|
|
111
|
+
// [0] static — cached (cacheControl ephemeral marker)
|
|
112
|
+
// [1] dynamic — uncached (tenant context + memory + now)
|
|
113
|
+
// The last tool in the registry also gets the cache marker so the
|
|
114
|
+
// tool schema block is served from cache on hot turns.
|
|
115
|
+
const dynamicLines = [args.systemPrompt.dynamic ?? '', memoryBlock]
|
|
116
|
+
.filter((s) => s.length > 0)
|
|
117
|
+
.join('\n\n');
|
|
118
|
+
const nowAtCacheSplit = clock.now();
|
|
119
|
+
const cached = applyCacheBreakpoints({
|
|
120
|
+
static: {
|
|
121
|
+
intro: args.systemPrompt.static,
|
|
122
|
+
corpus: '',
|
|
123
|
+
tools: rawTools,
|
|
124
|
+
},
|
|
125
|
+
dynamic: {
|
|
126
|
+
tenant: {
|
|
127
|
+
id: args.ctx.tenantId,
|
|
128
|
+
timezone: args.ctx.timezone,
|
|
129
|
+
},
|
|
130
|
+
principal: args.ctx.principal ? { id: args.ctx.principal.id } : undefined,
|
|
131
|
+
nowIso: nowAtCacheSplit.toISOString(),
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
// Append the host-supplied dynamic + memory content to the
|
|
135
|
+
// generated dynamic system segment so it lands AFTER the
|
|
136
|
+
// breakpoint. The structured tenant context that
|
|
137
|
+
// applyCacheBreakpoints synthesises is the first dynamic line;
|
|
138
|
+
// anything host-supplied follows it.
|
|
139
|
+
const dynamicMsg = cached.system[1];
|
|
140
|
+
if (dynamicMsg && dynamicLines.length > 0) {
|
|
141
|
+
dynamicMsg.content = `${dynamicMsg.content}\n${dynamicLines}`;
|
|
142
|
+
}
|
|
143
|
+
// ── 4. Reserve assistant turn row ───────────────────────────────
|
|
144
|
+
const startedAt = clock.now();
|
|
145
|
+
// Host-supplied turnId wins when present so the port impl can
|
|
146
|
+
// address rows it pre-created (typical pattern: route inserts
|
|
147
|
+
// a row, takes its primary key, passes it here as a string).
|
|
148
|
+
const turnId = args.turnId ?? makeTurnId(startedAt, args.ctx.requestId);
|
|
149
|
+
await args.ports.turnStore.upsert({
|
|
150
|
+
id: turnId,
|
|
151
|
+
threadId: args.threadId,
|
|
152
|
+
tenantId: args.ctx.tenantId,
|
|
153
|
+
role: 'assistant',
|
|
154
|
+
content: null,
|
|
155
|
+
status: 'pending',
|
|
156
|
+
modelId: selection.modelId,
|
|
157
|
+
createdAt: startedAt,
|
|
158
|
+
updatedAt: startedAt,
|
|
159
|
+
});
|
|
160
|
+
// ── 5. Stream ───────────────────────────────────────────────────
|
|
161
|
+
// System messages MUST be passed via the top-level `system`
|
|
162
|
+
// parameter (not mixed into `messages`). Anthropic's tool-use API
|
|
163
|
+
// only enables tool_use blocks when system is supplied at the
|
|
164
|
+
// top level; messages-mixed system trips a different code path
|
|
165
|
+
// in @ai-sdk/anthropic and the model falls back to emitting
|
|
166
|
+
// <function_calls> XML in prose instead of structured tool calls.
|
|
167
|
+
// Confirmed bug in 0.2.2 — symptom: tool names visible as plain
|
|
168
|
+
// text in the chat, tools never execute. Cache breakpoint markers
|
|
169
|
+
// on the system entries carry through unchanged.
|
|
170
|
+
const userMessages = await convertToModelMessages(args.messages);
|
|
171
|
+
const stream = streamText({
|
|
172
|
+
model,
|
|
173
|
+
system: cached.system,
|
|
174
|
+
messages: userMessages,
|
|
175
|
+
tools: cached.tools,
|
|
176
|
+
// Without `stopWhen`, AI SDK defaults to `stepCountIs(1)` —
|
|
177
|
+
// the SDK stops after the FIRST model response, so even if
|
|
178
|
+
// the model emits real tool-use blocks, the follow-up step
|
|
179
|
+
// that re-prompts with tool results never runs. The user
|
|
180
|
+
// sees the assistant bubble end immediately after the tool
|
|
181
|
+
// call with no answer.
|
|
182
|
+
stopWhen: stepCountIs(args.maxSteps ?? 5),
|
|
183
|
+
abortSignal: args.abortSignal,
|
|
184
|
+
onFinish: async (event) => {
|
|
185
|
+
const finishedAt = clock.now();
|
|
186
|
+
const durationMs = finishedAt.getTime() - startedAt.getTime();
|
|
187
|
+
// AI SDK v6 surfaces usage as `event.usage` with the
|
|
188
|
+
// shape { inputTokens, outputTokens, totalTokens } and
|
|
189
|
+
// optionally cachedInputTokens for providers that support
|
|
190
|
+
// prompt caching. Fall back to 0 to keep the cost
|
|
191
|
+
// arithmetic safe when fields are missing.
|
|
192
|
+
const usage = (event.usage ?? null);
|
|
193
|
+
const tokensIn = usage?.inputTokens ?? 0;
|
|
194
|
+
const tokensOut = usage?.outputTokens ?? 0;
|
|
195
|
+
const cacheReadTokens = usage?.cachedInputTokens ?? 0;
|
|
196
|
+
// Cache-write token count isn't exposed in the v6 usage
|
|
197
|
+
// object today. Tracked separately in cache-control.ts
|
|
198
|
+
// bookkeeping once slice 4 lands.
|
|
199
|
+
const cacheWriteTokens = 0;
|
|
200
|
+
const costUsd = estimateCost({
|
|
201
|
+
input: tokensIn,
|
|
202
|
+
output: tokensOut,
|
|
203
|
+
cacheRead: cacheReadTokens,
|
|
204
|
+
cacheWrite: cacheWriteTokens,
|
|
205
|
+
}, selection.modelId);
|
|
206
|
+
const costUsdMicro = Math.max(0, Math.round(costUsd * 1_000_000));
|
|
207
|
+
const finalTurn = {
|
|
208
|
+
id: turnId,
|
|
209
|
+
threadId: args.threadId,
|
|
210
|
+
tenantId: args.ctx.tenantId,
|
|
211
|
+
role: 'assistant',
|
|
212
|
+
content: event.text,
|
|
213
|
+
status: 'completed',
|
|
214
|
+
modelId: selection.modelId,
|
|
215
|
+
tokensIn,
|
|
216
|
+
tokensOut,
|
|
217
|
+
cacheReadTokens,
|
|
218
|
+
cacheWriteTokens,
|
|
219
|
+
costUsdMicro,
|
|
220
|
+
durationMs,
|
|
221
|
+
createdAt: startedAt,
|
|
222
|
+
updatedAt: finishedAt,
|
|
223
|
+
};
|
|
224
|
+
try {
|
|
225
|
+
await args.ports.turnStore.upsert(finalTurn);
|
|
226
|
+
}
|
|
227
|
+
catch (e) {
|
|
228
|
+
logger.error('runChatTurn turnStore.upsert failed', {
|
|
229
|
+
turnId,
|
|
230
|
+
error: e instanceof Error ? e.message : String(e),
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Post-call quota accounting. Fire-and-forget — a slow
|
|
234
|
+
// ledger write must not stall the SSE finalisation. The
|
|
235
|
+
// tool-calls count comes from the AI SDK toolCalls list
|
|
236
|
+
// (may be absent if the turn didn't invoke any tools).
|
|
237
|
+
if (args.ports.quotaStore) {
|
|
238
|
+
const toolCallsCount = Array.isArray(event.toolCalls)
|
|
239
|
+
? (event.toolCalls.length)
|
|
240
|
+
: 0;
|
|
241
|
+
args.ports.quotaStore
|
|
242
|
+
.record({
|
|
243
|
+
tenantId: args.ctx.tenantId,
|
|
244
|
+
surface: args.ctx.transport,
|
|
245
|
+
tokensIn,
|
|
246
|
+
tokensOut,
|
|
247
|
+
cacheReadTokens,
|
|
248
|
+
cacheWriteTokens,
|
|
249
|
+
toolCalls: toolCallsCount,
|
|
250
|
+
costUsdMicro,
|
|
251
|
+
modelId: selection.modelId,
|
|
252
|
+
occurredAt: finishedAt,
|
|
253
|
+
})
|
|
254
|
+
.catch((e) => {
|
|
255
|
+
logger.warn('runChatTurn quotaStore.record failed', {
|
|
256
|
+
turnId,
|
|
257
|
+
error: e instanceof Error ? e.message : String(e),
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
// Telemetry is fire-and-forget; emit failures are swallowed
|
|
262
|
+
// inside the sink and never block the stream finalisation.
|
|
263
|
+
void telemetry.emit([
|
|
264
|
+
{
|
|
265
|
+
type: 'turn.finalized',
|
|
266
|
+
turnId,
|
|
267
|
+
threadId: args.threadId,
|
|
268
|
+
tenantId: args.ctx.tenantId,
|
|
269
|
+
modelId: selection.modelId,
|
|
270
|
+
tier: selection.tier,
|
|
271
|
+
tokensIn,
|
|
272
|
+
tokensOut,
|
|
273
|
+
cacheReadTokens,
|
|
274
|
+
cacheWriteTokens,
|
|
275
|
+
costUsdMicro,
|
|
276
|
+
durationMs,
|
|
277
|
+
occurredAt: finishedAt,
|
|
278
|
+
},
|
|
279
|
+
]);
|
|
280
|
+
if (args.onTurnFinalized) {
|
|
281
|
+
try {
|
|
282
|
+
await args.onTurnFinalized(finalTurn);
|
|
283
|
+
}
|
|
284
|
+
catch (e) {
|
|
285
|
+
logger.error('runChatTurn onTurnFinalized hook threw', {
|
|
286
|
+
turnId,
|
|
287
|
+
error: e instanceof Error ? e.message : String(e),
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
},
|
|
292
|
+
onError: async ({ error }) => {
|
|
293
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
294
|
+
logger.error('runChatTurn streamText error', { turnId, message });
|
|
295
|
+
try {
|
|
296
|
+
await args.ports.turnStore.markFailed(turnId, {
|
|
297
|
+
code: 'stream_error',
|
|
298
|
+
message,
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
catch (e) {
|
|
302
|
+
logger.error('runChatTurn turnStore.markFailed failed', {
|
|
303
|
+
turnId,
|
|
304
|
+
error: e instanceof Error ? e.message : String(e),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
onAbort: async () => {
|
|
309
|
+
try {
|
|
310
|
+
await args.ports.turnStore.markAborted(turnId, 'client-abort');
|
|
311
|
+
}
|
|
312
|
+
catch (e) {
|
|
313
|
+
logger.error('runChatTurn turnStore.markAborted failed', {
|
|
314
|
+
turnId,
|
|
315
|
+
error: e instanceof Error ? e.message : String(e),
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
});
|
|
320
|
+
return stream.toUIMessageStreamResponse();
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Compose a turn id. Includes the request id when present so the
|
|
324
|
+
* persisted row can be cross-referenced against tracing without an
|
|
325
|
+
* extra join. Random suffix breaks ties on rapid same-ms turns.
|
|
326
|
+
*/
|
|
327
|
+
function makeTurnId(startedAt, requestId) {
|
|
328
|
+
const epochMs = startedAt.getTime();
|
|
329
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
330
|
+
return requestId ? `turn_${epochMs}_${requestId}_${rand}` : `turn_${epochMs}_${rand}`;
|
|
331
|
+
}
|
|
332
|
+
function extractText(message) {
|
|
333
|
+
if (!message)
|
|
334
|
+
return '';
|
|
335
|
+
return message.parts
|
|
336
|
+
.filter((p) => p.type === 'text')
|
|
337
|
+
.map((p) => p.text)
|
|
338
|
+
.join('')
|
|
339
|
+
.trim();
|
|
340
|
+
}
|
|
341
|
+
//# sourceMappingURL=run-chat-turn.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-chat-turn.js","sourceRoot":"","sources":["../../src/runtime/run-chat-turn.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure window-math helpers for `QuotaStore` implementations.
|
|
3
|
+
*
|
|
4
|
+
* The kernel itself does NOT use these directly — `runChatTurn` calls
|
|
5
|
+
* `quotaStore.check()` and `quotaStore.record()` and trusts the port
|
|
6
|
+
* to handle the storage. These helpers are EXPORTED for hosts so each
|
|
7
|
+
* port impl computes consistent counter keys, TTLs, and reset times
|
|
8
|
+
* without re-deriving them.
|
|
9
|
+
*
|
|
10
|
+
* Naming + TTL conventions:
|
|
11
|
+
* - Counter key: `ai_quota:<metric>:<tenant>:<surface>:<window>`
|
|
12
|
+
* - Daily TTL: 26h (survives DST + late writes after midnight).
|
|
13
|
+
* - Hourly TTL: 65m (survives clock skew + late writes).
|
|
14
|
+
*
|
|
15
|
+
* All windows anchor to UTC. Multi-region replicas compute identical
|
|
16
|
+
* keys without coordinating clocks. Hosts that want a different
|
|
17
|
+
* timezone anchor should write their own helpers — most production
|
|
18
|
+
* deployments are happiest on UTC.
|
|
19
|
+
*/
|
|
20
|
+
export declare const DAY_SECONDS: number;
|
|
21
|
+
export declare const HOUR_SECONDS: number;
|
|
22
|
+
export declare const DAY_TTL_SECONDS: number;
|
|
23
|
+
export declare const HOUR_TTL_SECONDS: number;
|
|
24
|
+
/** UTC date key for daily windows. Format: `YYYY-MM-DD`. */
|
|
25
|
+
export declare function dayKeyUtc(now?: Date): string;
|
|
26
|
+
/** UTC hour key for hourly windows. Format: `YYYY-MM-DD-HH`. */
|
|
27
|
+
export declare function hourKeyUtc(now?: Date): string;
|
|
28
|
+
/** Unix seconds at the next UTC midnight from `now`. */
|
|
29
|
+
export declare function nextUtcMidnight(now?: Date): number;
|
|
30
|
+
/** Unix seconds at the next UTC hour boundary from `now`. */
|
|
31
|
+
export declare function nextUtcHour(now?: Date): number;
|
|
32
|
+
export interface WindowDescriptor {
|
|
33
|
+
/** Storage key — Redis, Postgres column, KV namespace — host's choice. */
|
|
34
|
+
key: string;
|
|
35
|
+
/** TTL in seconds for the storage backend (where applicable). */
|
|
36
|
+
ttl: number;
|
|
37
|
+
/** Unix seconds at which this window resets — render on the deny UX. */
|
|
38
|
+
resetAt: number;
|
|
39
|
+
}
|
|
40
|
+
interface WindowArgs {
|
|
41
|
+
tenantId: string;
|
|
42
|
+
surface: string;
|
|
43
|
+
now?: Date;
|
|
44
|
+
}
|
|
45
|
+
export declare function dailyTokensWindow({ tenantId, surface, now }: WindowArgs): WindowDescriptor;
|
|
46
|
+
export declare function dailyCostWindow({ tenantId, surface, now }: WindowArgs): WindowDescriptor;
|
|
47
|
+
export declare function hourlyToolCallsWindow({ tenantId, surface, now, }: WindowArgs): WindowDescriptor;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=windows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows.d.ts","sourceRoot":"","sources":["../src/windows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,WAAW,QAAe,CAAA;AACvC,eAAO,MAAM,YAAY,QAAU,CAAA;AACnC,eAAO,MAAM,eAAe,QAA6B,CAAA;AACzD,eAAO,MAAM,gBAAgB,QAAwB,CAAA;AAErD,4DAA4D;AAC5D,wBAAgB,SAAS,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAExD;AAED,gEAAgE;AAChE,wBAAgB,UAAU,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAEzD;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAK9D;AAED,6DAA6D;AAC7D,wBAAgB,WAAW,CAAC,GAAG,GAAE,IAAiB,GAAG,MAAM,CAa1D;AAED,MAAM,WAAW,gBAAgB;IAC7B,0EAA0E;IAC1E,GAAG,EAAE,MAAM,CAAA;IACX,iEAAiE;IACjE,GAAG,EAAE,MAAM,CAAA;IACX,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,UAAU;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,GAAG,CAAC,EAAE,IAAI,CAAA;CACb;AAED,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAgB,EAAE,EAAE,UAAU,GAAG,gBAAgB,CAMvG;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAgB,EAAE,EAAE,UAAU,GAAG,gBAAgB,CAMrG;AAED,wBAAgB,qBAAqB,CAAC,EAClC,QAAQ,EACR,OAAO,EACP,GAAgB,GACnB,EAAE,UAAU,GAAG,gBAAgB,CAM/B"}
|
package/dist/windows.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure window-math helpers for `QuotaStore` implementations.
|
|
3
|
+
*
|
|
4
|
+
* The kernel itself does NOT use these directly — `runChatTurn` calls
|
|
5
|
+
* `quotaStore.check()` and `quotaStore.record()` and trusts the port
|
|
6
|
+
* to handle the storage. These helpers are EXPORTED for hosts so each
|
|
7
|
+
* port impl computes consistent counter keys, TTLs, and reset times
|
|
8
|
+
* without re-deriving them.
|
|
9
|
+
*
|
|
10
|
+
* Naming + TTL conventions:
|
|
11
|
+
* - Counter key: `ai_quota:<metric>:<tenant>:<surface>:<window>`
|
|
12
|
+
* - Daily TTL: 26h (survives DST + late writes after midnight).
|
|
13
|
+
* - Hourly TTL: 65m (survives clock skew + late writes).
|
|
14
|
+
*
|
|
15
|
+
* All windows anchor to UTC. Multi-region replicas compute identical
|
|
16
|
+
* keys without coordinating clocks. Hosts that want a different
|
|
17
|
+
* timezone anchor should write their own helpers — most production
|
|
18
|
+
* deployments are happiest on UTC.
|
|
19
|
+
*/
|
|
20
|
+
export const DAY_SECONDS = 60 * 60 * 24;
|
|
21
|
+
export const HOUR_SECONDS = 60 * 60;
|
|
22
|
+
export const DAY_TTL_SECONDS = DAY_SECONDS + HOUR_SECONDS; // 26h
|
|
23
|
+
export const HOUR_TTL_SECONDS = HOUR_SECONDS + 60 * 5; // 65m
|
|
24
|
+
/** UTC date key for daily windows. Format: `YYYY-MM-DD`. */
|
|
25
|
+
export function dayKeyUtc(now = new Date()) {
|
|
26
|
+
return now.toISOString().slice(0, 10);
|
|
27
|
+
}
|
|
28
|
+
/** UTC hour key for hourly windows. Format: `YYYY-MM-DD-HH`. */
|
|
29
|
+
export function hourKeyUtc(now = new Date()) {
|
|
30
|
+
return now.toISOString().slice(0, 13);
|
|
31
|
+
}
|
|
32
|
+
/** Unix seconds at the next UTC midnight from `now`. */
|
|
33
|
+
export function nextUtcMidnight(now = new Date()) {
|
|
34
|
+
const next = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, 0, 0, 0, 0));
|
|
35
|
+
return Math.floor(next.getTime() / 1000);
|
|
36
|
+
}
|
|
37
|
+
/** Unix seconds at the next UTC hour boundary from `now`. */
|
|
38
|
+
export function nextUtcHour(now = new Date()) {
|
|
39
|
+
const next = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours() + 1, 0, 0, 0));
|
|
40
|
+
return Math.floor(next.getTime() / 1000);
|
|
41
|
+
}
|
|
42
|
+
export function dailyTokensWindow({ tenantId, surface, now = new Date() }) {
|
|
43
|
+
return {
|
|
44
|
+
key: `ai_quota:tokens:${tenantId}:${surface}:${dayKeyUtc(now)}`,
|
|
45
|
+
ttl: DAY_TTL_SECONDS,
|
|
46
|
+
resetAt: nextUtcMidnight(now),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export function dailyCostWindow({ tenantId, surface, now = new Date() }) {
|
|
50
|
+
return {
|
|
51
|
+
key: `ai_quota:cost_cents:${tenantId}:${surface}:${dayKeyUtc(now)}`,
|
|
52
|
+
ttl: DAY_TTL_SECONDS,
|
|
53
|
+
resetAt: nextUtcMidnight(now),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export function hourlyToolCallsWindow({ tenantId, surface, now = new Date(), }) {
|
|
57
|
+
return {
|
|
58
|
+
key: `ai_quota:tool_calls:${tenantId}:${surface}:${hourKeyUtc(now)}`,
|
|
59
|
+
ttl: HOUR_TTL_SECONDS,
|
|
60
|
+
resetAt: nextUtcHour(now),
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=windows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"windows.js","sourceRoot":"","sources":["../src/windows.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;AACvC,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,GAAG,EAAE,CAAA;AACnC,MAAM,CAAC,MAAM,eAAe,GAAG,WAAW,GAAG,YAAY,CAAA,CAAC,MAAM;AAChE,MAAM,CAAC,MAAM,gBAAgB,GAAG,YAAY,GAAG,EAAE,GAAG,CAAC,CAAA,CAAC,MAAM;AAE5D,4DAA4D;AAC5D,MAAM,UAAU,SAAS,CAAC,MAAY,IAAI,IAAI,EAAE;IAC5C,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACzC,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,UAAU,CAAC,MAAY,IAAI,IAAI,EAAE;IAC7C,OAAO,GAAG,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACzC,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,eAAe,CAAC,MAAY,IAAI,IAAI,EAAE;IAClD,MAAM,IAAI,GAAG,IAAI,IAAI,CACjB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CACtF,CAAA;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAA;AAC5C,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,WAAW,CAAC,MAAY,IAAI,IAAI,EAAE;IAC9C,MAAM,IAAI,GAAG,IAAI,IAAI,CACjB,IAAI,CAAC,GAAG,CACJ,GAAG,CAAC,cAAc,EAAE,EACpB,GAAG,CAAC,WAAW,EAAE,EACjB,GAAG,CAAC,UAAU,EAAE,EAChB,GAAG,CAAC,WAAW,EAAE,GAAG,CAAC,EACrB,CAAC,EACD,CAAC,EACD,CAAC,CACJ,CACJ,CAAA;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAA;AAC5C,CAAC;AAiBD,MAAM,UAAU,iBAAiB,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAc;IACjF,OAAO;QACH,GAAG,EAAE,mBAAmB,QAAQ,IAAI,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE;QAC/D,GAAG,EAAE,eAAe;QACpB,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC;KAChC,CAAA;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,GAAG,IAAI,IAAI,EAAE,EAAc;IAC/E,OAAO;QACH,GAAG,EAAE,uBAAuB,QAAQ,IAAI,OAAO,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE;QACnE,GAAG,EAAE,eAAe;QACpB,OAAO,EAAE,eAAe,CAAC,GAAG,CAAC;KAChC,CAAA;AACL,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,EAClC,QAAQ,EACR,OAAO,EACP,GAAG,GAAG,IAAI,IAAI,EAAE,GACP;IACT,OAAO;QACH,GAAG,EAAE,uBAAuB,QAAQ,IAAI,OAAO,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE;QACpE,GAAG,EAAE,gBAAgB;QACrB,OAAO,EAAE,WAAW,CAAC,GAAG,CAAC;KAC5B,CAAA;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maestro-core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3",
|
|
4
4
|
"description": "Runtime kernel for the Maestro agent platform — tool envelope, tool definition factory, base context, port interfaces, and AI-SDK + MCP adapters.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Costa Software",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"test": "vitest run",
|
|
59
59
|
"lint": "echo \"(lint not yet configured)\" && exit 0",
|
|
60
60
|
"typecheck": "tsc -p tsconfig.build.json --noEmit",
|
|
61
|
-
"clean": "rm -rf dist .turbo *.tsbuildinfo"
|
|
61
|
+
"clean": "rm -rf dist .turbo *.tsbuildinfo",
|
|
62
|
+
"prepublishOnly": "rm -rf dist .turbo *.tsbuildinfo && tsc -p tsconfig.build.json"
|
|
62
63
|
},
|
|
63
64
|
"peerDependencies": {
|
|
64
65
|
"@ai-sdk/anthropic": "^3.0.0",
|