Files
next-tjwater-agent/src/routes/learningToolBridge.ts
T

59 lines
2.7 KiB
TypeScript

import { type Express } from "express";
import { z } from "zod";
import { executeMemoryManager, executeSkillManager } from "../learning/toolManagers.js";
import { type MemoryStore } from "../memory/store.js";
import { isTrustedInternalBridgeRequest } from "../product/internal-bridge-auth.js";
import { getRuntimeSessionContext } from "../runtime/sessionContext.js";
import { type SkillStore } from "../skills/store.js";
const memoryInput = z.object({
action: z.enum(["add", "list", "replace", "remove"]),
scope: z.string().max(32),
reason: z.string().trim().min(1).max(2000).optional(),
content: z.string().max(20000).optional(),
target_id: z.string().max(128).optional(),
}).strict().refine((input) => input.action === "list" || Boolean(input.reason), {
path: ["reason"], message: "reason is required for writes, updates and removals",
});
const skillInput = z.object({
action: z.enum(["list", "write_skill", "remove_skill", "append_pattern", "remove_pattern", "write_reference", "remove_reference", "write_script", "remove_script"]),
skill_path: z.string().max(512),
reason: z.string().trim().min(1).max(2000).optional(),
content: z.string().max(200000).optional(),
file_path: z.string().max(512).optional(),
pattern: z.string().max(20000).optional(),
target_id: z.string().max(128).optional(),
}).strict().refine((input) => input.action === "list" || Boolean(input.reason), {
path: ["reason"], message: "reason is required for writes, updates and removals",
});
export const registerLearningToolBridge = (app: Express, options: {
internalToken: string;
memoryStore: MemoryStore;
skillStore: SkillStore;
}) => {
for (const name of ["memory-manager", "skill-manager"] as const) {
app.post(`/internal/tools/${name}`, async (req, res) => {
if (!isTrustedInternalBridgeRequest(req, options.internalToken)) {
res.status(403).json({ message: "forbidden" });
return;
}
const context = typeof req.body?.session_id === "string"
? getRuntimeSessionContext(req.body.session_id) : null;
if (!context) {
res.status(404).json({ message: "session context not found" });
return;
}
if (name === "memory-manager") {
const input = memoryInput.safeParse(req.body.input);
if (!input.success) { res.status(400).json({ message: "invalid memory input" }); return; }
res.json(await executeMemoryManager(options.memoryStore, context, input.data));
} else {
const input = skillInput.safeParse(req.body.input);
if (!input.success) { res.status(400).json({ message: "invalid skill input" }); return; }
res.json(await executeSkillManager(options.skillStore, context, input.data));
}
});
}
};