Reconcile Briefs surface with current master

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-08-24 07:45:47 +00:00
1 parent 741b55dbf8
commit 8e8b391bfe
12 files changed
+37 -43

No files matched your search

@@ -85,12 +85,6 @@ describe("instance experimental settings validators", () => {
expect(settings.enableApps).toBe(false);
});
it("defaults built-in agents off", () => {
const settings = instanceExperimentalSettingsSchema.parse({});
expect(settings.enableBuiltInAgents).toBe(false);
});
it("accepts worktree run execution patches", () => {
expect(
patchInstanceExperimentalSettingsSchema.parse({
@@ -111,6 +111,7 @@ async function deleteCompanySkillsAfterLateHeartbeatWritesDrain(db: Db) {
await new Promise((resolve) => setTimeout(resolve, 25));
}
}
throw lastError;
}
function expectNoCanary(value: unknown, ...markers: string[]) {
@@ -21,6 +21,7 @@ const apiPrefixes: Record<string, string> = {
"assets.ts": "/api",
"auth.ts": "/api/auth",
"board-chat.ts": "/api",
"briefs.ts": "/api",
"built-in-agents.ts": "/api",
"cloud.ts": "/api/cloud",
"companies.ts": "/api/companies",
+2
View File
@@ -25,6 +25,7 @@ import { companySkillRoutes } from "./routes/company-skills.js";
import { companySkillPolicyRoutes } from "./routes/company-skill-policy.js";
import { inboxAgentPolicyRoutes } from "./routes/inbox-agent-policy.js";
import { builtInAgentRoutes } from "./routes/built-in-agents.js";
import { briefsRoutes } from "./routes/briefs.js";
import { folderRoutes } from "./routes/folders.js";
import { summarySlotRoutes } from "./routes/summary-slots.js";
import { statusCardRoutes } from "./routes/status-cards.js";
@@ -419,6 +420,7 @@ export async function createApp(
api.use(companySkillPolicyRoutes(db));
api.use(inboxAgentPolicyRoutes(db));
api.use(builtInAgentRoutes(db));
api.use(briefsRoutes(db));
api.use(summarySlotRoutes(db));
api.use(statusCardRoutes(db));
api.use(teamsCatalogRoutes(db));
+1
View File
@@ -4,6 +4,7 @@ export { companySkillRoutes } from "./company-skills.js";
export { companySkillPolicyRoutes } from "./company-skill-policy.js";
export { inboxAgentPolicyRoutes } from "./inbox-agent-policy.js";
export { builtInAgentRoutes } from "./built-in-agents.js";
export { briefsRoutes } from "./briefs.js";
export { folderRoutes } from "./folders.js";
export { summarySlotRoutes } from "./summary-slots.js";
export { statusCardRoutes } from "./status-cards.js";
+18
View File
@@ -571,6 +571,10 @@ const responses = {
description: "Conflict",
content: { "application/json": { schema: ErrorSchema } },
},
preconditionFailed: {
description: "Precondition failed",
content: { "application/json": { schema: ErrorSchema } },
},
payloadTooLarge: {
description: "Payload too large",
content: { "application/json": { schema: ErrorSchema } },
@@ -3564,6 +3568,20 @@ registry.registerPath({
responses: { 200: r.ok(), 401: r.unauthorized },
});
registry.registerPath({
method: "get",
path: "/api/companies/{companyId}/briefs/overview",
tags: ["briefs"],
summary: "Get Briefs overview",
request: { params: z.object({ companyId: z.string() }) },
responses: {
200: r.ok(),
401: r.unauthorized,
403: r.forbidden,
412: r.preconditionFailed,
},
});
registry.registerPath({
method: "get",
path: "/api/companies/{companyId}/recovery-observability",
-15
View File
@@ -615,21 +615,6 @@ export function agentService(db: Db) {
});
}
function assertBuiltInAgentMetadataMutationAllowed(
beforeMetadata: unknown,
afterMetadata: unknown,
options?: { allowBuiltInAgentMetadata?: boolean },
) {
if (options?.allowBuiltInAgentMetadata) return;
const beforeMarker = readBuiltInAgentMarker(beforeMetadata);
const afterMarker = readBuiltInAgentMarker(afterMetadata);
if (builtInAgentMarkersEqual(beforeMarker, afterMarker)) return;
throw conflict("Built-in agent marker is managed by Paperclip and cannot be edited directly", {
code: "built_in_agent_marker_readonly",
key: beforeMarker?.key ?? afterMarker?.key ?? null,
});
}
async function updateAgent(
id: string,
data: Partial<typeof agents.$inferInsert>,
-15
View File
@@ -1804,21 +1804,6 @@ export function authorizationService(db: Db) {
if (policyEffect.kind === "restricted") return denyRestrictedAssignmentPolicy(policyEffect);
return grantDecision;
}
if (input.action === "agent_config:read") {
return decideWithAgentConfigReadGrant("user", input.actor.userId);
}
if (input.action === "agent_config:update") {
return decideWithProtectedChangeGrants("user", input.actor.userId, {
direct: "agents:configure",
suggest: "agents:suggest-changes",
});
}
if (input.action === "skill_config:update") {
return decideWithProtectedChangeGrants("user", input.actor.userId, {
direct: "skills:create",
suggest: "skills:suggest-changes",
});
}
return decidePrincipalGrant({
companyId,
principalType: "user",
+1
View File
@@ -6,6 +6,7 @@ export { feedbackService } from "./feedback.js";
export { companySkillService } from "./company-skills.js";
export { companySkillPolicyService, normalizeSkillPolicySourceType } from "./company-skill-policy.js";
export { folderService } from "./folders.js";
export { briefsService } from "./briefs.js";
export { agentService, deduplicateAgentName } from "./agents.js";
export {
builtInAgentService,
-7
View File
@@ -195,13 +195,6 @@ export function AgentActionButtons({
pauseConfirm?: { title: string; description: ReactNode };
/** Hide the Terminate action (e.g. built-in agents are undeletable). */
hideTerminate?: boolean;
/**
* When set, pausing prompts a confirmation dialog first (e.g. for built-in
* agents that power a feature). Omit for the immediate-pause default.
*/
pauseConfirm?: { title: string; description: ReactNode };
/** Hide the Terminate action (e.g. built-in agents are undeletable). */
hideTerminate?: boolean;
/**
* Optional inline error reporter. When provided it is used instead of a toast
* for action failures (preserves the detail page's inline error banner). When
+3
View File
@@ -157,6 +157,9 @@ export const queryKeys = {
builtInAgents: {
list: (companyId: string) => ["built-in-agents", companyId] as const,
},
briefs: {
overview: (companyId: string) => ["briefs", companyId, "overview"] as const,
},
summarySlots: {
detail: (companyId: string, scopeKind: string, slotKey: string, scopeId?: string | null) =>
["summary-slots", companyId, scopeKind, slotKey, scopeId ?? null] as const,
+10
View File
@@ -34,6 +34,10 @@ const builtInAgentsApiMock = vi.hoisted(() => ({
const resumeMock = vi.hoisted(() => vi.fn());
const instanceSettingsApiMock = vi.hoisted(() => ({
getExperimental: vi.fn(),
}));
vi.mock("@/context/CompanyContext", () => ({
useCompany: () => companyState,
}));
@@ -58,6 +62,10 @@ vi.mock("@/api/agents", () => ({
agentsApi: { resume: resumeMock },
}));
vi.mock("@/api/instanceSettings", () => ({
instanceSettingsApi: instanceSettingsApiMock,
}));
vi.mock("@/components/ConfigureBuiltInAgentModal", () => ({
ConfigureBuiltInAgentModal: ({ open }: { open: boolean }) =>
open ? <div data-testid="configure-modal" /> : null,
@@ -161,7 +169,9 @@ describe("Briefs page", () => {
briefsApiMock.overview.mockReset();
builtInAgentsApiMock.list.mockReset();
resumeMock.mockReset();
instanceSettingsApiMock.getExperimental.mockReset();
breadcrumbState.setBreadcrumbs.mockReset();
instanceSettingsApiMock.getExperimental.mockResolvedValue({ enableBuiltInAgents: true });
builtInAgentsApiMock.list.mockResolvedValue([makeBuiltInState("ready")]);
briefsApiMock.overview.mockResolvedValue(makeOverview());
});