feat: implement Tier 3 features — lane queue, credential redaction, token dashboard, xAI, Voyage AI
- Lane Queue: per-session FIFO queue in gateway replacing reject-when-busy (9 tests) - Credential Redaction: redactConfig() expanded to cover 18+ secret fields (16 tests) - Web UI Token Dashboard: system.tokenUsage endpoint + Usage page with summary cards - xAI (Grok) Provider: OpenAI-compatible client with model pricing - Voyage AI Embeddings: new embedding provider with configurable dimensions (5 tests) - Update gap analysis: 90→95 match (70%→74%), Tier 3 section marked DONE - Update state.json: test count 1001→1034, add tier3_completion entry Total: 1034 tests passing across 85 files, typecheck clean
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
* LaneQueue — per-lane FIFO queue for serialising async work.
|
||||
*
|
||||
* Each "lane" (keyed by session ID) processes work items one at a time.
|
||||
* If a lane is idle, work starts immediately. If it's busy, the work
|
||||
* is queued and a promise is returned that resolves when it's this
|
||||
* entry's turn to execute.
|
||||
*
|
||||
* Independent lanes run in parallel — only items within the same lane
|
||||
* are serialised.
|
||||
*/
|
||||
|
||||
interface QueueEntry<T = unknown> {
|
||||
work: () => Promise<T>;
|
||||
resolve: (value: T) => void;
|
||||
reject: (reason: unknown) => void;
|
||||
}
|
||||
|
||||
interface Lane {
|
||||
active: boolean;
|
||||
queue: QueueEntry[];
|
||||
}
|
||||
|
||||
export class LaneQueue {
|
||||
private lanes: Map<string, Lane> = new Map();
|
||||
|
||||
/**
|
||||
* Enqueue a unit of work for the given lane.
|
||||
* Returns a promise that resolves with the work's return value
|
||||
* once it has been executed (which may be immediately if the lane is idle).
|
||||
*/
|
||||
async enqueue<T>(laneId: string, work: () => Promise<T>): Promise<T> {
|
||||
let lane = this.lanes.get(laneId);
|
||||
if (!lane) {
|
||||
lane = { active: false, queue: [] };
|
||||
this.lanes.set(laneId, lane);
|
||||
}
|
||||
|
||||
// If nothing is running on this lane, execute immediately
|
||||
if (!lane.active) {
|
||||
lane.active = true;
|
||||
try {
|
||||
return await work();
|
||||
} finally {
|
||||
lane.active = false;
|
||||
this.processNext(laneId);
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, queue the work and return a deferred promise
|
||||
return new Promise<T>((resolve, reject) => {
|
||||
lane!.queue.push({
|
||||
work: work as () => Promise<unknown>,
|
||||
resolve: resolve as (value: unknown) => void,
|
||||
reject,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** Check if a lane currently has active work executing. */
|
||||
isProcessing(laneId: string): boolean {
|
||||
return this.lanes.get(laneId)?.active ?? false;
|
||||
}
|
||||
|
||||
/** Get the number of pending (not yet started) items in a lane. */
|
||||
queueLength(laneId: string): number {
|
||||
return this.lanes.get(laneId)?.queue.length ?? 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel all pending entries in a lane.
|
||||
* Active work is NOT interrupted — only queued items are rejected.
|
||||
* Rejected promises receive an Error with message "Lane cancelled".
|
||||
*/
|
||||
cancel(laneId: string): void {
|
||||
const lane = this.lanes.get(laneId);
|
||||
if (!lane) return;
|
||||
|
||||
const pending = lane.queue.splice(0);
|
||||
for (const entry of pending) {
|
||||
entry.reject(new Error('Lane cancelled'));
|
||||
}
|
||||
|
||||
// Clean up empty idle lanes
|
||||
if (!lane.active && lane.queue.length === 0) {
|
||||
this.lanes.delete(laneId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the next queued entry for a lane (called after current work finishes).
|
||||
* Runs asynchronously so the caller's finally block completes first.
|
||||
*/
|
||||
private processNext(laneId: string): void {
|
||||
const lane = this.lanes.get(laneId);
|
||||
if (!lane) return;
|
||||
|
||||
const entry = lane.queue.shift();
|
||||
if (!entry) {
|
||||
// Lane is empty — clean up
|
||||
this.lanes.delete(laneId);
|
||||
return;
|
||||
}
|
||||
|
||||
lane.active = true;
|
||||
entry.work()
|
||||
.then((value) => entry.resolve(value))
|
||||
.catch((err) => entry.reject(err))
|
||||
.finally(() => {
|
||||
lane.active = false;
|
||||
this.processNext(laneId);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user