Merge remote-tracking branch 'public-gh/master' into paperclip-company-import-export

* public-gh/master:
  fix: address greptile follow-up feedback
  docs: clarify quickstart npx usage
  Add guarded dev restart handling
  Fix PAP-576 settings toggles and transcript default
  Add username log censor setting
  fix: use standard toggle component for permission controls

# Conflicts:
#	server/src/routes/agents.ts
#	ui/src/pages/AgentDetail.tsx
This commit is contained in:
dotta 2026-03-20 13:28:05 -05:00
commit 5140d7b0c4
44 changed files with 11673 additions and 208 deletions

View file

@ -48,6 +48,7 @@ import { findServerAdapter, listAdapterModels } from "../adapters/index.js";
import { redactEventPayload } from "../redaction.js";
import { redactCurrentUserValue } from "../log-redaction.js";
import { renderOrgChartSvg, renderOrgChartPng, type OrgNode, type OrgChartStyle, ORG_CHART_STYLES } from "./org-chart-svg.js";
import { instanceSettingsService } from "../services/instance-settings.js";
import { runClaudeLogin } from "@paperclipai/adapter-claude-local/server";
import {
DEFAULT_CODEX_LOCAL_BYPASS_APPROVALS_AND_SANDBOX,
@ -84,8 +85,15 @@ export function agentRoutes(db: Db) {
const instructions = agentInstructionsService();
const companySkills = companySkillService(db);
const workspaceOperations = workspaceOperationService(db);
const instanceSettings = instanceSettingsService(db);
const strictSecretsMode = process.env.PAPERCLIP_SECRETS_STRICT_MODE === "true";
async function getCurrentUserRedactionOptions() {
return {
enabled: (await instanceSettings.getGeneral()).censorUsernameInLogs,
};
}
function canCreateAgents(agent: { role: string; permissions: Record<string, unknown> | null | undefined }) {
if (!agent.permissions || typeof agent.permissions !== "object") return false;
return Boolean((agent.permissions as Record<string, unknown>).canCreateAgents);
@ -2084,7 +2092,7 @@ export function agentRoutes(db: Db) {
return;
}
assertCompanyAccess(req, run.companyId);
res.json(redactCurrentUserValue(run));
res.json(redactCurrentUserValue(run, await getCurrentUserRedactionOptions()));
});
router.post("/heartbeat-runs/:runId/cancel", async (req, res) => {
@ -2119,11 +2127,12 @@ export function agentRoutes(db: Db) {
const afterSeq = Number(req.query.afterSeq ?? 0);
const limit = Number(req.query.limit ?? 200);
const events = await heartbeat.listEvents(runId, Number.isFinite(afterSeq) ? afterSeq : 0, Number.isFinite(limit) ? limit : 200);
const currentUserRedactionOptions = await getCurrentUserRedactionOptions();
const redactedEvents = events.map((event) =>
redactCurrentUserValue({
...event,
payload: redactEventPayload(event.payload),
}),
}, currentUserRedactionOptions),
);
res.json(redactedEvents);
});
@ -2159,7 +2168,7 @@ export function agentRoutes(db: Db) {
const context = asRecord(run.contextSnapshot);
const executionWorkspaceId = asNonEmptyString(context?.executionWorkspaceId);
const operations = await workspaceOperations.listForRun(runId, executionWorkspaceId);
res.json(redactCurrentUserValue(operations));
res.json(redactCurrentUserValue(operations, await getCurrentUserRedactionOptions()));
});
router.get("/workspace-operations/:operationId/log", async (req, res) => {
@ -2255,7 +2264,7 @@ export function agentRoutes(db: Db) {
}
res.json({
...redactCurrentUserValue(run),
...redactCurrentUserValue(run, await getCurrentUserRedactionOptions()),
agentId: agent.id,
agentName: agent.name,
adapterType: agent.adapterType,