refactor: dedupe heartbeat-title check; JOIN thread visibility; typed skill service guard (P2-43, P2-44, P2-45)
This commit is contained in:
1 parent
6d0d0dd4a0
commit
c47050964a
4 files changed
+35
-22
No files matched your search
@@ -3,6 +3,7 @@ import { taskInputSchema } from "@shared/contracts";
|
||||
import { IMPORTANT_DOC_TYPES } from "@shared/document-categories";
|
||||
import { canAssignTask, canUpdateTask } from "../agent-permissions";
|
||||
import { inferTaskType } from "../connector-matching";
|
||||
import { isHeartbeatTaskTitle } from "../orchestration/heartbeat-task";
|
||||
import { validateTaskCreation } from "../task-quality-gates";
|
||||
import { requireParam, type AgentApiServer } from "./server-core";
|
||||
|
||||
@@ -303,7 +304,7 @@ export function registerTaskRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "Current agent is not allowed to assign this task to the selected agent" });
|
||||
return;
|
||||
}
|
||||
const isHeartbeat = input.title.startsWith("[Heartbeat]");
|
||||
const isHeartbeat = isHeartbeatTaskTitle(input.title);
|
||||
const taskValidation = validateTaskCreation({ title: input.title, description: input.description ?? "" }, isHeartbeat);
|
||||
if (!taskValidation.valid) {
|
||||
server.json(res, 422, { error: taskValidation.errors.join("; ") });
|
||||
|
||||
@@ -132,13 +132,15 @@ export function buildThreadMethods(getDb: () => DatabaseSync, readAll: ReadAll)
|
||||
companyId: string,
|
||||
options: { limit?: number; offset?: number } = {},
|
||||
): ConversationEventRecord[] {
|
||||
if (!getThread(threadId, companyId)) return [];
|
||||
const limit = options.limit ?? 50;
|
||||
const offset = options.offset ?? 0;
|
||||
const rows = readAll<SqliteRow>(
|
||||
`SELECT id, thread_id, actor_id, actor_type, event_type, body, metadata_json, created_at
|
||||
FROM conversation_events WHERE thread_id = ? ORDER BY created_at ASC LIMIT ? OFFSET ?`,
|
||||
threadId, limit, offset,
|
||||
FROM conversation_events
|
||||
WHERE thread_id = ?
|
||||
AND thread_id IN (SELECT id FROM conversation_threads WHERE id = ? AND company_id = ?)
|
||||
ORDER BY created_at ASC LIMIT ? OFFSET ?`,
|
||||
threadId, threadId, companyId, limit, offset,
|
||||
);
|
||||
return rows.map((r) => ({
|
||||
id: r.id as string,
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
taskInputSchema,
|
||||
} from "@shared/contracts";
|
||||
import { fail, ok, type RegisterCoreHandlersDependencies } from "./ipc-core-common";
|
||||
import { isHeartbeatTaskTitle } from "./orchestration/heartbeat-task";
|
||||
import { validateTaskCreation, checkCapabilityMatch } from "./task-quality-gates";
|
||||
|
||||
export function registerCoreTaskGraphHandlers({
|
||||
@@ -100,7 +101,7 @@ export function registerCoreTaskGraphHandlers({
|
||||
}
|
||||
const isNew = !parsed.id;
|
||||
if (isNew) {
|
||||
const isHeartbeat = parsed.title.startsWith("[Heartbeat]");
|
||||
const isHeartbeat = isHeartbeatTaskTitle(parsed.title);
|
||||
const validation = validateTaskCreation(parsed, isHeartbeat);
|
||||
if (!validation.valid) {
|
||||
return fail("TASK_VALIDATION_FAILED", validation.errors.join("; "));
|
||||
|
||||
@@ -29,11 +29,13 @@ export function registerSkillHandlers({
|
||||
skillService,
|
||||
db,
|
||||
}: SkillHandlerDependencies) {
|
||||
function requireSkillService() {
|
||||
function requireSkillService():
|
||||
| { service: NonNullable<typeof skillService>; error?: undefined }
|
||||
| { service?: undefined; error: ReturnType<typeof fail> } {
|
||||
if (!skillService) {
|
||||
return fail("SERVICE_NOT_READY", "Skill service is not initialized.");
|
||||
return { error: fail("SERVICE_NOT_READY", "Skill service is not initialized.") };
|
||||
}
|
||||
return skillService;
|
||||
return { service: skillService };
|
||||
}
|
||||
|
||||
// ---- Import skill from source (GitHub URL, local path, raw URL, skills.sh) ----
|
||||
@@ -41,8 +43,9 @@ export function registerSkillHandlers({
|
||||
const parsed = importSkillSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
const result = await svc.importFromSource(parsed.companyId, parsed.source);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
@@ -55,8 +58,9 @@ export function registerSkillHandlers({
|
||||
const parsed = listSkillsSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
// Scan workspace .agents/skills/ directories for new skills before listing
|
||||
try {
|
||||
await svc.scanWorkspaceSkills(parsed.companyId);
|
||||
@@ -71,8 +75,9 @@ export function registerSkillHandlers({
|
||||
const parsed = getSkillDetailSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
return svc.getDetail(parsed.companyId, parsed.skillId);
|
||||
});
|
||||
|
||||
@@ -81,8 +86,9 @@ export function registerSkillHandlers({
|
||||
const parsed = deleteSkillSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
const result = await svc.deleteSkill(parsed.companyId, parsed.skillId);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
@@ -95,8 +101,9 @@ export function registerSkillHandlers({
|
||||
const parsed = assignSkillToAgentSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
const result = svc.assignToAgent(parsed.companyId, parsed.agentId, parsed.skillId);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
@@ -109,8 +116,9 @@ export function registerSkillHandlers({
|
||||
const parsed = removeSkillFromAgentSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
const result = svc.removeFromAgent(parsed.companyId, parsed.agentId, parsed.skillId);
|
||||
if (result.ok) {
|
||||
publishDomainChanged();
|
||||
@@ -123,8 +131,9 @@ export function registerSkillHandlers({
|
||||
const parsed = checkSkillUpdateSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const svc = requireSkillService();
|
||||
if ("ok" in svc) return svc;
|
||||
const svcResult = requireSkillService();
|
||||
if (svcResult.error) return svcResult.error;
|
||||
const svc = svcResult.service;
|
||||
return svc.checkUpdateStatus(parsed.companyId, parsed.skillId);
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user