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.
- package/lib/forge-service.js +9 -0
- package/lib/postgres-store.js +64 -15
- package/lib/sidecar.js +4 -0
- package/package.json +1 -1
package/lib/forge-service.js
CHANGED
|
@@ -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
|
};
|
package/lib/postgres-store.js
CHANGED
|
@@ -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
|