agent-tool-forge 0.4.1 → 0.4.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.
@@ -207,11 +207,14 @@ function serveWidgetFile(req, res, widgetDir, errorFn) {
207
207
  * @param {object} [options]
208
208
  * @param {string} [options.widgetDir] — directory for /widget/* static files (defaults to <project>/widget)
209
209
  * @param {function} [options.mcpHandler] — optional async (req, res) handler for /mcp route
210
+ * @param {function} [options.customRoutes] — optional async (req, res, ctx) => boolean; called before
211
+ * the 404 fallback. Return true if the request was handled, false to fall through to 404.
210
212
  * @returns {function(import('http').IncomingMessage, import('http').ServerResponse): Promise<void>}
211
213
  */
212
214
  export function createSidecarRouter(ctx, options = {}) {
213
215
  const widgetDir = options.widgetDir || resolve(__dirname, '..', 'widget');
214
216
  const mcpHandler = options.mcpHandler || null;
217
+ const customRoutes = options.customRoutes || null;
215
218
 
216
219
  return async (req, res) => {
217
220
  const url = new URL(req.url, 'http://localhost');
@@ -267,6 +270,12 @@ export function createSidecarRouter(ctx, options = {}) {
267
270
  return;
268
271
  }
269
272
 
273
+ // ── Custom routes (consumer-provided) ─────────────────────────────────
274
+ if (customRoutes) {
275
+ const handled = await customRoutes(req, res, ctx);
276
+ if (handled) return;
277
+ }
278
+
270
279
  // ── 404 fallback ───────────────────────────────────────────────────────
271
280
  sendJson(res, 404, { error: 'not found' });
272
281
  };
@@ -241,21 +241,6 @@ export class PostgresStore {
241
241
  `UPDATE tool_registry SET ${sets.join(', ')} WHERE tool_name = $${i}`, vals);
242
242
  }
243
243
 
244
- // ── Verifier results ──────────────────────────────────────────────────
245
-
246
- async insertVerifierResult(row) {
247
- const { rows } = await this._pool.query(
248
- `INSERT INTO verifier_results
249
- (session_id, tool_name, verifier_name, outcome, message, tool_call_input, tool_call_output, created_at)
250
- VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING id`,
251
- [
252
- row.session_id ?? null, row.tool_name, row.verifier_name, row.outcome,
253
- row.message ?? null, row.tool_call_input ?? null, row.tool_call_output ?? null,
254
- new Date().toISOString()
255
- ]
256
- );
257
- return rows[0]?.id ?? null;
258
- }
259
244
  }
260
245
 
261
246
  // ── PostgresPromptStore ────────────────────────────────────────────────────
@@ -611,6 +596,14 @@ export class PostgresEvalStore {
611
596
  );
612
597
  return rows;
613
598
  }
599
+
600
+ async listRuns(limit = 50, offset = 0) {
601
+ const { rows } = await this._pool.query(
602
+ `SELECT * FROM eval_runs ORDER BY run_at DESC LIMIT $1 OFFSET $2`,
603
+ [limit, offset]
604
+ );
605
+ return rows;
606
+ }
614
607
  }
615
608
 
616
609
  // ── PostgresChatAuditStore ─────────────────────────────────────────────────
@@ -636,6 +629,35 @@ export class PostgresChatAuditStore {
636
629
  );
637
630
  return rows[0]?.id ?? null;
638
631
  }
632
+
633
+ async getStats() {
634
+ const { rows } = await this._pool.query(`
635
+ SELECT
636
+ COUNT(*) AS total_sessions,
637
+ COALESCE(AVG(duration_ms), 0) AS avg_duration_ms,
638
+ COALESCE(
639
+ COUNT(*) FILTER (WHERE status_code >= 400)::float / NULLIF(COUNT(*), 0),
640
+ 0
641
+ ) AS error_rate,
642
+ COUNT(*) FILTER (WHERE created_at >= NOW() - INTERVAL '24 hours') AS messages_today
643
+ FROM chat_audit
644
+ `);
645
+ const row = rows[0];
646
+ return {
647
+ totalSessions: parseInt(row.total_sessions, 10),
648
+ avgDurationMs: Math.round(parseFloat(row.avg_duration_ms)),
649
+ errorRate: parseFloat(row.error_rate),
650
+ messagesToday: parseInt(row.messages_today, 10)
651
+ };
652
+ }
653
+
654
+ async getSessions(limit = 20, offset = 0) {
655
+ const { rows } = await this._pool.query(
656
+ `SELECT * FROM chat_audit ORDER BY created_at DESC LIMIT $1 OFFSET $2`,
657
+ [limit, offset]
658
+ );
659
+ return rows;
660
+ }
639
661
  }
640
662
 
641
663
  // ── PostgresVerifierStore ──────────────────────────────────────────────────
@@ -724,4 +746,31 @@ export class PostgresVerifierStore {
724
746
  `SELECT * FROM verifier_tool_bindings WHERE verifier_name = $1`, [verifierName]);
725
747
  return rows;
726
748
  }
749
+
750
+ async insertVerifierResult(row) {
751
+ const { rows } = await this._pool.query(
752
+ `INSERT INTO verifier_results
753
+ (session_id, tool_name, verifier_name, outcome, message, tool_call_input, tool_call_output, created_at)
754
+ VALUES ($1,$2,$3,$4,$5,$6,$7,$8) RETURNING id`,
755
+ [row.session_id ?? null, row.tool_name, row.verifier_name, row.outcome,
756
+ row.message ?? null, row.tool_call_input ?? null, row.tool_call_output ?? null,
757
+ new Date().toISOString()]
758
+ );
759
+ return rows[0]?.id ?? null;
760
+ }
761
+
762
+ async getResults(toolName = null, limit = 100) {
763
+ if (toolName) {
764
+ const { rows } = await this._pool.query(
765
+ `SELECT * FROM verifier_results WHERE tool_name = $1 ORDER BY created_at DESC LIMIT $2`,
766
+ [toolName, limit]
767
+ );
768
+ return rows;
769
+ }
770
+ const { rows } = await this._pool.query(
771
+ `SELECT * FROM verifier_results ORDER BY created_at DESC LIMIT $1`,
772
+ [limit]
773
+ );
774
+ return rows;
775
+ }
727
776
  }
package/lib/sidecar.js CHANGED
@@ -29,6 +29,8 @@ import { createDriftMonitor } from './drift-background.js';
29
29
  * @param {boolean} [options.autoListen=true] — start listening immediately
30
30
  * @param {boolean} [options.enableDrift=false] — start background drift monitor
31
31
  * @param {string} [options.widgetDir] — custom widget directory
32
+ * @param {function} [options.customRoutes] — async (req, res, ctx) => boolean; inject custom routes
33
+ * before the built-in 404 handler. Return true if the request was handled, false to fall through.
32
34
  * @returns {Promise<{ server: import('http').Server, ctx: object, close: () => void }>}
33
35
  */
34
36
  export async function createSidecar(config = {}, options = {}) {
@@ -40,6 +42,7 @@ export async function createSidecar(config = {}, options = {}) {
40
42
  autoListen = true,
41
43
  enableDrift = false,
42
44
  widgetDir,
45
+ customRoutes,
43
46
  } = options;
44
47
 
45
48
  // Merge defaults first so validateConfig sees a fully-populated object (M1).
@@ -69,6 +72,7 @@ export async function createSidecar(config = {}, options = {}) {
69
72
  // Build request handler
70
73
  const routerOpts = {};
71
74
  if (widgetDir) routerOpts.widgetDir = widgetDir;
75
+ if (customRoutes) routerOpts.customRoutes = customRoutes;
72
76
  const router = createSidecarRouter(ctx, routerOpts);
73
77
 
74
78
  // Create HTTP server
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-tool-forge",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Production LLM agent sidecar + Claude Code skill library for building, testing, and running tool-calling agents.",
5
5
  "keywords": [
6
6
  "llm",