@blockrun/clawrouter 0.9.13 → 0.9.14
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/cli.js +15 -224
- package/dist/cli.js.map +1 -1
- package/dist/index.js +15 -224
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1356,7 +1356,7 @@ function route(prompt, systemPrompt, maxOutputTokens, options) {
|
|
|
1356
1356
|
|
|
1357
1357
|
// src/models.ts
|
|
1358
1358
|
var MODEL_ALIASES = {
|
|
1359
|
-
// Claude
|
|
1359
|
+
// Claude - short names
|
|
1360
1360
|
claude: "anthropic/claude-sonnet-4",
|
|
1361
1361
|
sonnet: "anthropic/claude-sonnet-4",
|
|
1362
1362
|
opus: "anthropic/claude-opus-4.6",
|
|
@@ -1364,6 +1364,11 @@ var MODEL_ALIASES = {
|
|
|
1364
1364
|
"opus-46": "anthropic/claude-opus-4.6",
|
|
1365
1365
|
"opus-45": "anthropic/claude-opus-4.5",
|
|
1366
1366
|
haiku: "anthropic/claude-haiku-4.5",
|
|
1367
|
+
// Claude - provider/shortname patterns (common in agent frameworks)
|
|
1368
|
+
"anthropic/sonnet": "anthropic/claude-sonnet-4",
|
|
1369
|
+
"anthropic/opus": "anthropic/claude-opus-4.6",
|
|
1370
|
+
"anthropic/haiku": "anthropic/claude-haiku-4.5",
|
|
1371
|
+
"anthropic/claude": "anthropic/claude-sonnet-4",
|
|
1367
1372
|
// OpenAI
|
|
1368
1373
|
gpt: "openai/gpt-4o",
|
|
1369
1374
|
gpt4: "openai/gpt-4o",
|
|
@@ -3334,174 +3339,6 @@ var PROXY_PORT = (() => {
|
|
|
3334
3339
|
return DEFAULT_PORT;
|
|
3335
3340
|
})();
|
|
3336
3341
|
|
|
3337
|
-
// src/journal.ts
|
|
3338
|
-
var DEFAULT_CONFIG2 = {
|
|
3339
|
-
maxEntries: 100,
|
|
3340
|
-
maxAgeMs: 24 * 60 * 60 * 1e3,
|
|
3341
|
-
// 24 hours
|
|
3342
|
-
maxEventsPerResponse: 5
|
|
3343
|
-
};
|
|
3344
|
-
var SessionJournal = class {
|
|
3345
|
-
journals = /* @__PURE__ */ new Map();
|
|
3346
|
-
config;
|
|
3347
|
-
constructor(config) {
|
|
3348
|
-
this.config = { ...DEFAULT_CONFIG2, ...config };
|
|
3349
|
-
}
|
|
3350
|
-
/**
|
|
3351
|
-
* Extract key events from assistant response content.
|
|
3352
|
-
* Looks for patterns like "I created...", "I fixed...", "Successfully..."
|
|
3353
|
-
*/
|
|
3354
|
-
extractEvents(content) {
|
|
3355
|
-
if (!content || typeof content !== "string") {
|
|
3356
|
-
return [];
|
|
3357
|
-
}
|
|
3358
|
-
const events = [];
|
|
3359
|
-
const seen = /* @__PURE__ */ new Set();
|
|
3360
|
-
const patterns = [
|
|
3361
|
-
// Creation patterns
|
|
3362
|
-
/I (?:also |then |have |)?(?:created|implemented|added|wrote|built|generated|set up|initialized) ([^.!?\n]{10,150})/gi,
|
|
3363
|
-
// Fix patterns
|
|
3364
|
-
/I (?:also |then |have |)?(?:fixed|resolved|solved|patched|corrected|addressed|debugged) ([^.!?\n]{10,150})/gi,
|
|
3365
|
-
// Completion patterns
|
|
3366
|
-
/I (?:also |then |have |)?(?:completed|finished|done with|wrapped up) ([^.!?\n]{10,150})/gi,
|
|
3367
|
-
// Update patterns
|
|
3368
|
-
/I (?:also |then |have |)?(?:updated|modified|changed|refactored|improved|enhanced|optimized) ([^.!?\n]{10,150})/gi,
|
|
3369
|
-
// Success patterns
|
|
3370
|
-
/Successfully ([^.!?\n]{10,150})/gi,
|
|
3371
|
-
// Tool usage patterns (when agent uses tools)
|
|
3372
|
-
/I (?:also |then |have |)?(?:ran|executed|called|invoked) ([^.!?\n]{10,100})/gi
|
|
3373
|
-
];
|
|
3374
|
-
for (const pattern of patterns) {
|
|
3375
|
-
pattern.lastIndex = 0;
|
|
3376
|
-
let match;
|
|
3377
|
-
while ((match = pattern.exec(content)) !== null) {
|
|
3378
|
-
const action = match[0].trim();
|
|
3379
|
-
const normalized = action.toLowerCase();
|
|
3380
|
-
if (seen.has(normalized)) {
|
|
3381
|
-
continue;
|
|
3382
|
-
}
|
|
3383
|
-
if (action.length >= 15 && action.length <= 200) {
|
|
3384
|
-
events.push(action);
|
|
3385
|
-
seen.add(normalized);
|
|
3386
|
-
}
|
|
3387
|
-
if (events.length >= this.config.maxEventsPerResponse) {
|
|
3388
|
-
break;
|
|
3389
|
-
}
|
|
3390
|
-
}
|
|
3391
|
-
if (events.length >= this.config.maxEventsPerResponse) {
|
|
3392
|
-
break;
|
|
3393
|
-
}
|
|
3394
|
-
}
|
|
3395
|
-
return events;
|
|
3396
|
-
}
|
|
3397
|
-
/**
|
|
3398
|
-
* Record events to the session journal.
|
|
3399
|
-
*/
|
|
3400
|
-
record(sessionId, events, model) {
|
|
3401
|
-
if (!sessionId || !events.length) {
|
|
3402
|
-
return;
|
|
3403
|
-
}
|
|
3404
|
-
const journal = this.journals.get(sessionId) || [];
|
|
3405
|
-
const now = Date.now();
|
|
3406
|
-
for (const action of events) {
|
|
3407
|
-
journal.push({
|
|
3408
|
-
timestamp: now,
|
|
3409
|
-
action,
|
|
3410
|
-
model
|
|
3411
|
-
});
|
|
3412
|
-
}
|
|
3413
|
-
const cutoff = now - this.config.maxAgeMs;
|
|
3414
|
-
const trimmed = journal.filter((e) => e.timestamp > cutoff).slice(-this.config.maxEntries);
|
|
3415
|
-
this.journals.set(sessionId, trimmed);
|
|
3416
|
-
}
|
|
3417
|
-
/**
|
|
3418
|
-
* Check if the user message indicates a need for historical context.
|
|
3419
|
-
*/
|
|
3420
|
-
needsContext(lastUserMessage) {
|
|
3421
|
-
if (!lastUserMessage || typeof lastUserMessage !== "string") {
|
|
3422
|
-
return false;
|
|
3423
|
-
}
|
|
3424
|
-
const lower = lastUserMessage.toLowerCase();
|
|
3425
|
-
const triggers = [
|
|
3426
|
-
// Direct questions about past work
|
|
3427
|
-
"what did you do",
|
|
3428
|
-
"what have you done",
|
|
3429
|
-
"what did we do",
|
|
3430
|
-
"what have we done",
|
|
3431
|
-
// Temporal references
|
|
3432
|
-
"earlier",
|
|
3433
|
-
"before",
|
|
3434
|
-
"previously",
|
|
3435
|
-
"this session",
|
|
3436
|
-
"today",
|
|
3437
|
-
"so far",
|
|
3438
|
-
// Summary requests
|
|
3439
|
-
"remind me",
|
|
3440
|
-
"summarize",
|
|
3441
|
-
"summary of",
|
|
3442
|
-
"recap",
|
|
3443
|
-
// Progress inquiries
|
|
3444
|
-
"your work",
|
|
3445
|
-
"your progress",
|
|
3446
|
-
"accomplished",
|
|
3447
|
-
"achievements",
|
|
3448
|
-
"completed tasks"
|
|
3449
|
-
];
|
|
3450
|
-
return triggers.some((t) => lower.includes(t));
|
|
3451
|
-
}
|
|
3452
|
-
/**
|
|
3453
|
-
* Format the journal for injection into system message.
|
|
3454
|
-
* Returns null if journal is empty.
|
|
3455
|
-
*/
|
|
3456
|
-
format(sessionId) {
|
|
3457
|
-
const journal = this.journals.get(sessionId);
|
|
3458
|
-
if (!journal?.length) {
|
|
3459
|
-
return null;
|
|
3460
|
-
}
|
|
3461
|
-
const lines = journal.map((e) => {
|
|
3462
|
-
const time = new Date(e.timestamp).toLocaleTimeString("en-US", {
|
|
3463
|
-
hour: "2-digit",
|
|
3464
|
-
minute: "2-digit",
|
|
3465
|
-
hour12: true
|
|
3466
|
-
});
|
|
3467
|
-
return `- ${time}: ${e.action}`;
|
|
3468
|
-
});
|
|
3469
|
-
return `[Session Memory - Key Actions]
|
|
3470
|
-
${lines.join("\n")}`;
|
|
3471
|
-
}
|
|
3472
|
-
/**
|
|
3473
|
-
* Get the raw journal entries for a session (for debugging/testing).
|
|
3474
|
-
*/
|
|
3475
|
-
getEntries(sessionId) {
|
|
3476
|
-
return this.journals.get(sessionId) || [];
|
|
3477
|
-
}
|
|
3478
|
-
/**
|
|
3479
|
-
* Clear journal for a specific session.
|
|
3480
|
-
*/
|
|
3481
|
-
clear(sessionId) {
|
|
3482
|
-
this.journals.delete(sessionId);
|
|
3483
|
-
}
|
|
3484
|
-
/**
|
|
3485
|
-
* Clear all journals.
|
|
3486
|
-
*/
|
|
3487
|
-
clearAll() {
|
|
3488
|
-
this.journals.clear();
|
|
3489
|
-
}
|
|
3490
|
-
/**
|
|
3491
|
-
* Get stats about the journal.
|
|
3492
|
-
*/
|
|
3493
|
-
getStats() {
|
|
3494
|
-
let totalEntries = 0;
|
|
3495
|
-
for (const entries of this.journals.values()) {
|
|
3496
|
-
totalEntries += entries.length;
|
|
3497
|
-
}
|
|
3498
|
-
return {
|
|
3499
|
-
sessions: this.journals.size,
|
|
3500
|
-
totalEntries
|
|
3501
|
-
};
|
|
3502
|
-
}
|
|
3503
|
-
};
|
|
3504
|
-
|
|
3505
3342
|
// src/proxy.ts
|
|
3506
3343
|
var BLOCKRUN_API = "https://blockrun.ai/api";
|
|
3507
3344
|
var AUTO_MODEL = "blockrun/auto";
|
|
@@ -3911,7 +3748,6 @@ async function startProxy(options) {
|
|
|
3911
3748
|
const deduplicator = new RequestDeduplicator();
|
|
3912
3749
|
const responseCache = new ResponseCache(options.cacheConfig);
|
|
3913
3750
|
const sessionStore = new SessionStore(options.sessionConfig);
|
|
3914
|
-
const sessionJournal = new SessionJournal();
|
|
3915
3751
|
const connections = /* @__PURE__ */ new Set();
|
|
3916
3752
|
const server = createServer(async (req, res) => {
|
|
3917
3753
|
req.on("error", (err) => {
|
|
@@ -4007,8 +3843,7 @@ async function startProxy(options) {
|
|
|
4007
3843
|
deduplicator,
|
|
4008
3844
|
balanceMonitor,
|
|
4009
3845
|
sessionStore,
|
|
4010
|
-
responseCache
|
|
4011
|
-
sessionJournal
|
|
3846
|
+
responseCache
|
|
4012
3847
|
);
|
|
4013
3848
|
} catch (err) {
|
|
4014
3849
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
@@ -4209,7 +4044,7 @@ async function tryModelRequest(upstreamUrl, method, headers, body, modelId, maxT
|
|
|
4209
4044
|
};
|
|
4210
4045
|
}
|
|
4211
4046
|
}
|
|
4212
|
-
async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator, balanceMonitor, sessionStore, responseCache
|
|
4047
|
+
async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, deduplicator, balanceMonitor, sessionStore, responseCache) {
|
|
4213
4048
|
const startTime = Date.now();
|
|
4214
4049
|
const upstreamUrl = `${apiBase}${req.url}`;
|
|
4215
4050
|
const bodyChunks = [];
|
|
@@ -4222,38 +4057,13 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4222
4057
|
let modelId = "";
|
|
4223
4058
|
let maxTokens = 4096;
|
|
4224
4059
|
let routingProfile = null;
|
|
4225
|
-
let accumulatedContent = "";
|
|
4226
4060
|
const isChatCompletion = req.url?.includes("/chat/completions");
|
|
4227
|
-
const sessionId = getSessionId(req.headers);
|
|
4228
4061
|
if (isChatCompletion && body.length > 0) {
|
|
4229
4062
|
try {
|
|
4230
4063
|
const parsed = JSON.parse(body.toString());
|
|
4231
4064
|
isStreaming = parsed.stream === true;
|
|
4232
4065
|
modelId = parsed.model || "";
|
|
4233
4066
|
maxTokens = parsed.max_tokens || 4096;
|
|
4234
|
-
if (sessionId && Array.isArray(parsed.messages)) {
|
|
4235
|
-
const messages = parsed.messages;
|
|
4236
|
-
const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
|
|
4237
|
-
const lastContent = typeof lastUserMsg?.content === "string" ? lastUserMsg.content : "";
|
|
4238
|
-
if (sessionJournal.needsContext(lastContent)) {
|
|
4239
|
-
const journalText = sessionJournal.format(sessionId);
|
|
4240
|
-
if (journalText) {
|
|
4241
|
-
const sysIdx = messages.findIndex((m) => m.role === "system");
|
|
4242
|
-
if (sysIdx >= 0 && typeof messages[sysIdx].content === "string") {
|
|
4243
|
-
messages[sysIdx] = {
|
|
4244
|
-
...messages[sysIdx],
|
|
4245
|
-
content: journalText + "\n\n" + messages[sysIdx].content
|
|
4246
|
-
};
|
|
4247
|
-
} else {
|
|
4248
|
-
messages.unshift({ role: "system", content: journalText });
|
|
4249
|
-
}
|
|
4250
|
-
parsed.messages = messages;
|
|
4251
|
-
console.log(
|
|
4252
|
-
`[ClawRouter] Injected session journal (${journalText.length} chars) for session ${sessionId.slice(0, 8)}...`
|
|
4253
|
-
);
|
|
4254
|
-
}
|
|
4255
|
-
}
|
|
4256
|
-
}
|
|
4257
4067
|
let bodyModified = false;
|
|
4258
4068
|
if (parsed.stream === true) {
|
|
4259
4069
|
parsed.stream = false;
|
|
@@ -4293,18 +4103,18 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4293
4103
|
latencyMs: 0
|
|
4294
4104
|
});
|
|
4295
4105
|
} else {
|
|
4296
|
-
const
|
|
4106
|
+
const sessionId = getSessionId(
|
|
4297
4107
|
req.headers
|
|
4298
4108
|
);
|
|
4299
|
-
const existingSession =
|
|
4109
|
+
const existingSession = sessionId ? sessionStore.getSession(sessionId) : void 0;
|
|
4300
4110
|
if (existingSession) {
|
|
4301
4111
|
console.log(
|
|
4302
|
-
`[ClawRouter] Session ${
|
|
4112
|
+
`[ClawRouter] Session ${sessionId?.slice(0, 8)}... using pinned model: ${existingSession.model}`
|
|
4303
4113
|
);
|
|
4304
4114
|
parsed.model = existingSession.model;
|
|
4305
4115
|
modelId = existingSession.model;
|
|
4306
4116
|
bodyModified = true;
|
|
4307
|
-
sessionStore.touchSession(
|
|
4117
|
+
sessionStore.touchSession(sessionId);
|
|
4308
4118
|
} else {
|
|
4309
4119
|
const messages = parsed.messages;
|
|
4310
4120
|
let lastUserMsg;
|
|
@@ -4333,10 +4143,10 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4333
4143
|
parsed.model = routingDecision.model;
|
|
4334
4144
|
modelId = routingDecision.model;
|
|
4335
4145
|
bodyModified = true;
|
|
4336
|
-
if (
|
|
4337
|
-
sessionStore.setSession(
|
|
4146
|
+
if (sessionId) {
|
|
4147
|
+
sessionStore.setSession(sessionId, routingDecision.model, routingDecision.tier);
|
|
4338
4148
|
console.log(
|
|
4339
|
-
`[ClawRouter] Session ${
|
|
4149
|
+
`[ClawRouter] Session ${sessionId.slice(0, 8)}... pinned to model: ${routingDecision.model}`
|
|
4340
4150
|
);
|
|
4341
4151
|
}
|
|
4342
4152
|
options.onRouted?.(routingDecision);
|
|
@@ -4671,9 +4481,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4671
4481
|
const content = stripThinkingTokens(rawContent);
|
|
4672
4482
|
const role = choice.message?.role ?? choice.delta?.role ?? "assistant";
|
|
4673
4483
|
const index = choice.index ?? 0;
|
|
4674
|
-
if (content) {
|
|
4675
|
-
accumulatedContent += content;
|
|
4676
|
-
}
|
|
4677
4484
|
const roleChunk = {
|
|
4678
4485
|
...baseChunk,
|
|
4679
4486
|
choices: [{ index, delta: { role }, logprobs: null, finish_reason: null }]
|
|
@@ -4787,22 +4594,6 @@ async function proxyRequest(req, res, apiBase, payFetch, options, routerOpts, de
|
|
|
4787
4594
|
});
|
|
4788
4595
|
console.log(`[ClawRouter] Cached response for ${modelId} (${responseBody.length} bytes)`);
|
|
4789
4596
|
}
|
|
4790
|
-
try {
|
|
4791
|
-
const rspJson = JSON.parse(responseBody.toString());
|
|
4792
|
-
if (rspJson.choices?.[0]?.message?.content) {
|
|
4793
|
-
accumulatedContent = rspJson.choices[0].message.content;
|
|
4794
|
-
}
|
|
4795
|
-
} catch {
|
|
4796
|
-
}
|
|
4797
|
-
}
|
|
4798
|
-
if (sessionId && accumulatedContent) {
|
|
4799
|
-
const events = sessionJournal.extractEvents(accumulatedContent);
|
|
4800
|
-
if (events.length > 0) {
|
|
4801
|
-
sessionJournal.record(sessionId, events, actualModelUsed);
|
|
4802
|
-
console.log(
|
|
4803
|
-
`[ClawRouter] Recorded ${events.length} events to session journal for session ${sessionId.slice(0, 8)}...`
|
|
4804
|
-
);
|
|
4805
|
-
}
|
|
4806
4597
|
}
|
|
4807
4598
|
if (estimatedCostMicros !== void 0) {
|
|
4808
4599
|
balanceMonitor.deductEstimated(estimatedCostMicros);
|