Codemod db pass-through calls: messages + runs + approvals + company (C1 part 2/3)
Rewrite ~376 call sites for messages, runs, approvalsMod, and company domains and delete the corresponding pass-through methods on AppDatabase / AppDatabaseBase. Structural type references (ReturnType<HandlerDeps["db"]["getRun"]>) and SweepStaleReviewsDeps-style mocks are updated to route through the owning sub-module.
This commit is contained in:
1 parent
5e9b7acbc6
commit
a69fbb053b
86 files changed
+393
-454
No files matched your search
@@ -45,7 +45,7 @@ export class CostWriter {
|
||||
);
|
||||
}
|
||||
|
||||
this.deps.db.recordCostEntry({
|
||||
this.deps.db.runs.recordCostEntry({
|
||||
runId: input.runId,
|
||||
companyId: input.companyId,
|
||||
agentId: input.agentId,
|
||||
|
||||
@@ -134,7 +134,7 @@ function checkOperationalApprovalBlock(
|
||||
toolName: string,
|
||||
): BeforeToolCallRpcResult {
|
||||
try {
|
||||
const blocking = deps.db.getBlockingApproval(ctx.companyId, ctx.agentId, ctx.taskId);
|
||||
const blocking = deps.db.approvalsMod.getBlockingApproval(ctx.companyId, ctx.agentId, ctx.taskId);
|
||||
if (!blocking) return { block: false, reason: null };
|
||||
if (!OPERATIONAL_APPROVAL_TYPES.has(blocking.type as ApprovalType)) {
|
||||
return { block: false, reason: null };
|
||||
|
||||
@@ -573,7 +573,7 @@ export class RuntimeBridge {
|
||||
// `?unreadOnly=true`, so agents posted status comments instead of
|
||||
// replies. The explicit read endpoint is the sole signal.
|
||||
const store = this.getOrCreateStore(payload.taskSessionId);
|
||||
const inboxRecords = this.deps.db.listAgentInboxUnread(payload.companyId, payload.agentId);
|
||||
const inboxRecords = this.deps.db.messages.listAgentInboxUnread(payload.companyId, payload.agentId);
|
||||
if (inboxRecords.length > 0) {
|
||||
this.deps.db.withTransaction(() => {
|
||||
for (const record of inboxRecords) {
|
||||
|
||||
@@ -31,7 +31,7 @@ export function registerAgentControlRoutes(server: AgentApiServer) {
|
||||
});
|
||||
const parsed = wakeSchema.safeParse(body);
|
||||
const reason = parsed.success ? parsed.data.reason : "peer_request";
|
||||
const alreadyActive = db.hasActiveRun({ companyId: token.companyId, agentId: targetAgentId });
|
||||
const alreadyActive = db.runs.hasActiveRun({ companyId: token.companyId, agentId: targetAgentId });
|
||||
const runId = onWakeAgent(targetAgentId, token.companyId, reason);
|
||||
if (runId) {
|
||||
server.json(res, 200, { ok: true, runId, message: `Wake started for agent ${targetAgentId}` });
|
||||
@@ -65,7 +65,7 @@ export function registerAgentControlRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "Current agent is not allowed to trigger heartbeat for this agent" });
|
||||
return;
|
||||
}
|
||||
const alreadyActive = db.hasActiveRun({ companyId: token.companyId, agentId: targetAgentId });
|
||||
const alreadyActive = db.runs.hasActiveRun({ companyId: token.companyId, agentId: targetAgentId });
|
||||
const runId = onWakeAgent(targetAgentId, token.companyId, "peer_request");
|
||||
if (runId) {
|
||||
server.json(res, 200, { ok: true, runId, message: `Heartbeat triggered for agent ${targetAgentId}` });
|
||||
|
||||
@@ -35,15 +35,15 @@ export function registerAgentStandupRoute(server: AgentApiServer): void {
|
||||
const twoHoursAgo = new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString();
|
||||
const recentComments = db.listRecentTaskComments([...allRelevantTaskIds], twoHoursAgo, 20);
|
||||
|
||||
const myPendingApprovals = db.listAgentPendingApprovals(token.companyId, token.agentId);
|
||||
const myResolvedApprovals = db.listAgentResolvedApprovals(token.companyId, token.agentId, 5);
|
||||
const myPendingApprovals = db.approvalsMod.listAgentPendingApprovals(token.companyId, token.agentId);
|
||||
const myResolvedApprovals = db.approvalsMod.listAgentResolvedApprovals(token.companyId, token.agentId, 5);
|
||||
|
||||
// Unread inbox lives in the standup briefing as the agent's
|
||||
// canonical view of pending communication. Pre-fix the agent had
|
||||
// to make a second API call to /messages?unreadOnly=true that the
|
||||
// model frequently skipped, so messages from peers and managers
|
||||
// sat dark while the agent posted heartbeat status comments.
|
||||
const unreadInbox = db.listAgentInboxUnread(token.companyId, token.agentId);
|
||||
const unreadInbox = db.messages.listAgentInboxUnread(token.companyId, token.agentId);
|
||||
const senderIds = new Set(unreadInbox.map((m) => m.fromAgentId));
|
||||
const senderNames = senderIds.size > 0
|
||||
? new Map(db.agents.listAgentNamesByIds(token.companyId, Array.from(senderIds)).map((row) => [row.id, row.name]))
|
||||
|
||||
@@ -29,7 +29,7 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
||||
// and the standup module is registered separately in api-server/index.ts.
|
||||
|
||||
server.addRoute("GET", "/api/agents/me/metrics", (_req, res, _params, token) => {
|
||||
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
|
||||
const companyMetrics = db.company.getCompanyMetrics(token.companyId, 30);
|
||||
const agentMetric = companyMetrics.agentMetrics.find(m => m.agentId === token.agentId);
|
||||
if (!agentMetric) {
|
||||
server.json(res, 404, { error: "Metrics not found" });
|
||||
@@ -45,7 +45,7 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
||||
server.json(res, 404, { error: "Agent not found" });
|
||||
return;
|
||||
}
|
||||
const companyMetrics = db.getCompanyMetrics(token.companyId, 30);
|
||||
const companyMetrics = db.company.getCompanyMetrics(token.companyId, 30);
|
||||
const agentMetric = companyMetrics.agentMetrics.find((metric) => metric.agentId === agentId);
|
||||
if (!agentMetric) {
|
||||
server.json(res, 404, { error: "Metrics not found" });
|
||||
@@ -190,7 +190,7 @@ export function registerAgentRoutes(server: AgentApiServer) {
|
||||
return;
|
||||
}
|
||||
// Also check pending hire approvals with the same name to prevent concurrent duplicate requests
|
||||
const pendingHires = db.listPendingHireApprovalPayloads(companyId);
|
||||
const pendingHires = db.approvalsMod.listPendingHireApprovalPayloads(companyId);
|
||||
for (const pending of pendingHires) {
|
||||
try {
|
||||
const payload = pending.payloadJson ? JSON.parse(pending.payloadJson) as Record<string, unknown> : {};
|
||||
|
||||
@@ -48,7 +48,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
// Reject the request at the API boundary so the model cannot
|
||||
// circumvent the ordering by resubmitting.
|
||||
if (input.type === "approve_ceo_strategy") {
|
||||
const companyHireApprovals = db.listCompanyApprovalsByType(companyId, "hire_agent");
|
||||
const companyHireApprovals = db.approvalsMod.listCompanyApprovalsByType(companyId, "hire_agent");
|
||||
if (companyHireApprovals.length === 0) {
|
||||
server.json(res, 409, {
|
||||
error: "approve_ceo_strategy requires at least one hire_agent approval to exist first. Submit the intended hires, then file the strategy.",
|
||||
@@ -56,7 +56,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
const existingStrategy = db.listCompanyApprovalsByType(companyId, "approve_ceo_strategy")
|
||||
const existingStrategy = db.approvalsMod.listCompanyApprovalsByType(companyId, "approve_ceo_strategy")
|
||||
.find((a) => a.state === "pending" || a.state === "approved");
|
||||
if (existingStrategy) {
|
||||
server.json(res, 409, {
|
||||
@@ -70,7 +70,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
|
||||
let id: string;
|
||||
try {
|
||||
id = db.requestApproval({
|
||||
id = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
type: input.type,
|
||||
relatedTaskId: input.relatedTaskId ?? null,
|
||||
@@ -139,7 +139,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
return;
|
||||
}
|
||||
const statuses = parsedQuery.data.status?.split(",").filter(Boolean) ?? null;
|
||||
const approvals = db.listApprovalsForApi(companyId, {
|
||||
const approvals = db.approvalsMod.listApprovalsForApi(companyId, {
|
||||
requestedByAgentId: parsedQuery.data.requestedByAgentId,
|
||||
relatedTaskId: parsedQuery.data.relatedTaskId,
|
||||
statuses,
|
||||
@@ -148,7 +148,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
});
|
||||
|
||||
server.addRoute("GET", "/api/approvals/:approvalId", (_req, res, params, token) => {
|
||||
const approval = db.findApprovalById(requireParam(params, "approvalId"));
|
||||
const approval = db.approvalsMod.findApprovalById(requireParam(params, "approvalId"));
|
||||
if (!approval || approval.companyId !== token.companyId) {
|
||||
server.json(res, 404, { error: "Approval not found" });
|
||||
return;
|
||||
@@ -162,21 +162,21 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
|
||||
server.addRoute("GET", "/api/approvals/:approvalId/comments", (_req, res, params, token) => {
|
||||
const approvalId = requireParam(params, "approvalId");
|
||||
const approval = db.findApprovalById(approvalId);
|
||||
const approval = db.approvalsMod.findApprovalById(approvalId);
|
||||
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
|
||||
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
|
||||
const comments = db.listApprovalComments(approvalId, approval.companyId);
|
||||
const comments = db.approvalsMod.listApprovalComments(approvalId, approval.companyId);
|
||||
server.json(res, 200, comments);
|
||||
});
|
||||
|
||||
server.addRoute("POST", "/api/approvals/:approvalId/comments", (_req, res, params, token, body) => {
|
||||
const approval = db.findApprovalById(requireParam(params, "approvalId"));
|
||||
const approval = db.approvalsMod.findApprovalById(requireParam(params, "approvalId"));
|
||||
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
|
||||
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
|
||||
const data = body as Record<string, unknown>;
|
||||
const commentBody = typeof data.body === "string" ? data.body.trim() : "";
|
||||
if (!commentBody) { server.json(res, 400, { error: "Comment body is required" }); return; }
|
||||
const commentId = db.addApprovalComment({
|
||||
const commentId = db.approvalsMod.addApprovalComment({
|
||||
companyId: approval.companyId,
|
||||
approvalId: approval.id,
|
||||
authorAgentId: token.agentId ?? null,
|
||||
@@ -194,7 +194,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
});
|
||||
|
||||
server.addRoute("POST", "/api/approvals/:approvalId/resubmit", (_req, res, params, token) => {
|
||||
const approval = db.findApprovalById(requireParam(params, "approvalId"));
|
||||
const approval = db.approvalsMod.findApprovalById(requireParam(params, "approvalId"));
|
||||
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
|
||||
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
|
||||
if (approval.state !== "revision_requested") {
|
||||
@@ -205,23 +205,23 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "Only the requesting agent can resubmit this approval" });
|
||||
return;
|
||||
}
|
||||
db.resubmitApproval(approval.id, approval.companyId);
|
||||
db.approvalsMod.resubmitApproval(approval.id, approval.companyId);
|
||||
onDomainChanged();
|
||||
server.json(res, 200, { resubmitted: true });
|
||||
});
|
||||
|
||||
server.addRoute("GET", "/api/approvals/:approvalId/tasks", (_req, res, params, token) => {
|
||||
const approvalId = requireParam(params, "approvalId");
|
||||
const approval = db.findApprovalById(approvalId);
|
||||
const approval = db.approvalsMod.findApprovalById(approvalId);
|
||||
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
|
||||
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
|
||||
const taskIds = db.listTasksForApproval(approvalId);
|
||||
const taskIds = db.approvalsMod.listTasksForApproval(approvalId);
|
||||
server.json(res, 200, taskIds);
|
||||
});
|
||||
|
||||
server.addRoute("POST", "/api/approvals/:approvalId/tasks", (_req, res, params, token, body) => {
|
||||
const approvalId = requireParam(params, "approvalId");
|
||||
const approval = db.findApprovalById(approvalId);
|
||||
const approval = db.approvalsMod.findApprovalById(approvalId);
|
||||
if (!approval) { server.json(res, 404, { error: "Approval not found" }); return; }
|
||||
if (approval.companyId !== token.companyId) { server.json(res, 403, { error: "Company mismatch" }); return; }
|
||||
const data = body as Record<string, unknown>;
|
||||
@@ -232,7 +232,7 @@ export function registerApprovalRoutes(server: AgentApiServer) {
|
||||
server.json(res, 400, { error: `Task ${taskId} does not belong to this company` }); return;
|
||||
}
|
||||
}
|
||||
db.linkManyTasksToApproval(approvalId, taskIds, token.companyId, token.agentId ?? null);
|
||||
db.approvalsMod.linkManyTasksToApproval(approvalId, taskIds, token.companyId, token.agentId ?? null);
|
||||
onDomainChanged();
|
||||
server.json(res, 200, { linked: taskIds.length });
|
||||
});
|
||||
|
||||
@@ -142,7 +142,7 @@ export function registerMessageRoutes(server: AgentApiServer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const messages = db.listAgentMessages(companyId, {
|
||||
const messages = db.messages.listAgentMessages(companyId, {
|
||||
agentId: token.agentId,
|
||||
channel: channelParam ?? undefined,
|
||||
channelTargetId: channelTargetId && channelTargetId.length > 0 ? channelTargetId : undefined,
|
||||
@@ -158,12 +158,12 @@ export function registerMessageRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "Company mismatch" });
|
||||
return;
|
||||
}
|
||||
const message = db.getAgentMessage(requireParam(params, "messageId"), companyId);
|
||||
const message = db.messages.getAgentMessage(requireParam(params, "messageId"), companyId);
|
||||
if (!message) {
|
||||
server.json(res, 404, { error: "Message not found" });
|
||||
return;
|
||||
}
|
||||
const visibleToAgent = db.listAgentMessages(companyId, {
|
||||
const visibleToAgent = db.messages.listAgentMessages(companyId, {
|
||||
agentId: token.agentId,
|
||||
messageId: message.id,
|
||||
limit: 1,
|
||||
@@ -172,7 +172,7 @@ export function registerMessageRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "This message is not visible to the current agent" });
|
||||
return;
|
||||
}
|
||||
db.markMessageReadForReader(requireParam(params, "messageId"), companyId, token.agentId);
|
||||
db.messages.markMessageReadForReader(requireParam(params, "messageId"), companyId, token.agentId);
|
||||
onDomainChanged();
|
||||
server.json(res, 200, { ok: true });
|
||||
});
|
||||
|
||||
@@ -42,7 +42,7 @@ export function registerMiscRoutes(server: AgentApiServer) {
|
||||
|
||||
const postActions = ["post", "reply", "dm"];
|
||||
if (account.requireApproval && postActions.includes(input.actionType)) {
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
type: "dangerous_command",
|
||||
relatedTaskId: null,
|
||||
@@ -142,7 +142,7 @@ export function registerMiscRoutes(server: AgentApiServer) {
|
||||
|
||||
const contentPreview = parsed.data.content.slice(0, 120) + (parsed.data.content.length > 120 ? "..." : "");
|
||||
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
requestedByAgentId: token.agentId,
|
||||
type: "social_post",
|
||||
|
||||
@@ -16,7 +16,7 @@ function parsePositiveInt(value: string | null, fallback: number) {
|
||||
}
|
||||
|
||||
function resolveAuthorizedRun(server: AgentApiServer, token: TokenPayload, runId: string) {
|
||||
const runState = server.db.getRunAuthState(runId);
|
||||
const runState = server.db.runs.getRunAuthState(runId);
|
||||
if (!runState || runState.companyId !== token.companyId || runState.agentId !== token.agentId) {
|
||||
return null;
|
||||
}
|
||||
@@ -40,7 +40,7 @@ export function registerRunRoutes(server: AgentApiServer) {
|
||||
const url = new URL(req.url ?? "/", `http://127.0.0.1:${server.port}`);
|
||||
const offset = parsePositiveInt(url.searchParams.get("offset"), 0);
|
||||
const limit = Math.min(parsePositiveInt(url.searchParams.get("limit"), DEFAULT_EVENT_LIMIT), MAX_EVENT_LIMIT);
|
||||
const events = server.db.listRunEvents(runId, token.companyId, { offset, limit });
|
||||
const events = server.db.runs.listRunEvents(runId, token.companyId, { offset, limit });
|
||||
server.json(res, 200, { events, nextOffset: offset + events.length });
|
||||
});
|
||||
|
||||
@@ -66,7 +66,7 @@ export function registerRunRoutes(server: AgentApiServer) {
|
||||
|
||||
const flushPendingEvents = () => {
|
||||
while (true) {
|
||||
const events = server.db.listRunEventsAfter(runId, token.companyId, cursor, SSE_BATCH_LIMIT);
|
||||
const events = server.db.runs.listRunEventsAfter(runId, token.companyId, cursor, SSE_BATCH_LIMIT);
|
||||
if (events.length === 0) return;
|
||||
for (const event of events) {
|
||||
writeSse(res, "run-event", event);
|
||||
|
||||
@@ -136,7 +136,7 @@ export function registerTaskRoutes(server: AgentApiServer) {
|
||||
});
|
||||
const reviewerAgent = db.entityCrud.getAgent(token.agentId);
|
||||
const docTitles = docs.map((d) => d.title).join(", ");
|
||||
db.requestApproval({
|
||||
db.approvalsMod.requestApproval({
|
||||
companyId: task.companyId,
|
||||
relatedTaskId: task.id,
|
||||
requestedByAgentId: token.agentId,
|
||||
|
||||
@@ -256,7 +256,7 @@ export function registerWorkGraphRoutes(server: AgentApiServer) {
|
||||
server.json(res, 403, { error: "Company mismatch" });
|
||||
return;
|
||||
}
|
||||
const { agents, tasks, recentActivity } = db.getDashboardSnapshot(companyId);
|
||||
const { agents, tasks, recentActivity } = db.company.getDashboardSnapshot(companyId);
|
||||
|
||||
const tasksByStatus: Record<string, number> = {};
|
||||
for (const t of tasks) {
|
||||
|
||||
@@ -217,7 +217,7 @@ export class AgentApiServer {
|
||||
if (typeof claimedRunId !== "string" || claimedRunId.trim() !== token.runId) {
|
||||
return { ok: false, status: 401, error: "Missing or mismatched X-Agent-Company-Run-Id header" };
|
||||
}
|
||||
const runState = this._db.getRunAuthState(token.runId);
|
||||
const runState = this._db.runs.getRunAuthState(token.runId);
|
||||
if (!runState || runState.companyId !== token.companyId || runState.agentId !== token.agentId) {
|
||||
return { ok: false, status: 403, error: "Token references an unknown or mismatched run" };
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ export function resolveApprovalDecision({
|
||||
actor?: string;
|
||||
}) {
|
||||
const note = decisionNote ?? "";
|
||||
db.decideApproval({
|
||||
db.approvalsMod.decideApproval({
|
||||
companyId,
|
||||
approvalId,
|
||||
state,
|
||||
|
||||
@@ -208,7 +208,7 @@ export function executeAutomationAction(
|
||||
}
|
||||
case "create_approval": {
|
||||
const approvalType = (config.approvalType as string) ?? "document_review";
|
||||
db.requestApproval({
|
||||
db.approvalsMod.requestApproval({
|
||||
companyId: ctx.companyId,
|
||||
relatedTaskId: ctx.taskId ?? null,
|
||||
requestedByAgentId: ctx.agentId ?? null,
|
||||
@@ -340,7 +340,7 @@ export function executeAutomationAction(
|
||||
(approval) => approval.companyId === ctx.companyId && approval.state === "pending",
|
||||
);
|
||||
for (const approval of pendingApprovals.slice(0, 5)) {
|
||||
db.decideApproval({
|
||||
db.approvalsMod.decideApproval({
|
||||
companyId: ctx.companyId,
|
||||
approvalId: approval.id,
|
||||
state: "approved",
|
||||
|
||||
@@ -181,7 +181,7 @@ function createDeliverableReviewApproval(
|
||||
): string {
|
||||
const { db, logger, publishDomainChanged, notify } = deps;
|
||||
const docTitles = docs.map((d) => d.title).join(", ");
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: agentId,
|
||||
@@ -232,9 +232,9 @@ function decideTerminalStatus(
|
||||
companyId: string,
|
||||
): "in_review" | "done" {
|
||||
if (agent.reportsTo !== null) return "in_review";
|
||||
const mode = db.getCompanyExecutionMode(companyId);
|
||||
const mode = db.company.getCompanyExecutionMode(companyId);
|
||||
if (mode !== "ai_ceo") return "in_review";
|
||||
const settings = db.getCompanyApprovalSettings(companyId);
|
||||
const settings = db.company.getCompanyApprovalSettings(companyId);
|
||||
return settings?.reviewDeliverables ? "in_review" : "done";
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ export async function runWorkflowPipeline(
|
||||
const { config, ctx } = buildWorkflowActionContext(companyId, step, actor);
|
||||
|
||||
if (step.requiresApproval) {
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
requestedByAgentId: actor?.id ?? null,
|
||||
relatedTaskId: ctx.taskId ?? null,
|
||||
|
||||
@@ -21,7 +21,7 @@ export function emitCrashRecoveryNotification(deps: RecoverInterruptedTasksDeps)
|
||||
const interruptedCount = deps.db.lastInterruptedCount;
|
||||
if (interruptedCount <= 0) return;
|
||||
deps.logger.warn(`[bootstrap] Detected ${interruptedCount} interrupted runs from previous session. Sending recovery notification.`);
|
||||
const interruptedRuns = deps.db.getInterruptedRunsForRecovery();
|
||||
const interruptedRuns = deps.db.runs.getInterruptedRunsForRecovery();
|
||||
if (interruptedRuns.length === 0) return;
|
||||
deps.emitEvent({ type: "recovery-needed", interruptedRuns });
|
||||
deps.notify({
|
||||
|
||||
@@ -27,9 +27,9 @@ export function createBrowserActionService({
|
||||
* missing. Isolates the "approval row disappeared" case so the caller does
|
||||
* not need to nest a try/catch around the state machine.
|
||||
*/
|
||||
function getApprovalOrNull(approvalId: string): ReturnType<typeof db.getApprovalRecord> | null {
|
||||
function getApprovalOrNull(approvalId: string): ReturnType<typeof db.approvalsMod.getApprovalRecord> | null {
|
||||
try {
|
||||
return db.getApprovalRecord(approvalId);
|
||||
return db.approvalsMod.getApprovalRecord(approvalId);
|
||||
} catch (err) {
|
||||
const classified = classifyUnknownError(err, {
|
||||
kind: "recoverable",
|
||||
|
||||
@@ -27,7 +27,7 @@ interface CheckpointDeps {
|
||||
}
|
||||
|
||||
export function getCompanyExecutionMode(db: AppDatabase, companyId: string): ExecutionMode {
|
||||
return db.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
return db.company.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
}
|
||||
|
||||
export function createCheckpointEngine(deps: CheckpointDeps) {
|
||||
@@ -60,7 +60,7 @@ export function createCheckpointEngine(deps: CheckpointDeps) {
|
||||
// the moment delegation starts.
|
||||
if (task.parentId) return null;
|
||||
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: agentId,
|
||||
@@ -107,7 +107,7 @@ export function createCheckpointEngine(deps: CheckpointDeps) {
|
||||
? "strategy_review"
|
||||
: "draft_review";
|
||||
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: authorAgentId,
|
||||
@@ -154,7 +154,7 @@ export function createCheckpointEngine(deps: CheckpointDeps) {
|
||||
}
|
||||
|
||||
// Check for pending approvals related to this agent's tasks
|
||||
return db.hasPendingReviewApprovalForAgent(companyId, agentId);
|
||||
return db.approvalsMod.hasPendingReviewApprovalForAgent(companyId, agentId);
|
||||
} catch (err) {
|
||||
logger.error(`[checkpoint] shouldPauseHeartbeat failed: ${err instanceof Error ? err.message : String(err)}`);
|
||||
return false;
|
||||
|
||||
@@ -250,19 +250,6 @@ export class AppDatabaseBase {
|
||||
listSnapshot(options: { companyId?: string | null } = {}): ProfileSnapshot { return this.snap.listSnapshot(options); }
|
||||
listRendererSnapshot(): ProfileSnapshot { return this.snap.listRendererSnapshot(); }
|
||||
|
||||
// ─── Company ───────────────────────────────────────────────────────────────
|
||||
|
||||
saveCompany(input: { id?: string | null; name: string; description: string; status: string; autoApproveHires?: boolean; reviewDeliverables?: boolean; executionMode?: string }) { return this.company.saveCompany(input); }
|
||||
deleteCompany(id: string) { return this.company.deleteCompany(id); }
|
||||
getCompanyStatus(companyId: string) { return this.company.getCompanyStatus(companyId); }
|
||||
getCompanyBasic(companyId: string) { return this.company.getCompanyBasic(companyId); }
|
||||
getCompanyExecutionMode(companyId: string) { return this.company.getCompanyExecutionMode(companyId); }
|
||||
getCompanyApprovalSettings(companyId: string) { return this.company.getCompanyApprovalSettings(companyId); }
|
||||
listActiveCompanyIds() { return this.company.listActiveCompanyIds(); }
|
||||
getCompanyMetrics(companyId: string, days: number = 30) { return this.company.getCompanyMetrics(companyId, days); }
|
||||
getStandupReport(companyId: string) { return this.company.getStandupReport(companyId); }
|
||||
getDashboardSnapshot(companyId: string) { return this.company.getDashboardSnapshot(companyId); }
|
||||
|
||||
// ─── Connectors ────────────────────────────────────────────────────────────
|
||||
|
||||
saveConnector(input: { id: ConnectorId; model: string | null; envBindingText: string; notes: string }) { this.connectorsCrud.saveConnector(input); }
|
||||
@@ -270,49 +257,6 @@ export class AppDatabaseBase {
|
||||
getConnector(id: ConnectorId) { return this.connectorsCrud.getConnector(id); }
|
||||
findConnector(id: ConnectorId) { return this.connectorsCrud.findConnector(id); }
|
||||
|
||||
// ─── Runs ──────────────────────────────────────────────────────────────────
|
||||
|
||||
getRun(id: string) { return this.runs.getRun(id); }
|
||||
getRunAuthState(runId: string) { return this.runs.getRunAuthState(runId); }
|
||||
createRun(input: { id?: string; companyId: string; taskId: string | null; agentId: string; workspaceId: string | null; connectorId: ConnectorId }) { return this.runs.createRun(input); }
|
||||
appendRunLog(runId: string, stream: "stdout" | "stderr", chunk: string) { return this.runs.appendRunLog(runId, stream, chunk); }
|
||||
readRunLog(runId: string) { return this.runs.readRunLog(runId); }
|
||||
readRunLogChunk(runId: string, offset = 0, limit = 65_536) { return this.runs.readRunLogChunk(runId, offset, limit); }
|
||||
markRunStatus(runId: string, status: RunStatus, message: string) { return this.runs.markRunStatus(runId, status, message); }
|
||||
finishRun(input: { runId: string; status: RunStatus; summary: string | null; errorMessage: string | null; exitCode: number | null; signal: string | null; model: string | null; unattributedCost: boolean }) { return this.runs.finishRun(input); }
|
||||
recordCostEntry(input: { runId: string; companyId: string; agentId: string; taskId: string | null; connectorId: string; amountUsd: number | null; unattributed: boolean }) { return this.runs.recordCostEntry(input); }
|
||||
hasActiveRun(filter: { companyId: string; taskId?: string; agentId?: string }) { return this.runs.hasActiveRun(filter); }
|
||||
listQueuedRuns() { return this.runs.listQueuedRuns(); }
|
||||
getInterruptedRunsForRecovery() { return this.runs.getInterruptedRunsForRecovery(); }
|
||||
appendRunEvent(input: { companyId: string; runId: string; agentId: string; eventType: string; stream?: string | null; level?: string | null; message?: string | null; payload?: Record<string, unknown> | null }) { return this.runs.appendRunEvent(input); }
|
||||
listRunEvents(runId: string, companyId: string, opts?: { offset?: number; limit?: number }) { return this.runs.listRunEvents(runId, companyId, opts); }
|
||||
listRunEventsAfter(runId: string, companyId: string, afterSeq: number, limit: number) { return this.runs.listRunEventsAfter(runId, companyId, afterSeq, limit); }
|
||||
updateRunActor(runId: string, actorType: string, actorId: string | null) { return this.runs.updateRunActor(runId, actorType, actorId); }
|
||||
|
||||
// ─── Approvals ─────────────────────────────────────────────────────────────
|
||||
|
||||
requestApproval(input: { companyId: string; relatedTaskId?: string | null; requestedByAgentId?: string | null; relatedAgentId?: string | null; type: string; payloadSummary: string; impactSummary: string; payloadJson?: string | null }) { return this.approvalsMod.requestApproval(input); }
|
||||
decideApproval(input: { companyId?: string | null; approvalId: string; state: string; decisionNote: string }) { return this.approvalsMod.decideApproval(input); }
|
||||
getApprovalRecord(id: string) { return this.approvalsMod.getApprovalRecord(id); }
|
||||
findApprovalById(id: string) { return this.approvalsMod.findApprovalById(id); }
|
||||
deleteApproval(id: string, companyId: string) { this.approvalsMod.deleteApproval(id, companyId); }
|
||||
getBlockingApproval(companyId: string, agentId: string, taskId: string | null) { return this.approvalsMod.getBlockingApproval(companyId, agentId, taskId); }
|
||||
resubmitApproval(approvalId: string, companyId: string) { this.approvalsMod.resubmitApproval(approvalId, companyId); }
|
||||
addApprovalComment(input: { companyId: string; approvalId: string; authorAgentId?: string | null; authorUserId?: string | null; body: string }) { return this.approvalsMod.addApprovalComment(input); }
|
||||
listApprovalComments(approvalId: string, companyId: string) { return this.approvalsMod.listApprovalComments(approvalId, companyId); }
|
||||
linkTaskToApproval(taskId: string, approvalId: string, companyId: string, linkedByAgentId?: string | null) { this.approvalsMod.linkTaskToApproval(taskId, approvalId, companyId, linkedByAgentId); }
|
||||
unlinkTaskFromApproval(taskId: string, approvalId: string) { this.approvalsMod.unlinkTaskFromApproval(taskId, approvalId); }
|
||||
listApprovalsForTask(taskId: string) { return this.approvalsMod.listApprovalsForTask(taskId); }
|
||||
listTasksForApproval(approvalId: string) { return this.approvalsMod.listTasksForApproval(approvalId); }
|
||||
linkManyTasksToApproval(approvalId: string, taskIds: string[], companyId: string, linkedByAgentId?: string | null) { this.approvalsMod.linkManyTasksToApproval(approvalId, taskIds, companyId, linkedByAgentId); }
|
||||
listAgentPendingApprovals(companyId: string, agentId: string) { return this.approvalsMod.listAgentPendingApprovals(companyId, agentId); }
|
||||
listAgentResolvedApprovals(companyId: string, agentId: string, limit?: number) { return this.approvalsMod.listAgentResolvedApprovals(companyId, agentId, limit); }
|
||||
listPendingHireApprovalPayloads(companyId: string) { return this.approvalsMod.listPendingHireApprovalPayloads(companyId); }
|
||||
listApprovalsForApi(companyId: string, filters: { requestedByAgentId?: string; relatedTaskId?: string; statuses?: string[] | null }) { return this.approvalsMod.listApprovalsForApi(companyId, filters); }
|
||||
findPendingApprovalForTask(companyId: string, taskId: string) { return this.approvalsMod.findPendingApprovalForTask(companyId, taskId); }
|
||||
hasPendingReviewApprovalForAgent(companyId: string, agentId: string) { return this.approvalsMod.hasPendingReviewApprovalForAgent(companyId, agentId); }
|
||||
listCompanyApprovalsByType(companyId: string, type: string) { return this.approvalsMod.listCompanyApprovalsByType(companyId, type); }
|
||||
|
||||
// ─── Workspaces ────────────────────────────────────────────────────────────
|
||||
|
||||
saveWorkspace(input: { id?: string | null; companyId: string; name: string; localPath: string; repoUrl: string; repoRef: string; projectId?: string | null; isPrimary: boolean }) { return this.workspacesMod.saveWorkspace(input); }
|
||||
|
||||
@@ -12,15 +12,6 @@ export class AppDatabase extends AppDatabaseBase {
|
||||
|
||||
listComments(taskId: string) { return this.activityMod.listComments(taskId); }
|
||||
|
||||
// ─── Messages ──────────────────────────────────────────────────────────────
|
||||
|
||||
sendAgentMessage(input: { companyId: string; fromAgentId: string; toAgentId?: string | null; channel?: string; channelTargetId?: string | null; subject: string; body: string; priority?: string; parentMessageId?: string | null; attachmentsJson?: string; senderKind?: "agent" | "human" | "system"; mentionedAgentIds?: readonly string[] }) { return this.messages.sendAgentMessage(input); }
|
||||
getAgentMessage(messageId: string, companyId: string) { return this.messages.getAgentMessage(messageId, companyId); }
|
||||
listAgentMessages(companyId: string, opts?: { agentId?: string; messageId?: string; channel?: string; channelTargetId?: string | null; unreadOnly?: boolean; limit?: number }) { return this.messages.listAgentMessages(companyId, opts); }
|
||||
searchMessages(companyId: string, query: string, opts?: { channel?: string; channelTargetId?: string | null; limit?: number }) { return this.messages.searchMessages(companyId, query, opts); }
|
||||
markMessageReadForReader(messageId: string, companyId: string, readerAgentId?: string | null) { return this.messages.markMessageReadForReader(messageId, companyId, readerAgentId); }
|
||||
listAgentInboxUnread(companyId: string, agentId: string) { return this.messages.listAgentInboxUnread(companyId, agentId); }
|
||||
|
||||
// ─── Social & Browser Actions ──────────────────────────────────────────────
|
||||
|
||||
saveSocialAccount(input: { id?: string | null; companyId: string; platform: string; accountName: string; displayName?: string; profileUrl?: string; credentialSecretId?: string | null; status?: string; requireApproval?: boolean; metadataJson?: string }) { return this.social.saveSocialAccount(input); }
|
||||
|
||||
@@ -164,7 +164,7 @@ function deriveTaskIdFromRun(
|
||||
): string | null {
|
||||
if (!runId) return null;
|
||||
try {
|
||||
const run = db.getRun(runId);
|
||||
const run = db.runs.getRun(runId);
|
||||
if (run.companyId !== companyId) return null;
|
||||
return run.taskId ?? null;
|
||||
} catch (error) {
|
||||
@@ -187,12 +187,12 @@ function maybeRaiseDeliverableReviewApproval(
|
||||
documentId: string,
|
||||
taskId: string | null,
|
||||
): string | null {
|
||||
const companySettings = deps.db.getCompanyApprovalSettings(input.companyId);
|
||||
const companySettings = deps.db.company.getCompanyApprovalSettings(input.companyId);
|
||||
if (!companySettings?.reviewDeliverables) return null;
|
||||
if (deps.db.getCompanyExecutionMode(input.companyId) === "ai_cofounder") return null;
|
||||
if (deps.db.company.getCompanyExecutionMode(input.companyId) === "ai_cofounder") return null;
|
||||
if (deps.db.tasks.hasPendingDeliverableReviews(input.companyId)) return null;
|
||||
|
||||
const approvalId = deps.db.requestApproval({
|
||||
const approvalId = deps.db.approvalsMod.requestApproval({
|
||||
companyId: input.companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: input.authorAgentId ?? "",
|
||||
|
||||
@@ -54,7 +54,7 @@ export function shouldAutoApprove(
|
||||
companyId: string,
|
||||
approvalType: string,
|
||||
): boolean {
|
||||
const rawMode = db.getCompanyExecutionMode(companyId);
|
||||
const rawMode = db.company.getCompanyExecutionMode(companyId);
|
||||
assertExecutionMode(rawMode);
|
||||
const mode = rawMode;
|
||||
|
||||
@@ -68,7 +68,7 @@ export function shouldAutoApprove(
|
||||
return false;
|
||||
}
|
||||
|
||||
const settings = db.getCompanyApprovalSettings(companyId);
|
||||
const settings = db.company.getCompanyApprovalSettings(companyId);
|
||||
if (!settings?.autoApproveHires) return false;
|
||||
|
||||
// When reviewDeliverables is ON, CEO strategy needs human review
|
||||
|
||||
@@ -56,7 +56,7 @@ export function registerAgentCoordinationHandlers({
|
||||
const parsed = markMessageReadSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
const message = db.getAgentMessage(parsed.messageId, parsed.companyId);
|
||||
const message = db.messages.getAgentMessage(parsed.messageId, parsed.companyId);
|
||||
if (!message) {
|
||||
return fail("MESSAGE_NOT_FOUND", "Message not found in the current company.");
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export function registerAgentCoordinationHandlers({
|
||||
return fail("INVALID_COMPANY_REFERENCE", "The selected message reader does not belong to the current company.");
|
||||
}
|
||||
if (parsed.readerAgentId) {
|
||||
const visibleToReader = db
|
||||
const visibleToReader = db.messages
|
||||
.listAgentMessages(parsed.companyId, {
|
||||
agentId: parsed.readerAgentId,
|
||||
messageId: parsed.messageId,
|
||||
@@ -75,7 +75,7 @@ export function registerAgentCoordinationHandlers({
|
||||
return fail("MESSAGE_NOT_VISIBLE", "This message is not visible to the selected reader.");
|
||||
}
|
||||
}
|
||||
db.markMessageReadForReader(parsed.messageId, parsed.companyId, parsed.readerAgentId ?? null);
|
||||
db.messages.markMessageReadForReader(parsed.messageId, parsed.companyId, parsed.readerAgentId ?? null);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
});
|
||||
@@ -84,7 +84,7 @@ export function registerAgentCoordinationHandlers({
|
||||
const parsed = searchMessagesSchema.parse(payload);
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
return ok(db.searchMessages(parsed.companyId, parsed.query, {
|
||||
return ok(db.messages.searchMessages(parsed.companyId, parsed.query, {
|
||||
channel: parsed.channel,
|
||||
channelTargetId: parsed.channelTargetId,
|
||||
limit: parsed.limit,
|
||||
|
||||
@@ -46,11 +46,11 @@ export function registerCoreAdminHandlers({
|
||||
>) {
|
||||
registerHandle(IPC_CHANNELS.saveCompany, async (_event, payload) => {
|
||||
const parsed = companyInputSchema.parse(payload);
|
||||
const previousMode = parsed.id ? db.getCompanyExecutionMode(parsed.id) : parsed.executionMode;
|
||||
const previousMode = parsed.id ? db.company.getCompanyExecutionMode(parsed.id) : parsed.executionMode;
|
||||
const previousApprovalSettings = parsed.id
|
||||
? db.getCompanyApprovalSettings(parsed.id)
|
||||
? db.company.getCompanyApprovalSettings(parsed.id)
|
||||
: { autoApproveHires: parsed.autoApproveHires, reviewDeliverables: parsed.reviewDeliverables };
|
||||
const id = db.saveCompany(parsed);
|
||||
const id = db.company.saveCompany(parsed);
|
||||
reconcileExecutionModeChange({
|
||||
db,
|
||||
companyId: id,
|
||||
@@ -159,7 +159,7 @@ export function registerCoreAdminHandlers({
|
||||
if (!db.hasCompany(companyId)) {
|
||||
return fail("COMPANY_NOT_FOUND", "Company not found.");
|
||||
}
|
||||
db.deleteCompany(companyId);
|
||||
db.company.deleteCompany(companyId);
|
||||
publishDomainChanged();
|
||||
return ok(true);
|
||||
});
|
||||
|
||||
@@ -298,14 +298,14 @@ export function registerCoreAgentGraphHandlers({
|
||||
if (!db.belongsToCompany("approvals", parsed.id, parsed.companyId)) {
|
||||
return fail("APPROVAL_NOT_FOUND", "Approval not found in the current company.");
|
||||
}
|
||||
const approval = db.getApprovalRecord(parsed.id);
|
||||
const approval = db.approvalsMod.getApprovalRecord(parsed.id);
|
||||
if (approval.state === "pending" && approval.type === "hire_agent") {
|
||||
return fail(
|
||||
"APPROVAL_DELETE_BLOCKED",
|
||||
"Pending hire approvals cannot be deleted. Reject the hire request instead so the linked agent record stays consistent.",
|
||||
);
|
||||
}
|
||||
db.deleteApproval(parsed.id, parsed.companyId);
|
||||
db.approvalsMod.deleteApproval(parsed.id, parsed.companyId);
|
||||
db.addActivity({
|
||||
companyId: parsed.companyId,
|
||||
actor: "board",
|
||||
|
||||
@@ -56,7 +56,7 @@ export function registerCoreOperationHandlers({
|
||||
}
|
||||
let id: string;
|
||||
try {
|
||||
id = db.requestApproval(parsed);
|
||||
id = db.approvalsMod.requestApproval(parsed);
|
||||
} catch (error) {
|
||||
return fail("APPROVAL_INVALID", error instanceof Error ? error.message : "Approval could not be created.");
|
||||
}
|
||||
@@ -109,7 +109,7 @@ export function registerCoreOperationHandlers({
|
||||
if (!db.belongsToCompany("approvals", parsed.approvalId, parsed.companyId)) {
|
||||
return fail("APPROVAL_NOT_FOUND", "Approval not found in the current company.");
|
||||
}
|
||||
const approval = db.getApprovalRecord(parsed.approvalId);
|
||||
const approval = db.approvalsMod.getApprovalRecord(parsed.approvalId);
|
||||
if (!approval || approval.companyId !== parsed.companyId) {
|
||||
return fail("APPROVAL_NOT_FOUND", "Approval not found in the current company.");
|
||||
}
|
||||
@@ -154,7 +154,7 @@ export function registerCoreOperationHandlers({
|
||||
if (!db.belongsToCompany("runs", parsed.runId, parsed.companyId)) {
|
||||
return fail("RUN_NOT_FOUND", "Run not found in the current company.");
|
||||
}
|
||||
return ok(db.readRunLog(parsed.runId));
|
||||
return ok(db.runs.readRunLog(parsed.runId));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getRunLogChunk, (_event, payload) => {
|
||||
@@ -164,7 +164,7 @@ export function registerCoreOperationHandlers({
|
||||
if (!db.belongsToCompany("runs", parsed.runId, parsed.companyId)) {
|
||||
return fail("RUN_NOT_FOUND", "Run not found in the current company.");
|
||||
}
|
||||
return ok(db.readRunLogChunk(parsed.runId, parsed.offset, parsed.limit));
|
||||
return ok(db.runs.readRunLogChunk(parsed.runId, parsed.offset, parsed.limit));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.addComment, async (_event, payload) => {
|
||||
|
||||
@@ -149,14 +149,14 @@ export function registerCoreReadHandlers({
|
||||
const input = metricsQuerySchema.parse(payload);
|
||||
const err = ensureCompanyExists(input.companyId);
|
||||
if (err) return err;
|
||||
return ok(db.getCompanyMetrics(input.companyId, input.days));
|
||||
return ok(db.company.getCompanyMetrics(input.companyId, input.days));
|
||||
});
|
||||
|
||||
registerHandle(IPC_CHANNELS.getStandupReport, (_event, payload) => {
|
||||
const companyId = z.string().uuid().parse(payload);
|
||||
const err = ensureCompanyExists(companyId);
|
||||
if (err) return err;
|
||||
return ok(db.getStandupReport(companyId));
|
||||
return ok(db.company.getStandupReport(companyId));
|
||||
});
|
||||
|
||||
// --- Entity getters for granular IPC updates ---
|
||||
@@ -194,7 +194,7 @@ export function registerCoreReadHandlers({
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
try {
|
||||
const run = db.getRun(parsed.id);
|
||||
const run = db.runs.getRun(parsed.id);
|
||||
if (run.companyId !== parsed.companyId) return fail("RUN_NOT_FOUND", "Run not found in current company.");
|
||||
return ok(run);
|
||||
} catch (err) {
|
||||
@@ -208,7 +208,7 @@ export function registerCoreReadHandlers({
|
||||
const companyError = ensureCompanyExists(parsed.companyId);
|
||||
if (companyError) return companyError;
|
||||
try {
|
||||
const approval = db.getApprovalRecord(parsed.id);
|
||||
const approval = db.approvalsMod.getApprovalRecord(parsed.id);
|
||||
if (approval.companyId !== parsed.companyId) return fail("APPROVAL_NOT_FOUND", "Approval not found in current company.");
|
||||
return ok(approval);
|
||||
} catch (err) {
|
||||
|
||||
@@ -184,7 +184,7 @@ export function validateAgentMessageInput(
|
||||
}
|
||||
|
||||
if (input.parentMessageId) {
|
||||
const parentMessage = db.getAgentMessage(input.parentMessageId, input.companyId);
|
||||
const parentMessage = db.messages.getAgentMessage(input.parentMessageId, input.companyId);
|
||||
if (!parentMessage) {
|
||||
return {
|
||||
code: "PARENT_MESSAGE_NOT_FOUND",
|
||||
@@ -317,8 +317,8 @@ export function dispatchAgentMessage(
|
||||
const persistedMessage = normalized.channel === "direct"
|
||||
? { ...normalized, senderKind, mentionedAgentIds }
|
||||
: { ...normalized, toAgentId: null, senderKind, mentionedAgentIds };
|
||||
const id = deps.db.sendAgentMessage(persistedMessage);
|
||||
const message = deps.db.getAgentMessage(id, normalized.companyId);
|
||||
const id = deps.db.messages.sendAgentMessage(persistedMessage);
|
||||
const message = deps.db.messages.getAgentMessage(id, normalized.companyId);
|
||||
if (message && deps.emitEvent) {
|
||||
deps.emitEvent({ type: "new-message", message });
|
||||
}
|
||||
|
||||
@@ -220,7 +220,7 @@ export function createOnboardingService({
|
||||
const connectors = db.listSnapshot({ companyId: null }).connectors;
|
||||
const selectedConnectorId = resolveOnboardingConnector(connectors, input.connectorId);
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: input.companyName.trim(),
|
||||
description: input.companyDescription.trim(),
|
||||
status: "active",
|
||||
|
||||
@@ -66,7 +66,7 @@ export function createHeartbeat(
|
||||
const isMessageWake = trigger === "message";
|
||||
const isCommunicationWake = isMessageWake || trigger === "peer_request";
|
||||
if (!hasWorkerReady()) return SKIPPED("worker_not_ready");
|
||||
const companyStatus = db.getCompanyStatus(companyId);
|
||||
const companyStatus = db.company.getCompanyStatus(companyId);
|
||||
if (companyStatus !== "active") return SKIPPED("company_inactive");
|
||||
const agent = db.entityCrud.getAgent(agentId);
|
||||
if (agent.companyId !== companyId) {
|
||||
@@ -131,8 +131,8 @@ export function createHeartbeat(
|
||||
}
|
||||
if (db.agents.isAgentPendingApproval(agentId)) return SKIPPED("pending_approval");
|
||||
|
||||
const companyMode = db.getCompanyExecutionMode(companyId);
|
||||
const approvalSettings = db.getCompanyApprovalSettings(companyId);
|
||||
const companyMode = db.company.getCompanyExecutionMode(companyId);
|
||||
const approvalSettings = db.company.getCompanyApprovalSettings(companyId);
|
||||
const shouldEnforceHumanReview =
|
||||
companyMode === "ai_cofounder" || approvalSettings?.reviewDeliverables === true;
|
||||
|
||||
@@ -207,7 +207,7 @@ export function createHeartbeat(
|
||||
`[heartbeat] Workspace resolution for ${agent.name}: task.workspaceId=${task?.workspaceId ?? "null"}, agent.workspaceId=${agent.workspaceId ?? "null"}, projectId=${task?.projectId ?? "null"}, source=${workspaceResolution.source}, mode=${workspaceResolution.lockMode}, resolved=${workspace?.name ?? "NONE"}, localPath=${workspace?.localPath ?? "NONE"}`,
|
||||
);
|
||||
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
id: runId,
|
||||
companyId,
|
||||
taskId: task?.id ?? null,
|
||||
@@ -216,7 +216,7 @@ export function createHeartbeat(
|
||||
connectorId: connector.id,
|
||||
});
|
||||
|
||||
const companyBasic = db.getCompanyBasic(companyId);
|
||||
const companyBasic = db.company.getCompanyBasic(companyId);
|
||||
dispatchRunToWorker({
|
||||
runId: run.id,
|
||||
companyId,
|
||||
@@ -238,7 +238,7 @@ export function createHeartbeat(
|
||||
try {
|
||||
const actorType = trigger === "timer" || trigger === "bootstrap" ? "system" : "agent";
|
||||
const actorId = trigger === "timer" || trigger === "bootstrap" ? null : trigger;
|
||||
db.updateRunActor(run.id, actorType, actorId);
|
||||
db.runs.updateRunActor(run.id, actorType, actorId);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`[heartbeat] updateRunActor failed for run ${run.id}: ${error instanceof Error ? error.message : String(error)}`,
|
||||
|
||||
@@ -8,7 +8,7 @@ export function createOrchestrationService(deps: OrchestrationDependencies) {
|
||||
const { db } = deps;
|
||||
|
||||
function hasActiveRun(filter: { taskId?: string; agentId?: string; companyId: string }) {
|
||||
return db.hasActiveRun(filter);
|
||||
return db.runs.hasActiveRun(filter);
|
||||
}
|
||||
|
||||
const runDispatch = createRunDispatch(deps);
|
||||
|
||||
@@ -44,7 +44,7 @@ export function resolveInitialUserTurn(
|
||||
}
|
||||
|
||||
if (trigger === "message" || trigger === "peer_request") {
|
||||
const unread = deps.db.listAgentInboxUnread(companyId, agentId);
|
||||
const unread = deps.db.messages.listAgentInboxUnread(companyId, agentId);
|
||||
if (unread.length > 0) {
|
||||
return buildCommunicationTurn(trigger, unread, deps.db);
|
||||
}
|
||||
|
||||
@@ -83,7 +83,7 @@ export function createRunDispatch(deps: OrchestrationDependencies) {
|
||||
params.workspace &&
|
||||
!db.claimWorkspaceLock(params.workspace.id, params.runId)
|
||||
) {
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: params.runId,
|
||||
status: "cancelled",
|
||||
summary: null,
|
||||
|
||||
@@ -67,7 +67,7 @@ export function createHeartbeatScheduler(
|
||||
try {
|
||||
if (isQuitting() || !hasWorkerReady()) return;
|
||||
|
||||
const activeCompanies = db.listActiveCompanyIds();
|
||||
const activeCompanies = db.company.listActiveCompanyIds();
|
||||
const now = Date.now();
|
||||
|
||||
for (const company of activeCompanies) {
|
||||
|
||||
@@ -27,7 +27,7 @@ export function createHousekeepingScheduler(
|
||||
try {
|
||||
if (isQuitting() || !hasWorkerReady()) return;
|
||||
|
||||
const activeCompanies = db.listActiveCompanyIds();
|
||||
const activeCompanies = db.company.listActiveCompanyIds();
|
||||
|
||||
for (const company of activeCompanies) {
|
||||
try {
|
||||
@@ -52,7 +52,7 @@ export function createHousekeepingScheduler(
|
||||
dispatchAgentMessage,
|
||||
resolveApprovalDecision,
|
||||
notify,
|
||||
getCompanyExecutionMode: (cId: string) => db.getCompanyExecutionMode(cId),
|
||||
getCompanyExecutionMode: (cId: string) => db.company.getCompanyExecutionMode(cId),
|
||||
handleTaskStatusChange: (taskId: string, cId: string, previousStatus: string, newStatus: string) => {
|
||||
deps.dispatchAutomation("task_status_changed", { companyId: cId, taskId, previousStatus, newStatus });
|
||||
publishDomainChanged();
|
||||
|
||||
@@ -12,7 +12,7 @@ export function createIdleAutomationScheduler(deps: OrchestrationDependencies) {
|
||||
try {
|
||||
if (isQuitting() || !hasWorkerReady()) return;
|
||||
|
||||
const activeCompanies = db.listActiveCompanyIds();
|
||||
const activeCompanies = db.company.listActiveCompanyIds();
|
||||
const now = Date.now();
|
||||
|
||||
for (const company of activeCompanies) {
|
||||
|
||||
@@ -38,7 +38,7 @@ export function createTaskRunner(
|
||||
return fail("WORKER_UNAVAILABLE", "Orchestration worker is not ready.");
|
||||
}
|
||||
const task = db.entityCrud.getTask(taskId);
|
||||
const companyStatus = db.getCompanyStatus(task.companyId);
|
||||
const companyStatus = db.company.getCompanyStatus(task.companyId);
|
||||
if (companyStatus !== "active") {
|
||||
return fail("COMPANY_NOT_ACTIVE", "Runs can only start while the company is active.");
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export function createTaskRunner(
|
||||
if (db.agents.isAgentPendingApproval(agent.id)) {
|
||||
return fail("AGENT_PENDING_APPROVAL", "This agent is pending hire approval and cannot run tasks.");
|
||||
}
|
||||
const blockingApproval = db.getBlockingApproval(task.companyId, agent.id, task.id);
|
||||
const blockingApproval = db.approvalsMod.getBlockingApproval(task.companyId, agent.id, task.id);
|
||||
if (blockingApproval) {
|
||||
return fail(
|
||||
"APPROVAL_GATE_BLOCKED",
|
||||
@@ -99,7 +99,7 @@ export function createTaskRunner(
|
||||
return fail("WORKSPACE_COMPANY_MISMATCH", "The selected workspace does not belong to the task company.");
|
||||
}
|
||||
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
id: runId,
|
||||
companyId: task.companyId,
|
||||
taskId: task.id,
|
||||
@@ -108,7 +108,7 @@ export function createTaskRunner(
|
||||
connectorId: connector.id,
|
||||
});
|
||||
|
||||
const companyBasic = db.getCompanyBasic(task.companyId);
|
||||
const companyBasic = db.company.getCompanyBasic(task.companyId);
|
||||
dispatchRunToWorker({
|
||||
runId: run.id,
|
||||
companyId: task.companyId,
|
||||
@@ -163,7 +163,7 @@ export function createTaskRunner(
|
||||
const snapshot = db.listSnapshot();
|
||||
const companiesById = new Map(snapshot.companies.map((company) => [company.id, company]));
|
||||
|
||||
for (const run of db.listQueuedRuns()) {
|
||||
for (const run of db.runs.listQueuedRuns()) {
|
||||
const company = companiesById.get(run.companyId);
|
||||
if (!company || company.status !== "active") {
|
||||
continue;
|
||||
@@ -186,7 +186,7 @@ export function createTaskRunner(
|
||||
const eligibilityFailure = getRunEligibilityFailure(agent, connector, task?.projectId ?? null);
|
||||
if (eligibilityFailure) {
|
||||
if (eligibilityFailure === "CONNECTOR_MODEL_NOT_ALLOWED") {
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "failed",
|
||||
summary: "Queued run rejected during recovery because the agent model is disabled or invalid for its connector.",
|
||||
|
||||
@@ -288,7 +288,7 @@ export function buildRunPayload(params: RunDispatchParams, deps: TurnContextDeps
|
||||
task,
|
||||
companyName,
|
||||
locale: snapshot.locale,
|
||||
executionMode: db.getCompanyExecutionMode(companyId) as ExecutionMode,
|
||||
executionMode: db.company.getCompanyExecutionMode(companyId) as ExecutionMode,
|
||||
goals: snapshot.goals.filter((goal) => goal.companyId === companyId),
|
||||
projects: snapshot.projects.filter((project) => project.companyId === companyId),
|
||||
directReports: db.agents.getDirectReports(agent.id, companyId),
|
||||
@@ -354,7 +354,7 @@ export function buildRunPayload(params: RunDispatchParams, deps: TurnContextDeps
|
||||
systemPrompt,
|
||||
trigger,
|
||||
taskSessionId: taskSessionId ?? runId,
|
||||
executionMode: db.getCompanyExecutionMode(companyId) as "ai_ceo" | "ai_cofounder",
|
||||
executionMode: db.company.getCompanyExecutionMode(companyId) as "ai_ceo" | "ai_cofounder",
|
||||
initialUserTurn,
|
||||
};
|
||||
}
|
||||
@@ -214,7 +214,7 @@ export function sweepStaleReviews(
|
||||
const chain = deps.db.agents.getChainOfCommand(reviewer.id);
|
||||
const escalationTarget = chain.length > 0 ? chain[0] : null;
|
||||
|
||||
const pendingApproval = deps.db.findPendingApprovalForTask(companyId, task.id);
|
||||
const pendingApproval = deps.db.approvalsMod.findPendingApprovalForTask(companyId, task.id);
|
||||
|
||||
// In ai_cofounder mode, ALL approvals require human decision — never auto-advance.
|
||||
if (isCofounderMode && pendingApproval) {
|
||||
|
||||
@@ -69,7 +69,7 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
|
||||
// In co-founder mode, set wait state when task enters review
|
||||
if (newStatus === "in_review") {
|
||||
const mode = db.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
const mode = db.company.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
if (mode === "ai_cofounder") {
|
||||
const reason = `@Board review needed: Task "${task.title}" is ready for your review.`;
|
||||
db.setThreadWaitState({
|
||||
@@ -104,7 +104,7 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
*/
|
||||
onApprovalCreated(approvalId: string, companyId: string, taskId: string | null, approvalType: string, summary: string): void {
|
||||
try {
|
||||
const approval = db.getApprovalRecord(approvalId);
|
||||
const approval = db.approvalsMod.getApprovalRecord(approvalId);
|
||||
const task = taskId ? db.entityCrud.getTask(taskId) : null;
|
||||
const thread = task
|
||||
? ensureTaskThread(db, task.id, companyId, task.title)
|
||||
@@ -127,7 +127,7 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
db.subscribe({ threadId: thread.id, subscriberId: "user", subscriberType: "user" });
|
||||
|
||||
// In co-founder mode, set wait state for user approval
|
||||
const mode = db.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
const mode = db.company.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
if (mode === "ai_cofounder") {
|
||||
const approvalReason = `@Board approval needed: ${approvalType}`;
|
||||
db.setThreadWaitState({
|
||||
@@ -247,7 +247,7 @@ export function createThreadIntegration({ db, logger, emitEvent, publishDomainCh
|
||||
// In co-founder mode, successful runs create a user_reply wait state
|
||||
// so the heartbeat pauses until the human reviews the output
|
||||
if (status === "succeeded") {
|
||||
const mode = db.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
const mode = db.company.getCompanyExecutionMode(companyId) as ExecutionMode;
|
||||
if (mode === "ai_cofounder") {
|
||||
const task = db.entityCrud.getTask(taskId);
|
||||
const completionReason = `@Board review needed: Agent completed work on "${task.title}" — awaiting your review.`;
|
||||
|
||||
@@ -150,7 +150,7 @@ export function announceApprovalToBoard(
|
||||
);
|
||||
if (!target) return;
|
||||
|
||||
const messageId = db.sendAgentMessage({
|
||||
const messageId = db.messages.sendAgentMessage({
|
||||
companyId: approval.companyId,
|
||||
fromAgentId: approval.requestedByAgentId,
|
||||
channel: target.channel,
|
||||
@@ -160,7 +160,7 @@ export function announceApprovalToBoard(
|
||||
priority: "urgent",
|
||||
senderKind: "agent",
|
||||
});
|
||||
const message = db.getAgentMessage(messageId, approval.companyId);
|
||||
const message = db.messages.getAgentMessage(messageId, approval.companyId);
|
||||
if (message) {
|
||||
deps.emitEvent({ type: "new-message", message });
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export function createWorkerService({
|
||||
const snapshot = db.listSnapshot();
|
||||
for (const run of snapshot.runs) {
|
||||
if (run.status === "running" || run.status === "queued") {
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "interrupted",
|
||||
summary: "Application shut down.",
|
||||
|
||||
@@ -33,7 +33,7 @@ export function handleWorkerExit(
|
||||
|
||||
for (const run of snapshot.runs) {
|
||||
if (run.status === "running") {
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "interrupted",
|
||||
summary: "Worker process crashed.",
|
||||
@@ -119,7 +119,7 @@ export function handleWorkerExit(
|
||||
});
|
||||
} catch (error) {
|
||||
logger.warn(`[worker] Failed to re-enqueue run ${candidate.id}: ${error instanceof Error ? error.message : String(error)}`);
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: candidate.id,
|
||||
status: "interrupted",
|
||||
summary: "Could not re-enqueue after worker restart.",
|
||||
|
||||
@@ -6,13 +6,13 @@ import type { workerMessageSchema } from "../entry/types";
|
||||
import type { HandlerContext, HandlerDeps, WorkerMessageEvent } from "./handler-context";
|
||||
|
||||
type RunFinishedEvent = Extract<WorkerMessageEvent, { type: "run-finished" }>;
|
||||
type RunRecord = ReturnType<HandlerDeps["db"]["getRun"]>;
|
||||
type RunRecord = ReturnType<HandlerDeps["db"]["runs"]["getRun"]>;
|
||||
type TaskRecord = ReturnType<HandlerDeps["db"]["entityCrud"]["getTask"]>;
|
||||
|
||||
export interface RunFinishedContext {
|
||||
run: RunRecord;
|
||||
task: TaskRecord | null;
|
||||
runOutcome: ReturnType<HandlerDeps["db"]["finishRun"]>;
|
||||
runOutcome: ReturnType<HandlerDeps["db"]["runs"]["finishRun"]>;
|
||||
}
|
||||
|
||||
export function finalizeRun(
|
||||
@@ -25,7 +25,7 @@ export function finalizeRun(
|
||||
|
||||
let run: RunRecord;
|
||||
try {
|
||||
run = db.getRun(event.runId);
|
||||
run = db.runs.getRun(event.runId);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`[finalizeRun] getRun(${event.runId}) failed; run may have been deleted: ${error instanceof Error ? error.message : String(error)}`,
|
||||
@@ -66,7 +66,7 @@ export function finalizeRun(
|
||||
}
|
||||
|
||||
leaseRenewalMap.delete(event.runId);
|
||||
const runOutcome = db.finishRun(event);
|
||||
const runOutcome = db.runs.finishRun(event);
|
||||
if (run.workspaceId) {
|
||||
db.releaseWorkspaceLock(run.workspaceId, run.id);
|
||||
}
|
||||
@@ -350,7 +350,7 @@ function hasUnfinishedTeamBuilding(
|
||||
if (activeReports.length > 0) return false;
|
||||
const goalCount = db.countActiveGoalsForCompany(companyId);
|
||||
if (goalCount === 0) return false;
|
||||
const pendingHires = db
|
||||
const pendingHires = db.approvalsMod
|
||||
.listCompanyApprovalsByType(companyId, "hire_agent")
|
||||
.filter((a) => a.state === "pending");
|
||||
return pendingHires.length === 0;
|
||||
|
||||
@@ -16,8 +16,8 @@ export async function handleRunLog(
|
||||
const filtered = filterRunLogChunk(event.stream, event.chunk);
|
||||
if (filtered === null) return;
|
||||
|
||||
await db.appendRunLog(event.runId, event.stream, filtered);
|
||||
const runState = db.getRunAuthState(event.runId);
|
||||
await db.runs.appendRunLog(event.runId, event.stream, filtered);
|
||||
const runState = db.runs.getRunAuthState(event.runId);
|
||||
if (runState) {
|
||||
persistRunEvent(db, logger, {
|
||||
companyId: runState.companyId,
|
||||
@@ -36,7 +36,7 @@ export async function handleRunLog(
|
||||
const lastRenewal = leaseRenewalMap.get(event.runId) ?? 0;
|
||||
if (now - lastRenewal >= LEASE_RENEWAL_INTERVAL_MS) {
|
||||
try {
|
||||
const run = db.getRun(event.runId);
|
||||
const run = db.runs.getRun(event.runId);
|
||||
if (run.taskId) {
|
||||
db.tasks.renewTaskLease(run.taskId, run.id);
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ export function handleRunStatus(
|
||||
): void {
|
||||
const { db, logger, emitEvent, onRunStarted } = deps;
|
||||
|
||||
const runState = db.getRunAuthState(event.runId);
|
||||
db.markRunStatus(event.runId, event.status, event.message);
|
||||
const runState = db.runs.getRunAuthState(event.runId);
|
||||
db.runs.markRunStatus(event.runId, event.status, event.message);
|
||||
if (runState) {
|
||||
persistRunEvent(db, logger, {
|
||||
companyId: runState.companyId,
|
||||
@@ -30,7 +30,7 @@ export function handleRunStatus(
|
||||
// dispatchRunToWorker before enqueue. Only lease renewal and
|
||||
// run-started housekeeping run here.
|
||||
try {
|
||||
const run = db.getRun(event.runId);
|
||||
const run = db.runs.getRun(event.runId);
|
||||
if (run.taskId) {
|
||||
db.tasks.renewTaskLease(run.taskId, run.id);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export function persistRunEvent(
|
||||
input: PersistInput,
|
||||
) {
|
||||
try {
|
||||
db.appendRunEvent(input);
|
||||
db.runs.appendRunEvent(input);
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`[run-events] Failed to append ${input.eventType} for ${input.runId}: ${
|
||||
|
||||
@@ -58,7 +58,7 @@ function createHarness(db: AppDatabase) {
|
||||
runDispatch.resolveWorkspace,
|
||||
runDispatch.getRunEligibilityFailure,
|
||||
runDispatch.dispatchRunToWorker,
|
||||
(filter) => db.hasActiveRun(filter),
|
||||
(filter) => db.runs.hasActiveRun(filter),
|
||||
);
|
||||
|
||||
return { enqueueWorkerRun, taskRunner };
|
||||
@@ -78,7 +78,7 @@ describe("agent model switching", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Model Switch Co",
|
||||
description: "Tests post-hire model switching",
|
||||
status: "active",
|
||||
@@ -126,7 +126,7 @@ describe("agent model switching", () => {
|
||||
throw new Error("Expected first run to queue successfully.");
|
||||
}
|
||||
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: firstRun.data,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
|
||||
@@ -32,7 +32,7 @@ async function createHarness() {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Recursive Org Co",
|
||||
description: "Tests manager-driven recursive hires",
|
||||
status: "active",
|
||||
@@ -81,7 +81,7 @@ async function createHarness() {
|
||||
priority: "high",
|
||||
status: "in_progress",
|
||||
});
|
||||
const run = db.createRun({ companyId, taskId, agentId: ctoId, workspaceId, connectorId: "openai-codex" });
|
||||
const run = db.runs.createRun({ companyId, taskId, agentId: ctoId, workspaceId, connectorId: "openai-codex" });
|
||||
const token = createAgentToken(ctoId, companyId, run.id);
|
||||
const server = createApiServer(db, () => {}, () => null);
|
||||
const port = await server.start();
|
||||
@@ -130,12 +130,12 @@ describe("recursive org execution", () => {
|
||||
expect(pendingHire.status).toBe("pending_approval");
|
||||
expect(pendingHire.reportsTo).toBe(harness.ctoId);
|
||||
|
||||
const approval = harness.db.getApprovalRecord(body.approvalId);
|
||||
const approval = harness.db.approvalsMod.getApprovalRecord(body.approvalId);
|
||||
expect(approval.type).toBe("hire_agent");
|
||||
expect(approval.requestedByAgentId).toBe(harness.ctoId);
|
||||
expect(approval.relatedAgentId).toBe(body.agentId);
|
||||
|
||||
harness.db.decideApproval({
|
||||
harness.db.approvalsMod.decideApproval({
|
||||
approvalId: body.approvalId,
|
||||
state: "approved",
|
||||
decisionNote: "Approved by board",
|
||||
@@ -192,7 +192,7 @@ describe("recursive org execution", () => {
|
||||
priority: "high",
|
||||
status: "in_progress",
|
||||
});
|
||||
const hrRun = harness.db.createRun({
|
||||
const hrRun = harness.db.runs.createRun({
|
||||
companyId: harness.companyId,
|
||||
taskId: hrTaskId,
|
||||
agentId: hrId,
|
||||
|
||||
@@ -24,7 +24,7 @@ async function createHarness() {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({ name: "Orbit Foundry", description: "API test", status: "active" });
|
||||
const companyId = db.company.saveCompany({ name: "Orbit Foundry", description: "API test", status: "active" });
|
||||
const workspaceId = db.saveWorkspace({
|
||||
companyId,
|
||||
name: "Primary",
|
||||
@@ -55,7 +55,7 @@ async function createHarness() {
|
||||
priority: "high",
|
||||
status: "todo",
|
||||
});
|
||||
const run = db.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "openai-codex" });
|
||||
const run = db.runs.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "openai-codex" });
|
||||
const token = createAgentToken(agentId, companyId, run.id);
|
||||
const server = createApiServer(db, () => {}, () => null);
|
||||
const port = await server.start();
|
||||
@@ -95,7 +95,7 @@ describe("run event API", () => {
|
||||
it("returns persisted run events for the active run token", async () => {
|
||||
const harness = await createHarness();
|
||||
try {
|
||||
harness.db.appendRunEvent({
|
||||
harness.db.runs.appendRunEvent({
|
||||
companyId: harness.companyId,
|
||||
runId: harness.runId,
|
||||
agentId: harness.agentId,
|
||||
@@ -141,7 +141,7 @@ describe("run event API", () => {
|
||||
expect(reader).toBeDefined();
|
||||
|
||||
setTimeout(() => {
|
||||
harness.db.appendRunEvent({
|
||||
harness.db.runs.appendRunEvent({
|
||||
companyId: harness.companyId,
|
||||
runId: harness.runId,
|
||||
agentId: harness.agentId,
|
||||
|
||||
@@ -14,7 +14,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const hireId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "pending_approval" as "idle" });
|
||||
|
||||
const approvalId = shared.db.requestApproval({
|
||||
const approvalId = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "hire_agent",
|
||||
@@ -23,7 +23,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
payloadJson: JSON.stringify({ agentId: hireId }),
|
||||
});
|
||||
|
||||
shared.db.decideApproval({
|
||||
shared.db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "approved",
|
||||
decisionNote: "Approved by board",
|
||||
@@ -37,7 +37,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const hireId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "pending_approval" as "idle" });
|
||||
|
||||
const approvalId = shared.db.requestApproval({
|
||||
const approvalId = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "hire_agent",
|
||||
@@ -46,7 +46,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
payloadJson: JSON.stringify({ agentId: hireId }),
|
||||
});
|
||||
|
||||
shared.db.decideApproval({
|
||||
shared.db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "rejected",
|
||||
decisionNote: "Not needed",
|
||||
@@ -59,7 +59,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
it("getApprovalRecord includes payloadJson", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
const hireId = createAgent({ name: "CTO", role: "CTO", reportsTo: ceoId, status: "pending_approval" as "idle" });
|
||||
const approvalId = shared.db.requestApproval({
|
||||
const approvalId = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "hire_agent",
|
||||
@@ -68,7 +68,7 @@ describe("Step 4: Hire approval + decideApproval", () => {
|
||||
payloadJson: JSON.stringify({ agentId: hireId, config: { connector: "openai-codex" } }),
|
||||
});
|
||||
|
||||
const approval = shared.db.getApprovalRecord(approvalId);
|
||||
const approval = shared.db.approvalsMod.getApprovalRecord(approvalId);
|
||||
expect(approval.payloadJson).toBeTruthy();
|
||||
const payload = JSON.parse(approval.payloadJson!);
|
||||
expect(payload.agentId).toBe(hireId);
|
||||
|
||||
@@ -104,7 +104,7 @@ describe("Agent Messages", () => {
|
||||
});
|
||||
|
||||
it("sends and retrieves direct messages", () => {
|
||||
const msgId = shared.db.sendAgentMessage({
|
||||
const msgId = shared.db.messages.sendAgentMessage({
|
||||
companyId: shared.companyId,
|
||||
fromAgentId: agentAId,
|
||||
toAgentId: agentBId,
|
||||
@@ -116,7 +116,7 @@ describe("Agent Messages", () => {
|
||||
|
||||
expect(msgId).toBeTruthy();
|
||||
|
||||
const messages = shared.db.listAgentMessages(shared.companyId);
|
||||
const messages = shared.db.messages.listAgentMessages(shared.companyId);
|
||||
expect(messages).toHaveLength(1);
|
||||
expect(messages[0].subject).toBe("Need design review");
|
||||
expect(messages[0].fromAgentId).toBe(agentAId);
|
||||
@@ -125,7 +125,7 @@ describe("Agent Messages", () => {
|
||||
});
|
||||
|
||||
it("marks messages as read", () => {
|
||||
const msgId = shared.db.sendAgentMessage({
|
||||
const msgId = shared.db.messages.sendAgentMessage({
|
||||
companyId: shared.companyId,
|
||||
fromAgentId: agentAId,
|
||||
toAgentId: agentBId,
|
||||
@@ -133,14 +133,14 @@ describe("Agent Messages", () => {
|
||||
body: "Hello",
|
||||
});
|
||||
|
||||
shared.db.markMessageReadForReader(msgId, shared.companyId);
|
||||
const messages = shared.db.listAgentMessages(shared.companyId);
|
||||
shared.db.messages.markMessageReadForReader(msgId, shared.companyId);
|
||||
const messages = shared.db.messages.listAgentMessages(shared.companyId);
|
||||
const updatedMessage = messages.find((message) => message.id === msgId);
|
||||
expect(updatedMessage?.readAt).toBeTruthy();
|
||||
});
|
||||
|
||||
it("tracks read state per agent without marking the sender unread", () => {
|
||||
const msgId = shared.db.sendAgentMessage({
|
||||
const msgId = shared.db.messages.sendAgentMessage({
|
||||
companyId: shared.companyId,
|
||||
fromAgentId: agentAId,
|
||||
toAgentId: agentBId,
|
||||
@@ -148,22 +148,22 @@ describe("Agent Messages", () => {
|
||||
body: "Hello",
|
||||
});
|
||||
|
||||
const senderView = shared.db.listAgentMessages(shared.companyId, { agentId: agentAId });
|
||||
const senderView = shared.db.messages.listAgentMessages(shared.companyId, { agentId: agentAId });
|
||||
expect(senderView).toHaveLength(1);
|
||||
expect(senderView[0].id).toBe(msgId);
|
||||
expect(senderView[0].readAt).toBeTruthy();
|
||||
|
||||
const recipientViewBefore = shared.db.listAgentMessages(shared.companyId, { agentId: agentBId, unreadOnly: true });
|
||||
const recipientViewBefore = shared.db.messages.listAgentMessages(shared.companyId, { agentId: agentBId, unreadOnly: true });
|
||||
expect(recipientViewBefore).toHaveLength(1);
|
||||
expect(recipientViewBefore[0].id).toBe(msgId);
|
||||
|
||||
shared.db.markMessageReadForReader(msgId, shared.companyId, agentBId);
|
||||
shared.db.messages.markMessageReadForReader(msgId, shared.companyId, agentBId);
|
||||
|
||||
const recipientViewAfter = shared.db.listAgentMessages(shared.companyId, { agentId: agentBId, unreadOnly: true });
|
||||
const recipientViewAfter = shared.db.messages.listAgentMessages(shared.companyId, { agentId: agentBId, unreadOnly: true });
|
||||
expect(recipientViewAfter).toHaveLength(0);
|
||||
|
||||
const thirdAgentId = createAgent({ name: "Agent C" });
|
||||
const thirdView = shared.db.listAgentMessages(shared.companyId, { agentId: thirdAgentId });
|
||||
const thirdView = shared.db.messages.listAgentMessages(shared.companyId, { agentId: thirdAgentId });
|
||||
expect(thirdView).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -188,7 +188,7 @@ describe("Agent Messages", () => {
|
||||
priority: "medium",
|
||||
});
|
||||
|
||||
const deptMessageId = shared.db.sendAgentMessage({
|
||||
const deptMessageId = shared.db.messages.sendAgentMessage({
|
||||
companyId: shared.companyId,
|
||||
fromAgentId: engineeringLeadId,
|
||||
channel: "department",
|
||||
@@ -196,7 +196,7 @@ describe("Agent Messages", () => {
|
||||
subject: "Engineering update",
|
||||
body: "Only engineering should see this",
|
||||
});
|
||||
const projectMessageId = shared.db.sendAgentMessage({
|
||||
const projectMessageId = shared.db.messages.sendAgentMessage({
|
||||
companyId: shared.companyId,
|
||||
fromAgentId: engineeringLeadId,
|
||||
channel: "project",
|
||||
@@ -205,30 +205,30 @@ describe("Agent Messages", () => {
|
||||
body: "Only project members should see this",
|
||||
});
|
||||
|
||||
const engineeringView = shared.db.listAgentMessages(shared.companyId, { agentId: engineeringLeadId });
|
||||
const engineeringView = shared.db.messages.listAgentMessages(shared.companyId, { agentId: engineeringLeadId });
|
||||
expect(engineeringView.some((message) => message.id === deptMessageId)).toBe(true);
|
||||
expect(engineeringView.some((message) => message.id === projectMessageId)).toBe(true);
|
||||
|
||||
const productView = shared.db.listAgentMessages(shared.companyId, { agentId: productLeadId });
|
||||
const productView = shared.db.messages.listAgentMessages(shared.companyId, { agentId: productLeadId });
|
||||
expect(productView.some((message) => message.id === deptMessageId)).toBe(false);
|
||||
expect(productView.some((message) => message.id === projectMessageId)).toBe(false);
|
||||
});
|
||||
|
||||
it("filters messages by channel", () => {
|
||||
shared.db.sendAgentMessage({ companyId: shared.companyId, fromAgentId: agentAId, subject: "Dept update", body: "All good", channel: "department", channelTargetId: "engineering" });
|
||||
shared.db.sendAgentMessage({ companyId: shared.companyId, fromAgentId: agentAId, toAgentId: agentBId, subject: "DM", body: "Hey", channel: "direct" });
|
||||
shared.db.messages.sendAgentMessage({ companyId: shared.companyId, fromAgentId: agentAId, subject: "Dept update", body: "All good", channel: "department", channelTargetId: "engineering" });
|
||||
shared.db.messages.sendAgentMessage({ companyId: shared.companyId, fromAgentId: agentAId, toAgentId: agentBId, subject: "DM", body: "Hey", channel: "direct" });
|
||||
|
||||
const deptMsgs = shared.db.listAgentMessages(shared.companyId, { channel: "department" });
|
||||
const deptMsgs = shared.db.messages.listAgentMessages(shared.companyId, { channel: "department" });
|
||||
expect(deptMsgs).toHaveLength(1);
|
||||
expect(deptMsgs[0].subject).toBe("Dept update");
|
||||
|
||||
const directMsgs = shared.db.listAgentMessages(shared.companyId, { channel: "direct" });
|
||||
const directMsgs = shared.db.messages.listAgentMessages(shared.companyId, { channel: "direct" });
|
||||
expect(directMsgs).toHaveLength(1);
|
||||
expect(directMsgs[0].subject).toBe("DM");
|
||||
});
|
||||
|
||||
it("appears in snapshot", () => {
|
||||
shared.db.sendAgentMessage({ companyId: shared.companyId, fromAgentId: agentAId, channel: "company", subject: "Snapshot test", body: "Hi" });
|
||||
shared.db.messages.sendAgentMessage({ companyId: shared.companyId, fromAgentId: agentAId, channel: "company", subject: "Snapshot test", body: "Hi" });
|
||||
const snapshot = shared.db.listSnapshot();
|
||||
expect(snapshot.agentMessages.length).toBeGreaterThanOrEqual(1);
|
||||
expect(snapshot.agentMessages[0].subject).toBe("Snapshot test");
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("Step 7: Worker crash recovery (database layer)", () => {
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
const run = shared.db.createRun({
|
||||
const run = shared.db.runs.createRun({
|
||||
companyId: shared.companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -31,7 +31,7 @@ describe("Step 7: Worker crash recovery (database layer)", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
|
||||
shared.db.markRunStatus(run.id, "running", "Started");
|
||||
shared.db.runs.markRunStatus(run.id, "running", "Started");
|
||||
|
||||
// Verify it's running
|
||||
let snapshot = shared.db.listSnapshot();
|
||||
@@ -61,7 +61,7 @@ describe("Step 7: Worker crash recovery (database layer)", () => {
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
const run = shared.db.createRun({
|
||||
const run = shared.db.runs.createRun({
|
||||
companyId: shared.companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -69,10 +69,10 @@ describe("Step 7: Worker crash recovery (database layer)", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
|
||||
shared.db.markRunStatus(run.id, "running", "Started");
|
||||
shared.db.runs.markRunStatus(run.id, "running", "Started");
|
||||
|
||||
// Simulate crash recovery - mark as interrupted
|
||||
shared.db.finishRun({
|
||||
shared.db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "interrupted",
|
||||
summary: "Worker process crashed.",
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
const cmoId = createAgent({ name: "Aria", role: "CMO", reportsTo: ceoId, status: "pending_approval" as "idle" });
|
||||
|
||||
// 3. CEO submits org strategy approval
|
||||
const orgApprovalId = shared.db.requestApproval({
|
||||
const orgApprovalId = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "approve_ceo_strategy",
|
||||
@@ -64,7 +64,7 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
expect(phase1Prompt).toContain("Awaiting Org Strategy Approval");
|
||||
|
||||
// 4. Board approves hires
|
||||
const hireApprovalCTO = shared.db.requestApproval({
|
||||
const hireApprovalCTO = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "hire_agent",
|
||||
@@ -72,9 +72,9 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
impactSummary: "Test",
|
||||
payloadJson: JSON.stringify({ agentId: ctoId }),
|
||||
});
|
||||
shared.db.decideApproval({ approvalId: hireApprovalCTO, state: "approved", decisionNote: "Approved" });
|
||||
shared.db.approvalsMod.decideApproval({ approvalId: hireApprovalCTO, state: "approved", decisionNote: "Approved" });
|
||||
|
||||
const hireApprovalCMO = shared.db.requestApproval({
|
||||
const hireApprovalCMO = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "hire_agent",
|
||||
@@ -82,14 +82,14 @@ describe("E2E: Full autonomous CEO cold start flow", () => {
|
||||
impactSummary: "Test",
|
||||
payloadJson: JSON.stringify({ agentId: cmoId }),
|
||||
});
|
||||
shared.db.decideApproval({ approvalId: hireApprovalCMO, state: "approved", decisionNote: "Approved" });
|
||||
shared.db.approvalsMod.decideApproval({ approvalId: hireApprovalCMO, state: "approved", decisionNote: "Approved" });
|
||||
|
||||
// Agents now idle
|
||||
expect(shared.db.entityCrud.getAgent(ctoId).status).toBe("idle");
|
||||
expect(shared.db.entityCrud.getAgent(cmoId).status).toBe("idle");
|
||||
|
||||
// 5. Board approves org strategy
|
||||
shared.db.decideApproval({ approvalId: orgApprovalId, state: "approved", decisionNote: "Go ahead" });
|
||||
shared.db.approvalsMod.decideApproval({ approvalId: orgApprovalId, state: "approved", decisionNote: "Go ahead" });
|
||||
|
||||
// 6. CEO now in Phase 2
|
||||
const updatedCto = shared.db.entityCrud.getAgent(ctoId);
|
||||
@@ -287,7 +287,7 @@ describe("Review Fix: Rejected approval wakes agent", () => {
|
||||
it("decideApproval with rejected state still triggers wake (tested via DB)", () => {
|
||||
const ceoId = createAgent({ name: "CEO", role: "CEO" });
|
||||
|
||||
const approvalId = shared.db.requestApproval({
|
||||
const approvalId = shared.db.approvalsMod.requestApproval({
|
||||
companyId: shared.companyId,
|
||||
requestedByAgentId: ceoId,
|
||||
type: "approve_ceo_strategy",
|
||||
@@ -295,14 +295,14 @@ describe("Review Fix: Rejected approval wakes agent", () => {
|
||||
impactSummary: "test",
|
||||
});
|
||||
|
||||
shared.db.decideApproval({
|
||||
shared.db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "rejected",
|
||||
decisionNote: "Not ready",
|
||||
});
|
||||
|
||||
// Verify approval state
|
||||
const approval = shared.db.getApprovalRecord(approvalId);
|
||||
const approval = shared.db.approvalsMod.getApprovalRecord(approvalId);
|
||||
expect(approval.state).toBe("rejected");
|
||||
// The IPC handler in index.ts now wakes on rejected too —
|
||||
// we verify the DB layer works; the wakeAgentIfPossible call is in index.ts
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("Step 6: Run failure escalation (database layer)", () => {
|
||||
status: "in_progress",
|
||||
});
|
||||
|
||||
const run = shared.db.createRun({
|
||||
const run = shared.db.runs.createRun({
|
||||
companyId: shared.companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -30,9 +30,9 @@ describe("Step 6: Run failure escalation (database layer)", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
|
||||
shared.db.markRunStatus(run.id, "running", "Started");
|
||||
shared.db.runs.markRunStatus(run.id, "running", "Started");
|
||||
|
||||
const outcome = shared.db.finishRun({
|
||||
const outcome = shared.db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "failed",
|
||||
summary: null,
|
||||
|
||||
@@ -54,7 +54,7 @@ export function installSharedSetup(): SharedHandle {
|
||||
state.db = new AppDatabase(profileDir);
|
||||
state.db.init();
|
||||
|
||||
state.companyId = state.db.saveCompany({
|
||||
state.companyId = state.db.company.saveCompany({
|
||||
name: "Autonomous Test Corp",
|
||||
description: "Test company for autonomous chain",
|
||||
status: "active",
|
||||
|
||||
@@ -57,7 +57,7 @@ function createOrchestrationHarness(db: AppDatabase) {
|
||||
runDispatch.resolveWorkspace,
|
||||
runDispatch.getRunEligibilityFailure,
|
||||
runDispatch.dispatchRunToWorker,
|
||||
(filter) => db.hasActiveRun(filter),
|
||||
(filter) => db.runs.hasActiveRun(filter),
|
||||
);
|
||||
|
||||
return { deps, taskRunner };
|
||||
@@ -77,7 +77,7 @@ describe("connector model enforcement", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Runtime Enforcement Co",
|
||||
description: "Blocks runs when agent models are disabled",
|
||||
status: "active",
|
||||
@@ -141,7 +141,7 @@ describe("connector model enforcement", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Recovered Run Co",
|
||||
description: "Rejects invalid queued runs during recovery",
|
||||
status: "active",
|
||||
@@ -177,7 +177,7 @@ describe("connector model enforcement", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
|
||||
@@ -64,7 +64,7 @@ function scaffold(db: AppDatabase, budgetAmountCents = 500): ScaffoldResult {
|
||||
notes: "Non-exempt connector for session rotation tests",
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Pipeline Test Corp",
|
||||
description: "Cross-subsystem pipeline integration tests",
|
||||
status: "active",
|
||||
@@ -187,7 +187,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// 2. CAS checkout: create run and checkout task
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId,
|
||||
agentId: s.agentId,
|
||||
@@ -207,9 +207,9 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
expect(budgetResult.blockedBy).toHaveLength(0);
|
||||
|
||||
// 4. Execute run
|
||||
db.markRunStatus(runId, "running", "Starting");
|
||||
db.recordCostEntry({ runId, companyId: s.companyId, agentId: s.agentId, taskId, connectorId: "gemini-cli", amountUsd: 0.05, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(runId, "running", "Starting");
|
||||
db.runs.recordCostEntry({ runId, companyId: s.companyId, agentId: s.agentId, taskId, connectorId: "gemini-cli", amountUsd: 0.05, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId,
|
||||
status: "succeeded",
|
||||
summary: "Login page built with form validation",
|
||||
@@ -229,7 +229,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
sessionService.accumulateTokenUsage(sessionId, 5000, 2000, runId);
|
||||
|
||||
// 6. Verify final state
|
||||
const run = db.getRun(runId);
|
||||
const run = db.runs.getRun(runId);
|
||||
expect(run.status).toBe("succeeded");
|
||||
|
||||
const task = db.entityCrud.getTask(taskId);
|
||||
@@ -266,7 +266,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// First agent checks out
|
||||
const { id: runId1 } = db.createRun({
|
||||
const { id: runId1 } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId,
|
||||
agentId: s.agentId,
|
||||
@@ -289,7 +289,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
capabilities: "Backend development",
|
||||
budgetMonthlyUsd: 100,
|
||||
});
|
||||
const { id: runId2 } = db.createRun({
|
||||
const { id: runId2 } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId,
|
||||
agentId: agent2Id,
|
||||
@@ -322,7 +322,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId,
|
||||
agentId: s.agentId,
|
||||
@@ -331,9 +331,9 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "Starting");
|
||||
db.recordCostEntry({ runId, companyId: s.companyId, agentId: s.agentId, taskId, connectorId: s.connectorId, amountUsd: 1.5, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(runId, "running", "Starting");
|
||||
db.runs.recordCostEntry({ runId, companyId: s.companyId, agentId: s.agentId, taskId, connectorId: s.connectorId, amountUsd: 1.5, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
@@ -382,7 +382,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
});
|
||||
|
||||
// Run 1: create run and accumulate tokens
|
||||
const { id: runId1 } = db.createRun({
|
||||
const { id: runId1 } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId,
|
||||
agentId: s.agentId,
|
||||
@@ -392,7 +392,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
sessionService.accumulateTokenUsage(sessionId, 5000, 2000, runId1);
|
||||
|
||||
// Run 2: create another run and accumulate more tokens
|
||||
const { id: runId2 } = db.createRun({
|
||||
const { id: runId2 } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId,
|
||||
agentId: s.agentId,
|
||||
@@ -432,7 +432,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const { id: runId1 } = db.createRun({
|
||||
const { id: runId1 } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId: taskId1,
|
||||
agentId: s.agentId,
|
||||
@@ -440,9 +440,9 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
db.tasks.checkoutTask(taskId1, runId1);
|
||||
db.markRunStatus(runId1, "running", "Starting");
|
||||
db.recordCostEntry({ runId: runId1, companyId: s.companyId, agentId: s.agentId, taskId: taskId1, connectorId: s.connectorId, amountUsd: 3.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(runId1, "running", "Starting");
|
||||
db.runs.recordCostEntry({ runId: runId1, companyId: s.companyId, agentId: s.agentId, taskId: taskId1, connectorId: s.connectorId, amountUsd: 3.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: runId1,
|
||||
status: "succeeded",
|
||||
summary: "First task done",
|
||||
@@ -473,7 +473,7 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const { id: runId2 } = db.createRun({
|
||||
const { id: runId2 } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId: taskId2,
|
||||
agentId: s.agentId,
|
||||
@@ -481,9 +481,9 @@ describe("Cross-Subsystem Pipeline (D-04)", () => {
|
||||
connectorId: s.connectorId,
|
||||
});
|
||||
db.tasks.checkoutTask(taskId2, runId2);
|
||||
db.markRunStatus(runId2, "running", "Starting");
|
||||
db.recordCostEntry({ runId: runId2, companyId: s.companyId, agentId: s.agentId, taskId: taskId2, connectorId: s.connectorId, amountUsd: 3.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(runId2, "running", "Starting");
|
||||
db.runs.recordCostEntry({ runId: runId2, companyId: s.companyId, agentId: s.agentId, taskId: taskId2, connectorId: s.connectorId, amountUsd: 3.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: runId2,
|
||||
status: "succeeded",
|
||||
summary: "Second task done",
|
||||
|
||||
@@ -37,7 +37,7 @@ describe("AppDatabase", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Orbit Foundry",
|
||||
description: "Test company",
|
||||
status: "active",
|
||||
@@ -113,7 +113,7 @@ describe("AppDatabase", () => {
|
||||
OPENAI_API_KEY: "sk-test-123",
|
||||
});
|
||||
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -121,11 +121,11 @@ describe("AppDatabase", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
|
||||
await db.appendRunLog(run.id, "stdout", "hello from run\n");
|
||||
expect(db.readRunLog(run.id)).toContain("hello from run");
|
||||
await db.runs.appendRunLog(run.id, "stdout", "hello from run\n");
|
||||
expect(db.runs.readRunLog(run.id)).toContain("hello from run");
|
||||
|
||||
db.markRunStatus(run.id, "running", "Connector launched");
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(run.id, "running", "Connector launched");
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Completed successfully",
|
||||
@@ -143,7 +143,7 @@ describe("AppDatabase", () => {
|
||||
|
||||
await db.exportBackup(backupDir);
|
||||
|
||||
db.saveCompany({
|
||||
db.company.saveCompany({
|
||||
name: "Temporary company",
|
||||
description: "Should disappear after restore",
|
||||
status: "active",
|
||||
@@ -164,13 +164,13 @@ describe("AppDatabase", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const alphaCompanyId = db.saveCompany({
|
||||
const alphaCompanyId = db.company.saveCompany({
|
||||
name: "Alpha",
|
||||
description: "Alpha company",
|
||||
status: "active",
|
||||
});
|
||||
|
||||
const betaCompanyId = db.saveCompany({
|
||||
const betaCompanyId = db.company.saveCompany({
|
||||
name: "Beta",
|
||||
description: "Beta company",
|
||||
status: "active",
|
||||
|
||||
@@ -34,7 +34,7 @@ async function createHarness() {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Document Provenance Co",
|
||||
description: "Tests document revision provenance through run completion",
|
||||
status: "active",
|
||||
@@ -82,7 +82,7 @@ async function createHarness() {
|
||||
status: "draft",
|
||||
});
|
||||
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -284,7 +284,7 @@ describe("document run provenance", () => {
|
||||
it("creates deliverable review approvals that stay linked to the originating task", async () => {
|
||||
const harness = await createHarness();
|
||||
try {
|
||||
harness.db.saveCompany({
|
||||
harness.db.company.saveCompany({
|
||||
id: harness.companyId,
|
||||
name: "Document Provenance Co",
|
||||
description: "Tests document review task linkage",
|
||||
|
||||
@@ -62,7 +62,7 @@ function scaffold(db: AppDatabase): ScaffoldResult {
|
||||
});
|
||||
|
||||
// Company
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Recovery Test Corp",
|
||||
description: "Process recovery integration tests",
|
||||
status: "active",
|
||||
@@ -157,7 +157,7 @@ function createAndStartRun(
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId: opts.companyId,
|
||||
taskId,
|
||||
agentId: opts.agentId,
|
||||
@@ -166,7 +166,7 @@ function createAndStartRun(
|
||||
});
|
||||
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "Starting execution");
|
||||
db.runs.markRunStatus(runId, "running", "Starting execution");
|
||||
|
||||
if (opts.claimWorkspace !== false) {
|
||||
db.claimWorkspaceLock(opts.workspaceId, runId);
|
||||
@@ -278,8 +278,8 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
const interruptedCount = simulateWorkerCrash(db);
|
||||
|
||||
expect(interruptedCount).toBe(2);
|
||||
expect(db.getRun(run1.runId).status).toBe("interrupted");
|
||||
expect(db.getRun(run2.runId).status).toBe("interrupted");
|
||||
expect(db.runs.getRun(run1.runId).status).toBe("interrupted");
|
||||
expect(db.runs.getRun(run2.runId).status).toBe("interrupted");
|
||||
});
|
||||
|
||||
it("releases task locks after crash", () => {
|
||||
@@ -378,7 +378,7 @@ describe("Process Recovery -- Worker Crash Detection", () => {
|
||||
priority: "medium",
|
||||
status: "todo",
|
||||
});
|
||||
const { id: newRunId } = db.createRun({
|
||||
const { id: newRunId } = db.runs.createRun({
|
||||
companyId: s.companyId,
|
||||
taskId: taskId2,
|
||||
agentId: agent2Id,
|
||||
@@ -530,7 +530,7 @@ describe("Process Recovery -- Multiple Concurrent Runs", () => {
|
||||
|
||||
expect(interruptedCount).toBe(3);
|
||||
for (const run of runs) {
|
||||
expect(db.getRun(run.runId).status).toBe("interrupted");
|
||||
expect(db.runs.getRun(run.runId).status).toBe("interrupted");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ describe("skill-aware capability registry", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Skill Match Co",
|
||||
description: "Tests skill-aware assignment",
|
||||
status: "active",
|
||||
@@ -122,7 +122,7 @@ describe("skill-aware capability registry", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Registry Co",
|
||||
description: "Tests capability registry prompt injection",
|
||||
status: "active",
|
||||
|
||||
@@ -36,7 +36,7 @@ function createScaffold(db: AppDatabase) {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Thread Wait Co",
|
||||
description: "Tests cofounder wait states and replies",
|
||||
status: "active",
|
||||
@@ -204,7 +204,7 @@ describe("thread wait + heartbeat integration", () => {
|
||||
throw new Error("Expected a task thread to exist for approval resolution testing.");
|
||||
}
|
||||
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: engineerId,
|
||||
@@ -238,7 +238,7 @@ describe("thread wait + heartbeat integration", () => {
|
||||
expect(waitStates).toHaveLength(1);
|
||||
expect(checkpointEngine.shouldPauseHeartbeat(companyId, engineerId)).toBe(true);
|
||||
|
||||
db.decideApproval({
|
||||
db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "approved",
|
||||
decisionNote: "Approved by board",
|
||||
|
||||
@@ -53,7 +53,7 @@ async function createHarness(): Promise<Harness> {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Work Products Co",
|
||||
description: "Tests archived visibility, bulk retirement, deliverable signal",
|
||||
status: "active",
|
||||
@@ -88,7 +88,7 @@ async function createHarness(): Promise<Harness> {
|
||||
priority: "medium",
|
||||
status: "in_progress",
|
||||
});
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("agent budget policy synchronization", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Budget Sync Co",
|
||||
description: "Budget sync regression coverage",
|
||||
status: "active",
|
||||
|
||||
@@ -23,7 +23,7 @@ describe("hire routing guards", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Routing Co",
|
||||
description: "Hire routing regression coverage",
|
||||
status: "active",
|
||||
|
||||
@@ -30,7 +30,7 @@ describe("canManageOrganization", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Org Permissions Co",
|
||||
description: "Tests recursive org authority",
|
||||
status: "active",
|
||||
@@ -100,7 +100,7 @@ describe("canManageOrganization", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Org Permission Override Co",
|
||||
description: "Tests permission-based org authority",
|
||||
status: "active",
|
||||
@@ -154,7 +154,7 @@ describe("canManageOrganization", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Scoped Authority Co",
|
||||
description: "Tests that branch growth does not unlock org-wide admin powers",
|
||||
status: "active",
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("hire approval routing", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Hire Routing Co",
|
||||
description: "Tests recursive hire wake routing",
|
||||
status: "active",
|
||||
@@ -91,7 +91,7 @@ describe("hire approval routing", () => {
|
||||
payloadJson: "{}",
|
||||
});
|
||||
|
||||
db.decideApproval({
|
||||
db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "approved",
|
||||
decisionNote: "Approved by board",
|
||||
|
||||
@@ -31,7 +31,7 @@ describe("deliverable review feedback writeback", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Review Feedback Co",
|
||||
description: "Tests review feedback knowledge writeback",
|
||||
status: "active",
|
||||
@@ -75,7 +75,7 @@ describe("deliverable review feedback writeback", () => {
|
||||
authorAgentId: agentId,
|
||||
status: "in_review",
|
||||
});
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: agentId,
|
||||
@@ -85,7 +85,7 @@ describe("deliverable review feedback writeback", () => {
|
||||
payloadJson: JSON.stringify({ documentIds: [documentId] }),
|
||||
});
|
||||
|
||||
db.decideApproval({
|
||||
db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "revision_requested",
|
||||
decisionNote: "Add stronger competitor evidence and a clearer recommendation.",
|
||||
|
||||
@@ -28,7 +28,7 @@ type RawDb = {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function createTestCompany(db: AppDatabase): string {
|
||||
return db.saveCompany({
|
||||
return db.company.saveCompany({
|
||||
name: "Test Company",
|
||||
description: "Atomic checkout tests",
|
||||
status: "active",
|
||||
@@ -109,7 +109,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -131,14 +131,14 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId1 } = db.createRun({
|
||||
const { id: runId1 } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
workspaceId,
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
const { id: runId2 } = db.createRun({
|
||||
const { id: runId2 } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -160,7 +160,7 @@ describe("CAS checkout", () => {
|
||||
// Manually set task to in_progress
|
||||
(db as unknown as RawDb).db.prepare("UPDATE tasks SET status = 'in_progress' WHERE id = ?").run(taskId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -178,7 +178,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -187,8 +187,8 @@ describe("CAS checkout", () => {
|
||||
});
|
||||
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "");
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(runId, "running", "");
|
||||
db.runs.finishRun({
|
||||
runId,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
@@ -214,7 +214,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -223,7 +223,7 @@ describe("CAS checkout", () => {
|
||||
});
|
||||
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "");
|
||||
db.runs.markRunStatus(runId, "running", "");
|
||||
|
||||
// Deleting the agent triggers cancelRunForDeletion for active runs
|
||||
db.agents.deleteAgent(agentId, companyId);
|
||||
@@ -243,7 +243,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -283,7 +283,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -316,7 +316,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -328,7 +328,7 @@ describe("CAS checkout", () => {
|
||||
|
||||
// Never call markRunStatus('running', ...) — this simulates the
|
||||
// "workspace already locked" bail-out inside dispatchRunToWorker.
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId,
|
||||
status: "failed",
|
||||
summary: null,
|
||||
@@ -343,7 +343,7 @@ describe("CAS checkout", () => {
|
||||
|
||||
// And the next CAS checkout must succeed — proving the heartbeat
|
||||
// retry path is unblocked.
|
||||
const { id: retryRunId } = db.createRun({
|
||||
const { id: retryRunId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -364,7 +364,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -372,7 +372,7 @@ describe("CAS checkout", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId,
|
||||
status: "cancelled",
|
||||
summary: null,
|
||||
@@ -397,7 +397,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: runId } = db.createRun({
|
||||
const { id: runId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -405,8 +405,8 @@ describe("CAS checkout", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, runId);
|
||||
db.markRunStatus(runId, "running", "");
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(runId, "running", "");
|
||||
db.runs.finishRun({
|
||||
runId,
|
||||
status: "failed",
|
||||
summary: null,
|
||||
@@ -465,7 +465,7 @@ describe("CAS checkout", () => {
|
||||
const agentId = createTestAgent(db, companyId, workspaceId);
|
||||
const taskId = createTestTask(db, companyId, null, workspaceId);
|
||||
|
||||
const { id: liveRunId } = db.createRun({
|
||||
const { id: liveRunId } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -473,7 +473,7 @@ describe("CAS checkout", () => {
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, liveRunId);
|
||||
db.markRunStatus(liveRunId, "running", "");
|
||||
db.runs.markRunStatus(liveRunId, "running", "");
|
||||
|
||||
db.close();
|
||||
db = new AppDatabase(tempDir);
|
||||
|
||||
@@ -40,7 +40,7 @@ describe("company mode switching", () => {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Mode Switch Co",
|
||||
description: "Execution mode cleanup",
|
||||
status: "active",
|
||||
@@ -101,7 +101,7 @@ describe("company mode switching", () => {
|
||||
threadIntegration.onTaskCreated(taskId, companyId);
|
||||
threadIntegration.onRunFinished("run-1", taskId, engineerId, companyId, "succeeded", null);
|
||||
db.tasks.setTaskRequiresUserReview(taskId, true);
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: taskId,
|
||||
requestedByAgentId: engineerId,
|
||||
@@ -114,7 +114,7 @@ describe("company mode switching", () => {
|
||||
|
||||
expect(db.listThreadWaitStates(companyId).length).toBeGreaterThan(0);
|
||||
expect(db.entityCrud.getTask(taskId).requiresUserReview).toBe(true);
|
||||
expect(db.getApprovalRecord(approvalId).state).toBe("pending");
|
||||
expect(db.approvalsMod.getApprovalRecord(approvalId).state).toBe("pending");
|
||||
|
||||
const handlers = new Map<string, IpcHandler>();
|
||||
registerCoreAdminHandlers({
|
||||
@@ -147,10 +147,10 @@ describe("company mode switching", () => {
|
||||
}) as { ok: boolean };
|
||||
|
||||
expect(result.ok).toBe(true);
|
||||
expect(db.getCompanyExecutionMode(companyId)).toBe("ai_ceo");
|
||||
expect(db.company.getCompanyExecutionMode(companyId)).toBe("ai_ceo");
|
||||
expect(db.listThreadWaitStates(companyId)).toHaveLength(0);
|
||||
expect(db.entityCrud.getTask(taskId).requiresUserReview).toBe(false);
|
||||
expect(db.getApprovalRecord(approvalId).state).toBe("approved");
|
||||
expect(db.approvalsMod.getApprovalRecord(approvalId).state).toBe("approved");
|
||||
|
||||
db.close();
|
||||
});
|
||||
|
||||
+29
-29
@@ -34,7 +34,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("persists company-scoped entities and resolves secret env bindings", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Northline Systems",
|
||||
description: "Operator company",
|
||||
status: "active",
|
||||
@@ -115,7 +115,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("backs up and restores the profile", async () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Backup Test Co",
|
||||
description: "Backup validation company",
|
||||
status: "active",
|
||||
@@ -123,7 +123,7 @@ describe("AppDatabase", () => {
|
||||
const backupDir = await mkdtemp(join(tmpdir(), "agentcompany-backup-"));
|
||||
|
||||
await db.exportBackup(backupDir);
|
||||
db.saveCompany({
|
||||
db.company.saveCompany({
|
||||
id: companyId,
|
||||
name: "Mutated Company",
|
||||
description: "Changed after backup",
|
||||
@@ -139,12 +139,12 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("scopes secret resolution to the owning company", () => {
|
||||
const alphaCompanyId = db.saveCompany({
|
||||
const alphaCompanyId = db.company.saveCompany({
|
||||
name: "Alpha Company",
|
||||
description: "Alpha",
|
||||
status: "active",
|
||||
});
|
||||
const betaCompanyId = db.saveCompany({
|
||||
const betaCompanyId = db.company.saveCompany({
|
||||
name: "Beta Company",
|
||||
description: "Beta",
|
||||
status: "active",
|
||||
@@ -168,7 +168,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("atomically checks out unclaimed work via CAS with run ID", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Checkout Company",
|
||||
description: "Atomic checkout validation",
|
||||
status: "active",
|
||||
@@ -210,14 +210,14 @@ describe("AppDatabase", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const { id: runId1 } = db.createRun({
|
||||
const { id: runId1 } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: leadId,
|
||||
workspaceId,
|
||||
connectorId: "openai-codex",
|
||||
});
|
||||
const { id: runId2 } = db.createRun({
|
||||
const { id: runId2 } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: leadId,
|
||||
@@ -233,7 +233,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("auto-pauses an agent when a run exhausts the monthly budget", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Budget Company",
|
||||
description: "Budget hard stop validation",
|
||||
status: "active",
|
||||
@@ -269,7 +269,7 @@ describe("AppDatabase", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -278,7 +278,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
// Simulate a per-message cost entry (written by CostWriter in prod).
|
||||
db.recordCostEntry({
|
||||
db.runs.recordCostEntry({
|
||||
runId: run.id,
|
||||
companyId,
|
||||
agentId,
|
||||
@@ -288,7 +288,7 @@ describe("AppDatabase", () => {
|
||||
unattributed: false,
|
||||
});
|
||||
|
||||
const outcome = db.finishRun({
|
||||
const outcome = db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Budget consumed",
|
||||
@@ -305,7 +305,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("activates pending hires when the corresponding approval is granted", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Hiring Company",
|
||||
description: "Approval-driven hiring",
|
||||
status: "active",
|
||||
@@ -337,7 +337,7 @@ describe("AppDatabase", () => {
|
||||
budgetMonthlyUsd: 50,
|
||||
});
|
||||
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
relatedTaskId: null,
|
||||
requestedByAgentId: managerId,
|
||||
@@ -347,7 +347,7 @@ describe("AppDatabase", () => {
|
||||
payloadJson: JSON.stringify({ agentId: pendingHireId }),
|
||||
});
|
||||
|
||||
db.decideApproval({
|
||||
db.approvalsMod.decideApproval({
|
||||
approvalId,
|
||||
state: "approved",
|
||||
decisionNote: "Approved",
|
||||
@@ -357,7 +357,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("detects pending-approval agents via isAgentPendingApproval", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Gate Test Co",
|
||||
description: "Approval gate validation",
|
||||
status: "active",
|
||||
@@ -408,7 +408,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("acquires and releases workspace locks", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Lock Test Co",
|
||||
description: "Workspace lock test",
|
||||
status: "active",
|
||||
@@ -444,7 +444,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("releases all workspace locks on crash recovery", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Recovery Co",
|
||||
description: "Lock recovery test",
|
||||
status: "active",
|
||||
@@ -466,7 +466,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
it("purges expired run logs based on retention setting", async () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Purge Co",
|
||||
description: "Log purge test",
|
||||
status: "active",
|
||||
@@ -502,11 +502,11 @@ describe("AppDatabase", () => {
|
||||
status: "todo",
|
||||
});
|
||||
|
||||
const run = db.createRun({ companyId, taskId, agentId, workspaceId: wsId, connectorId: "openai-codex" });
|
||||
await db.appendRunLog(run.id, "stdout", "test log line\n");
|
||||
const run = db.runs.createRun({ companyId, taskId, agentId, workspaceId: wsId, connectorId: "openai-codex" });
|
||||
await db.runs.appendRunLog(run.id, "stdout", "test log line\n");
|
||||
|
||||
// Finish the run with a very old timestamp
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "done",
|
||||
@@ -518,7 +518,7 @@ describe("AppDatabase", () => {
|
||||
});
|
||||
|
||||
// Log should exist before purge
|
||||
expect(db.readRunLog(run.id)).toContain("test log line");
|
||||
expect(db.runs.readRunLog(run.id)).toContain("test log line");
|
||||
|
||||
// Set retention to 1 day and manually backdate the run's finished_at
|
||||
db.updateSettings({ logRetentionDays: 1 });
|
||||
@@ -533,11 +533,11 @@ describe("AppDatabase", () => {
|
||||
expect(purged).toBe(1);
|
||||
|
||||
// Log should be gone after purge
|
||||
expect(db.readRunLog(run.id)).toBe("");
|
||||
expect(db.runs.readRunLog(run.id)).toBe("");
|
||||
});
|
||||
|
||||
it("populates related_agent_id on hire approvals", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Agent Approval Co",
|
||||
description: "test",
|
||||
status: "active",
|
||||
@@ -569,7 +569,7 @@ describe("AppDatabase", () => {
|
||||
budgetMonthlyUsd: 50,
|
||||
});
|
||||
|
||||
const approvalId = db.requestApproval({
|
||||
const approvalId = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
requestedByAgentId: managerId,
|
||||
type: "hire_agent",
|
||||
@@ -578,12 +578,12 @@ describe("AppDatabase", () => {
|
||||
payloadJson: JSON.stringify({ agentId: hireId }),
|
||||
});
|
||||
|
||||
const approval = db.getApprovalRecord(approvalId);
|
||||
const approval = db.approvalsMod.getApprovalRecord(approvalId);
|
||||
// related_agent_id should be populated from payloadJson extraction
|
||||
expect(approval.relatedAgentId).toBe(hireId);
|
||||
|
||||
// Explicit relatedAgentId param should also work
|
||||
const approvalId2 = db.requestApproval({
|
||||
const approvalId2 = db.approvalsMod.requestApproval({
|
||||
companyId,
|
||||
requestedByAgentId: managerId,
|
||||
relatedAgentId: hireId,
|
||||
@@ -592,7 +592,7 @@ describe("AppDatabase", () => {
|
||||
impactSummary: "test",
|
||||
});
|
||||
|
||||
const approval2 = db.getApprovalRecord(approvalId2);
|
||||
const approval2 = db.approvalsMod.getApprovalRecord(approvalId2);
|
||||
expect(approval2.relatedAgentId).toBe(hireId);
|
||||
});
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ import { createBudgetEnforcementService } from "../../src/main/budget-enforcemen
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function scaffold(db: AppDatabase) {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Budget Edge Case Co",
|
||||
description: "Budget exhaustion mid-run edge case tests (D-05.2)",
|
||||
status: "active",
|
||||
@@ -128,7 +128,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
});
|
||||
|
||||
// Start Run-1 (the in-flight run)
|
||||
const run1 = db.createRun({
|
||||
const run1 = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -136,7 +136,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, run1.id);
|
||||
db.markRunStatus(run1.id, "running", "");
|
||||
db.runs.markRunStatus(run1.id, "running", "");
|
||||
|
||||
// Create a second task and run that exhausts budget WHILE Run-1 is in-flight
|
||||
const taskId2 = db.tasks.saveTask({
|
||||
@@ -148,7 +148,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
priority: "medium",
|
||||
status: "todo",
|
||||
});
|
||||
const run0 = db.createRun({
|
||||
const run0 = db.runs.createRun({
|
||||
companyId,
|
||||
taskId: taskId2,
|
||||
agentId,
|
||||
@@ -156,10 +156,10 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId2, run0.id);
|
||||
db.markRunStatus(run0.id, "running", "");
|
||||
db.runs.markRunStatus(run0.id, "running", "");
|
||||
// Complete Run-0 with $6 cost (exceeds $5 budget)
|
||||
db.recordCostEntry({ runId: run0.id, companyId, agentId, taskId: taskId2, connectorId: "gemini-cli", amountUsd: 6.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.recordCostEntry({ runId: run0.id, companyId, agentId, taskId: taskId2, connectorId: "gemini-cli", amountUsd: 6.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run0.id,
|
||||
status: "succeeded",
|
||||
summary: "Expensive run",
|
||||
@@ -172,8 +172,8 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
|
||||
// Now complete Run-1 (the in-flight run) -- it should succeed normally
|
||||
// Per D-04/D-05: in-flight runs are unaffected by budget exhaustion
|
||||
db.recordCostEntry({ runId: run1.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 0.5, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.recordCostEntry({ runId: run1.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 0.5, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run1.id,
|
||||
status: "succeeded",
|
||||
summary: "In-flight run completed",
|
||||
@@ -185,7 +185,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
});
|
||||
|
||||
// Verify Run-1 completed successfully
|
||||
const finishedRun = db.getRun(run1.id);
|
||||
const finishedRun = db.runs.getRun(run1.id);
|
||||
expect(finishedRun.status).toBe("succeeded");
|
||||
|
||||
// Verify Run-1's cost was recorded (total spend now $6.50)
|
||||
@@ -214,7 +214,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
});
|
||||
|
||||
// Complete a run that exceeds budget
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -222,9 +222,9 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, run.id);
|
||||
db.markRunStatus(run.id, "running", "");
|
||||
db.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 6.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(run.id, "running", "");
|
||||
db.runs.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 6.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
@@ -262,7 +262,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
});
|
||||
|
||||
// Exhaust budget
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -270,9 +270,9 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, run.id);
|
||||
db.markRunStatus(run.id, "running", "");
|
||||
db.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 6.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(run.id, "running", "");
|
||||
db.runs.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 6.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
@@ -319,7 +319,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
});
|
||||
|
||||
// Complete a run costing $1.50 (150 cents > 100 agent limit)
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -327,9 +327,9 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, run.id);
|
||||
db.markRunStatus(run.id, "running", "");
|
||||
db.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 1.5, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(run.id, "running", "");
|
||||
db.runs.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 1.5, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
@@ -371,7 +371,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
expect(before.allowed).toBe(true);
|
||||
|
||||
// Complete a run costing $4 (400 cents > 300 limit)
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -379,9 +379,9 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, run.id);
|
||||
db.markRunStatus(run.id, "running", "");
|
||||
db.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 4.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.markRunStatus(run.id, "running", "");
|
||||
db.runs.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 4.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
@@ -417,7 +417,7 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
});
|
||||
|
||||
// Run that will exceed budget on completion
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
@@ -425,11 +425,11 @@ describe("Edge Case: Budget Exhaustion Mid-Run (D-05.2)", () => {
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
db.tasks.checkoutTask(taskId, run.id);
|
||||
db.markRunStatus(run.id, "running", "");
|
||||
db.runs.markRunStatus(run.id, "running", "");
|
||||
|
||||
// Complete with cost that exceeds budget -- cost entry is still recorded
|
||||
db.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 5.0, unattributed: false });
|
||||
db.finishRun({
|
||||
db.runs.recordCostEntry({ runId: run.id, companyId, agentId, taskId, connectorId: "gemini-cli", amountUsd: 5.0, unattributed: false });
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Done",
|
||||
|
||||
@@ -28,7 +28,7 @@ type RawDb = {
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function createTestCompany(db: AppDatabase): string {
|
||||
return db.saveCompany({
|
||||
return db.company.saveCompany({
|
||||
name: "Edge Case Company",
|
||||
description: "Concurrent checkout edge case tests (D-05.1)",
|
||||
status: "active",
|
||||
@@ -110,14 +110,14 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
const agentB = createTestAgent(db, companyId, workspaceId, "Agent-B");
|
||||
const taskId = createTestTask(db, companyId, workspaceId);
|
||||
|
||||
const { id: runIdA } = db.createRun({
|
||||
const { id: runIdA } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentA,
|
||||
workspaceId,
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
const { id: runIdB } = db.createRun({
|
||||
const { id: runIdB } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentB,
|
||||
@@ -146,14 +146,14 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
const agentB = createTestAgent(db, companyId, workspaceId, "Agent-B");
|
||||
const taskId1 = createTestTask(db, companyId, workspaceId);
|
||||
|
||||
const { id: runIdA } = db.createRun({
|
||||
const { id: runIdA } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId: taskId1,
|
||||
agentId: agentA,
|
||||
workspaceId,
|
||||
connectorId: "gemini-cli",
|
||||
});
|
||||
const { id: runIdB1 } = db.createRun({
|
||||
const { id: runIdB1 } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId: taskId1,
|
||||
agentId: agentB,
|
||||
@@ -168,7 +168,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
|
||||
// Agent-B can still checkout a DIFFERENT task
|
||||
const taskId2 = createTestTask(db, companyId, workspaceId);
|
||||
const { id: runIdB2 } = db.createRun({
|
||||
const { id: runIdB2 } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId: taskId2,
|
||||
agentId: agentB,
|
||||
@@ -192,7 +192,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
const taskId = createTestTask(db, companyId, workspaceId);
|
||||
|
||||
// Agent-A checks out successfully
|
||||
const { id: runIdA } = db.createRun({
|
||||
const { id: runIdA } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentA,
|
||||
@@ -202,7 +202,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
expect(db.tasks.checkoutTask(taskId, runIdA)).toBe(true);
|
||||
|
||||
// Task is now in_progress. A new run from Agent-B tries to checkout
|
||||
const { id: runIdB } = db.createRun({
|
||||
const { id: runIdB } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentB,
|
||||
@@ -225,7 +225,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
const taskId = createTestTask(db, companyId, workspaceId);
|
||||
|
||||
// Agent-A checks out task
|
||||
const { id: runIdA } = db.createRun({
|
||||
const { id: runIdA } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentA,
|
||||
@@ -246,7 +246,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
|
||||
// Task is still in_progress though, so a new checkout fails
|
||||
// (CAS checks status IN ('todo', 'backlog'))
|
||||
const { id: runIdB } = db.createRun({
|
||||
const { id: runIdB } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentB,
|
||||
@@ -264,7 +264,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
const agentA = createTestAgent(db, companyId, workspaceId, "Agent-A");
|
||||
const taskId = createTestTask(db, companyId, workspaceId);
|
||||
|
||||
const { id: runIdA } = db.createRun({
|
||||
const { id: runIdA } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentA,
|
||||
@@ -300,7 +300,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
const taskId = createTestTask(db, companyId, workspaceId);
|
||||
|
||||
// Agent-A checks out, then lock goes stale
|
||||
const { id: runIdA } = db.createRun({
|
||||
const { id: runIdA } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentA,
|
||||
@@ -322,7 +322,7 @@ describe("Edge Case: Concurrent Task Checkout (D-05.1)", () => {
|
||||
expect(db.entityCrud.getTask(taskId).status).toBe("todo");
|
||||
|
||||
// Agent-B can now checkout the recovered task
|
||||
const { id: runIdB } = db.createRun({
|
||||
const { id: runIdB } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId: agentB,
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { ConnectorId } from "@shared/types";
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function scaffold(db: AppDatabase, connectorId: ConnectorId = "gemini-cli") {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Session Edge Case Co",
|
||||
description: "Session rotation edge case tests (D-05.3)",
|
||||
status: "active",
|
||||
@@ -175,14 +175,14 @@ describe("Edge Case: Session Rotation During Execution (D-05.3)", () => {
|
||||
}
|
||||
|
||||
// Create a completed run for history (so generateHandoff has run data)
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
workspaceId,
|
||||
connectorId: "gemini-cli" as ConnectorId,
|
||||
});
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Completed initial implementation",
|
||||
@@ -305,14 +305,14 @@ describe("Edge Case: Session Rotation During Execution (D-05.3)", () => {
|
||||
db.tasks.incrementSessionUsage(sessionId, 300000, 200000, "run-1");
|
||||
|
||||
// Create a completed run for run history
|
||||
const run = db.createRun({
|
||||
const run = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
workspaceId,
|
||||
connectorId: "gemini-cli" as ConnectorId,
|
||||
});
|
||||
db.finishRun({
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Previous work done",
|
||||
|
||||
@@ -38,7 +38,7 @@ describe("ipc-core-agent-graph-handlers model enforcement", () => {
|
||||
version: "test",
|
||||
lastError: null,
|
||||
});
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Handler Test Co",
|
||||
description: "Tests saveAgent model validation",
|
||||
status: "active",
|
||||
|
||||
@@ -22,7 +22,7 @@ describe("knowledge provenance", () => {
|
||||
const db = new AppDatabase(profileDir);
|
||||
db.init();
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Knowledge Provenance Co",
|
||||
description: "Tests sourceRunId updates on knowledge entry edits",
|
||||
status: "active",
|
||||
|
||||
@@ -27,7 +27,7 @@ function createScaffold(db: AppDatabase) {
|
||||
lastError: null,
|
||||
});
|
||||
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Message Dispatch Co",
|
||||
description: "Tests board wake and wait-state resolution",
|
||||
status: "active",
|
||||
@@ -191,9 +191,9 @@ describe("message dispatch", () => {
|
||||
if (!result.ok) throw new Error("Expected dispatch to succeed.");
|
||||
expect(new Set(result.data.wakeTargets)).toEqual(new Set([ceoId, ctoId]));
|
||||
|
||||
const ceoInbox = db.listAgentInboxUnread(companyId, ceoId);
|
||||
const ceoInbox = db.messages.listAgentInboxUnread(companyId, ceoId);
|
||||
expect(ceoInbox.map((entry) => entry.id)).toContain(result.data.id);
|
||||
const ctoInbox = db.listAgentInboxUnread(companyId, ctoId);
|
||||
const ctoInbox = db.messages.listAgentInboxUnread(companyId, ctoId);
|
||||
expect(ctoInbox.map((entry) => entry.id)).toContain(result.data.id);
|
||||
|
||||
db.close();
|
||||
@@ -230,9 +230,9 @@ describe("message dispatch", () => {
|
||||
|
||||
// The message is still visible to CTO's inbox — they were the
|
||||
// addressed recipient — but CTO is not required to answer.
|
||||
const ctoInbox = db.listAgentInboxUnread(companyId, ctoId);
|
||||
const ctoInbox = db.messages.listAgentInboxUnread(companyId, ctoId);
|
||||
expect(ctoInbox.map((entry) => entry.id)).toContain(result.data.id);
|
||||
const ceoInbox = db.listAgentInboxUnread(companyId, ceoId);
|
||||
const ceoInbox = db.messages.listAgentInboxUnread(companyId, ceoId);
|
||||
expect(ceoInbox.map((entry) => entry.id)).toContain(result.data.id);
|
||||
|
||||
db.close();
|
||||
|
||||
@@ -26,7 +26,7 @@ describe("AppDatabase.searchMessages", () => {
|
||||
db = new AppDatabase(tempDir);
|
||||
db.init();
|
||||
|
||||
companyId = db.saveCompany({
|
||||
companyId = db.company.saveCompany({
|
||||
name: "Search Test Corp",
|
||||
description: "Company for testing message search",
|
||||
status: "active",
|
||||
@@ -61,7 +61,7 @@ describe("AppDatabase.searchMessages", () => {
|
||||
});
|
||||
|
||||
// Seed messages across different channels
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentA,
|
||||
toAgentId: agentB,
|
||||
@@ -71,7 +71,7 @@ describe("AppDatabase.searchMessages", () => {
|
||||
priority: "normal",
|
||||
});
|
||||
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentB,
|
||||
toAgentId: agentA,
|
||||
@@ -81,7 +81,7 @@ describe("AppDatabase.searchMessages", () => {
|
||||
priority: "normal",
|
||||
});
|
||||
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentA,
|
||||
channel: "company",
|
||||
@@ -90,7 +90,7 @@ describe("AppDatabase.searchMessages", () => {
|
||||
priority: "normal",
|
||||
});
|
||||
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentA,
|
||||
channel: "department",
|
||||
@@ -100,7 +100,7 @@ describe("AppDatabase.searchMessages", () => {
|
||||
priority: "urgent",
|
||||
});
|
||||
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentB,
|
||||
channel: "department",
|
||||
@@ -117,21 +117,21 @@ describe("AppDatabase.searchMessages", () => {
|
||||
});
|
||||
|
||||
it("returns messages where body contains the query string", () => {
|
||||
const results = db.searchMessages(companyId, "velocity");
|
||||
const results = db.messages.searchMessages(companyId, "velocity");
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].companyId).toBe(companyId);
|
||||
expect(results[0].body.toLowerCase()).toContain("velocity");
|
||||
});
|
||||
|
||||
it("returns messages where subject contains the query string", () => {
|
||||
const results = db.searchMessages(companyId, "retrospective");
|
||||
const results = db.messages.searchMessages(companyId, "retrospective");
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].subject).toBe("Sprint retrospective");
|
||||
});
|
||||
|
||||
it("escapes LIKE wildcards so % and _ match literally", () => {
|
||||
// Insert a message with literal % and _ characters
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentA,
|
||||
channel: "company",
|
||||
@@ -141,25 +141,25 @@ describe("AppDatabase.searchMessages", () => {
|
||||
});
|
||||
|
||||
// Search for the literal "95%"
|
||||
const percentResults = db.searchMessages(companyId, "95%");
|
||||
const percentResults = db.messages.searchMessages(companyId, "95%");
|
||||
expect(percentResults.length).toBe(1);
|
||||
expect(percentResults[0].body).toContain("95%");
|
||||
|
||||
// Search for the literal "disk_io"
|
||||
const underscoreResults = db.searchMessages(companyId, "disk_io");
|
||||
const underscoreResults = db.messages.searchMessages(companyId, "disk_io");
|
||||
expect(underscoreResults.length).toBe(1);
|
||||
expect(underscoreResults[0].body).toContain("disk_io");
|
||||
});
|
||||
|
||||
it("scopes to a specific channel when channel param is provided", () => {
|
||||
const results = db.searchMessages(companyId, "approvals", { channel: "department" });
|
||||
const results = db.messages.searchMessages(companyId, "approvals", { channel: "department" });
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].channel).toBe("department");
|
||||
expect(results[0].body).toContain("approvals");
|
||||
});
|
||||
|
||||
it("scopes to channelTargetId when provided alongside channel", () => {
|
||||
const results = db.searchMessages(companyId, "updated", {
|
||||
const results = db.messages.searchMessages(companyId, "updated", {
|
||||
channel: "department",
|
||||
channelTargetId: "design",
|
||||
});
|
||||
@@ -168,14 +168,14 @@ describe("AppDatabase.searchMessages", () => {
|
||||
});
|
||||
|
||||
it("returns empty array when no matches", () => {
|
||||
const results = db.searchMessages(companyId, "xyznonexistentquery");
|
||||
const results = db.messages.searchMessages(companyId, "xyznonexistentquery");
|
||||
expect(results).toEqual([]);
|
||||
});
|
||||
|
||||
it("respects the limit parameter (default 50)", () => {
|
||||
// Insert 60 messages
|
||||
for (let i = 0; i < 60; i++) {
|
||||
db.sendAgentMessage({
|
||||
db.messages.sendAgentMessage({
|
||||
companyId,
|
||||
fromAgentId: agentA,
|
||||
channel: "company",
|
||||
@@ -186,16 +186,16 @@ describe("AppDatabase.searchMessages", () => {
|
||||
}
|
||||
|
||||
// Default limit should be 50
|
||||
const defaultResults = db.searchMessages(companyId, "flood test");
|
||||
const defaultResults = db.messages.searchMessages(companyId, "flood test");
|
||||
expect(defaultResults.length).toBe(50);
|
||||
|
||||
// Explicit limit of 10
|
||||
const limitedResults = db.searchMessages(companyId, "flood test", { limit: 10 });
|
||||
const limitedResults = db.messages.searchMessages(companyId, "flood test", { limit: 10 });
|
||||
expect(limitedResults.length).toBe(10);
|
||||
});
|
||||
|
||||
it("returns results ordered by created_at DESC (most recent first)", () => {
|
||||
const results = db.searchMessages(companyId, "migration");
|
||||
const results = db.messages.searchMessages(companyId, "migration");
|
||||
expect(results.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
for (let i = 1; i < results.length; i++) {
|
||||
|
||||
@@ -27,7 +27,7 @@ import { resolveWorkspaceWithLockMode } from "../../src/main/orchestration/resol
|
||||
*/
|
||||
|
||||
function seedCompany(db: AppDatabase) {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Parallel Co",
|
||||
description: "Regression fixture",
|
||||
status: "active",
|
||||
@@ -151,7 +151,7 @@ describe("resolveWorkspaceWithLockMode", () => {
|
||||
});
|
||||
|
||||
it("no workspace at all → none", () => {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Empty Co",
|
||||
description: "",
|
||||
status: "active",
|
||||
|
||||
@@ -21,7 +21,7 @@ import type { ConnectorId, TaskSessionRecord } from "@shared/types";
|
||||
// Helpers — create a minimal company + agent + task scaffold
|
||||
// ---------------------------------------------------------------------------
|
||||
function scaffold(db: AppDatabase) {
|
||||
const companyId = db.saveCompany({
|
||||
const companyId = db.company.saveCompany({
|
||||
name: "Session Test Co",
|
||||
description: "Testing session management",
|
||||
status: "active",
|
||||
@@ -185,8 +185,8 @@ describe("task_sessions CRUD", () => {
|
||||
const { taskId, companyId, agentId, workspaceId } = scaffold(db);
|
||||
|
||||
// Create some runs
|
||||
const run1 = db.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "claude-pro" });
|
||||
db.finishRun({
|
||||
const run1 = db.runs.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "claude-pro" });
|
||||
db.runs.finishRun({
|
||||
runId: run1.id,
|
||||
status: "succeeded",
|
||||
summary: "First run done",
|
||||
@@ -197,8 +197,8 @@ describe("task_sessions CRUD", () => {
|
||||
unattributedCost: false,
|
||||
});
|
||||
|
||||
const run2 = db.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "claude-pro" });
|
||||
db.finishRun({
|
||||
const run2 = db.runs.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "claude-pro" });
|
||||
db.runs.finishRun({
|
||||
runId: run2.id,
|
||||
status: "failed",
|
||||
summary: "Second run failed",
|
||||
@@ -431,8 +431,8 @@ describe("session management service", () => {
|
||||
});
|
||||
|
||||
// Create a completed run for history
|
||||
const run = db.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "gemini-cli" as ConnectorId });
|
||||
db.finishRun({
|
||||
const run = db.runs.createRun({ companyId, taskId, agentId, workspaceId, connectorId: "gemini-cli" as ConnectorId });
|
||||
db.runs.finishRun({
|
||||
runId: run.id,
|
||||
status: "succeeded",
|
||||
summary: "Implemented initial logic",
|
||||
|
||||
@@ -22,7 +22,9 @@ describe("sweepStaleReviews", () => {
|
||||
getManagerForAgent: vi.fn().mockReturnValue(null),
|
||||
getChainOfCommand: vi.fn().mockReturnValue([]),
|
||||
},
|
||||
findPendingApprovalForTask: vi.fn().mockReturnValue(null),
|
||||
approvalsMod: {
|
||||
findPendingApprovalForTask: vi.fn().mockReturnValue(null),
|
||||
},
|
||||
} as unknown as AppDatabase,
|
||||
getCompanyExecutionMode: vi.fn().mockReturnValue("ai_ceo"),
|
||||
dispatchAgentMessage: vi.fn().mockReturnValue({ ok: true }),
|
||||
@@ -47,7 +49,9 @@ describe("sweepStaleReviews", () => {
|
||||
getManagerForAgent: ReturnType<typeof vi.fn>;
|
||||
getChainOfCommand: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
findPendingApprovalForTask: ReturnType<typeof vi.fn>;
|
||||
approvalsMod: {
|
||||
findPendingApprovalForTask: ReturnType<typeof vi.fn>;
|
||||
};
|
||||
};
|
||||
|
||||
it("escalates to manager's manager when task in_review for 35 minutes", () => {
|
||||
@@ -96,7 +100,7 @@ describe("sweepStaleReviews", () => {
|
||||
dbMock.tasks.listReviewTasksForSweep.mockReturnValueOnce([
|
||||
{ id: "task-1", assigneeAgentId: "agent-1", updatedAt: THIRTY_FIVE_MIN_AGO, title: "Build login" },
|
||||
]);
|
||||
dbMock.findPendingApprovalForTask.mockReturnValueOnce(null);
|
||||
dbMock.approvalsMod.findPendingApprovalForTask.mockReturnValueOnce(null);
|
||||
dbMock.agents.getManagerForAgent.mockReturnValueOnce({ id: "ceo-1", name: "CEO", companyId: "company-1" });
|
||||
dbMock.agents.getChainOfCommand.mockReturnValueOnce([]);
|
||||
|
||||
@@ -122,7 +126,7 @@ describe("sweepStaleReviews", () => {
|
||||
]);
|
||||
dbMock.agents.getManagerForAgent.mockReturnValueOnce({ id: "manager-1", name: "Manager", companyId: "company-1" });
|
||||
dbMock.agents.getChainOfCommand.mockReturnValueOnce([{ id: "higher-1", name: "Higher" }]);
|
||||
dbMock.findPendingApprovalForTask.mockReturnValueOnce({ id: "approval-1", type: "deliverable_review" });
|
||||
dbMock.approvalsMod.findPendingApprovalForTask.mockReturnValueOnce({ id: "approval-1", type: "deliverable_review" });
|
||||
|
||||
sweepStaleReviews("company-1", deps, NOW);
|
||||
|
||||
@@ -147,7 +151,7 @@ describe("sweepStaleReviews", () => {
|
||||
]);
|
||||
dbMock.agents.getManagerForAgent.mockReturnValueOnce({ id: "manager-1", name: "Manager", companyId: "company-1" });
|
||||
dbMock.agents.getChainOfCommand.mockReturnValueOnce([{ id: "higher-1", name: "Higher" }]);
|
||||
dbMock.findPendingApprovalForTask.mockReturnValueOnce({ id: "approval-1", type: "deliverable_review" });
|
||||
dbMock.approvalsMod.findPendingApprovalForTask.mockReturnValueOnce({ id: "approval-1", type: "deliverable_review" });
|
||||
(deps.resolveApprovalDecision as ReturnType<typeof vi.fn>).mockImplementationOnce(() => {
|
||||
throw new Error("Approval already decided");
|
||||
});
|
||||
@@ -164,7 +168,7 @@ describe("sweepStaleReviews", () => {
|
||||
]);
|
||||
dbMock.agents.getManagerForAgent.mockReturnValueOnce({ id: "manager-1", name: "Manager", companyId: "company-1" });
|
||||
dbMock.agents.getChainOfCommand.mockReturnValueOnce([{ id: "higher-1", name: "Higher" }]);
|
||||
dbMock.findPendingApprovalForTask.mockReturnValueOnce(null);
|
||||
dbMock.approvalsMod.findPendingApprovalForTask.mockReturnValueOnce(null);
|
||||
|
||||
sweepStaleReviews("company-1", deps, NOW);
|
||||
|
||||
@@ -221,7 +225,7 @@ describe("sweepStaleReviews", () => {
|
||||
dbMock.tasks.listReviewTasksForSweep.mockReturnValueOnce([
|
||||
{ id: "task-1", assigneeAgentId: "agent-1", updatedAt: THIRTY_FIVE_MIN_AGO, title: "Review me" },
|
||||
]);
|
||||
dbMock.findPendingApprovalForTask.mockReturnValueOnce({ id: "approval-1", type: "deliverable_review" });
|
||||
dbMock.approvalsMod.findPendingApprovalForTask.mockReturnValueOnce({ id: "approval-1", type: "deliverable_review" });
|
||||
dbMock.agents.getManagerForAgent.mockReturnValueOnce({ id: "manager-1", name: "Manager", companyId: "company-1" });
|
||||
|
||||
const count = sweepStaleReviews("company-1", deps, NOW);
|
||||
|
||||
@@ -27,7 +27,7 @@ import { AppDatabase } from "../../src/main/database";
|
||||
*/
|
||||
|
||||
function createTestCompany(db: AppDatabase): string {
|
||||
return db.saveCompany({
|
||||
return db.company.saveCompany({
|
||||
name: "Phase Helper Company",
|
||||
description: "B2 phase + lease unit tests",
|
||||
status: "active",
|
||||
@@ -94,7 +94,7 @@ function createTestRun(
|
||||
agentId: string,
|
||||
workspaceId: string,
|
||||
): string {
|
||||
const { id } = db.createRun({
|
||||
const { id } = db.runs.createRun({
|
||||
companyId,
|
||||
taskId,
|
||||
agentId,
|
||||
|
||||
@@ -19,7 +19,7 @@ import { AppDatabase } from "../../src/main/database";
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function createTestCompany(db: AppDatabase): string {
|
||||
return db.saveCompany({
|
||||
return db.company.saveCompany({
|
||||
name: "Wakeup Test Company",
|
||||
description: "Wakeup lifecycle tests",
|
||||
status: "active",
|
||||
|
||||
Reference in new issue
Block a user