Set new agent concurrency default to five

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-04-19 21:45:59 -05:00
1 parent 98344a3642
commit e783c88226
13 files changed
+59 -11

No files matched your search

+1 -1
View File
@@ -619,7 +619,7 @@ Per-agent schedule fields in `adapter_config`:
- `enabled` boolean
- `intervalSec` integer (minimum 30)
- `maxConcurrentRuns` fixed at `1` for V1
- `maxConcurrentRuns` integer; new agents default to `5`
Scheduler must skip invocation when:
+2
View File
@@ -66,6 +66,8 @@ export const AGENT_ROLE_LABELS: Record<AgentRole, string> = {
general: "General",
};
export const AGENT_DEFAULT_MAX_CONCURRENT_RUNS = 5;
export const AGENT_ICON_NAMES = [
"bot",
"cpu",
+1
View File
@@ -9,6 +9,7 @@ export {
AGENT_ADAPTER_TYPES,
AGENT_ROLES,
AGENT_ROLE_LABELS,
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
AGENT_ICON_NAMES,
ISSUE_STATUSES,
INBOX_MINE_ISSUE_STATUSES,
@@ -483,6 +483,7 @@ describe("agent permission routes", () => {
heartbeat: {
enabled: false,
intervalSec: 3600,
maxConcurrentRuns: 5,
},
},
}),
@@ -520,6 +521,7 @@ describe("agent permission routes", () => {
heartbeat: {
enabled: false,
intervalSec: 3600,
maxConcurrentRuns: 5,
},
},
}),
@@ -2132,6 +2132,7 @@ describe("company portability", () => {
runtimeConfig: {
heartbeat: {
enabled: false,
maxConcurrentRuns: 5,
},
},
});
+4
View File
@@ -7,6 +7,7 @@ import { and, desc, eq, inArray, not, sql } from "drizzle-orm";
import {
agentSkillSyncSchema,
agentMineInboxQuerySchema,
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
createAgentKeySchema,
createAgentHireSchema,
createAgentSchema,
@@ -524,6 +525,9 @@ export function agentRoutes(db: Db) {
if (parseBooleanLike(heartbeat.enabled) == null) {
heartbeat.enabled = false;
}
if (parseNumberLike(heartbeat.maxConcurrentRuns) == null) {
heartbeat.maxConcurrentRuns = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
}
normalizedRuntimeConfig.heartbeat = heartbeat;
return normalizedRuntimeConfig;
+22 -2
View File
@@ -16,7 +16,7 @@ import {
issues,
issueComments,
} from "@paperclipai/db";
import { isUuidLike, normalizeAgentUrlKey } from "@paperclipai/shared";
import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS, isUuidLike, normalizeAgentUrlKey } from "@paperclipai/shared";
import { conflict, notFound, unprocessable } from "../errors.js";
import { normalizeAgentPermissions } from "./agent-permissions.js";
import { REDACTED_EVENT_VALUE, sanitizeRecord } from "../redaction.js";
@@ -114,6 +114,25 @@ function hasConfigPatchFields(data: Partial<typeof agents.$inferInsert>) {
return CONFIG_REVISION_FIELDS.some((field) => Object.prototype.hasOwnProperty.call(data, field));
}
function parseFiniteNumberLike(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value !== "string") return null;
const parsed = Number(value.trim());
return Number.isFinite(parsed) ? parsed : null;
}
function normalizeRuntimeConfigForNewAgent(runtimeConfig: unknown): Record<string, unknown> {
const normalizedRuntimeConfig = isPlainRecord(runtimeConfig) ? { ...runtimeConfig } : {};
const heartbeat = isPlainRecord(normalizedRuntimeConfig.heartbeat)
? { ...normalizedRuntimeConfig.heartbeat }
: {};
if (parseFiniteNumberLike(heartbeat.maxConcurrentRuns) == null) {
heartbeat.maxConcurrentRuns = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
}
normalizedRuntimeConfig.heartbeat = heartbeat;
return normalizedRuntimeConfig;
}
function diffConfigSnapshot(
before: AgentConfigSnapshot,
after: AgentConfigSnapshot,
@@ -398,9 +417,10 @@ export function agentService(db: Db) {
const role = data.role ?? "general";
const normalizedPermissions = normalizeAgentPermissions(data.permissions, role);
const runtimeConfig = normalizeRuntimeConfigForNewAgent(data.runtimeConfig);
const created = await db
.insert(agents)
.values({ ...data, name: uniqueName, companyId, role, permissions: normalizedPermissions })
.values({ ...data, name: uniqueName, companyId, role, permissions: normalizedPermissions, runtimeConfig })
.returning()
.then((rows) => rows[0]);
+12 -1
View File
@@ -31,6 +31,7 @@ import type {
RoutineVariable,
} from "@paperclipai/shared";
import {
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
ISSUE_PRIORITIES,
ISSUE_STATUSES,
PROJECT_STATUSES,
@@ -590,7 +591,7 @@ const RUNTIME_DEFAULT_RULES: Array<{ path: string[]; value: unknown }> = [
{ path: ["heartbeat", "wakeOnAssignment"], value: true },
{ path: ["heartbeat", "wakeOnAutomation"], value: true },
{ path: ["heartbeat", "wakeOnDemand"], value: true },
{ path: ["heartbeat", "maxConcurrentRuns"], value: 3 },
{ path: ["heartbeat", "maxConcurrentRuns"], value: AGENT_DEFAULT_MAX_CONCURRENT_RUNS },
];
const ADAPTER_DEFAULT_RULES_BY_TYPE: Record<string, Array<{ path: string[]; value: unknown }>> = {
@@ -741,10 +742,20 @@ function clonePortableRecord(value: unknown) {
return structuredClone(value) as Record<string, unknown>;
}
function parseFiniteNumberLike(value: unknown): number | null {
if (typeof value === "number" && Number.isFinite(value)) return value;
if (typeof value !== "string") return null;
const parsed = Number(value.trim());
return Number.isFinite(parsed) ? parsed : null;
}
function disableImportedTimerHeartbeat(runtimeConfig: unknown) {
const next = clonePortableRecord(runtimeConfig) ?? {};
const heartbeat = isPlainRecord(next.heartbeat) ? { ...next.heartbeat } : {};
heartbeat.enabled = false;
if (parseFiniteNumberLike(heartbeat.maxConcurrentRuns) == null) {
heartbeat.maxConcurrentRuns = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
}
next.heartbeat = heartbeat;
return next;
}
+7 -2
View File
@@ -4,7 +4,12 @@ import { execFile as execFileCallback } from "node:child_process";
import { promisify } from "node:util";
import { and, asc, desc, eq, getTableColumns, gt, inArray, isNull, or, sql } from "drizzle-orm";
import type { Db } from "@paperclipai/db";
import type { BillingType, ExecutionWorkspace, ExecutionWorkspaceConfig } from "@paperclipai/shared";
import {
AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
type BillingType,
type ExecutionWorkspace,
type ExecutionWorkspaceConfig,
} from "@paperclipai/shared";
import {
agents,
agentRuntimeState,
@@ -83,7 +88,7 @@ const MAX_RUN_EVENT_PAYLOAD_STRING_CHARS = 16 * 1024;
const MAX_RUN_EVENT_PAYLOAD_ARRAY_ITEMS = 50;
const MAX_RUN_EVENT_PAYLOAD_OBJECT_KEYS = 100;
const MAX_RUN_EVENT_PAYLOAD_DEPTH = 6;
const HEARTBEAT_MAX_CONCURRENT_RUNS_DEFAULT = 1;
const HEARTBEAT_MAX_CONCURRENT_RUNS_DEFAULT = AGENT_DEFAULT_MAX_CONCURRENT_RUNS;
const HEARTBEAT_MAX_CONCURRENT_RUNS_MAX = 10;
const DEFERRED_WAKE_CONTEXT_KEY = "_paperclipWakeContext";
const WAKE_COMMENT_IDS_KEY = "wakeCommentIds";
+1 -1
View File
@@ -84,7 +84,7 @@ test.describe("Onboarding wizard", () => {
intervalSec: 300,
wakeOnDemand: false,
cooldownSec: 10,
maxConcurrentRuns: 1,
maxConcurrentRuns: 5,
},
},
},
+2 -1
View File
@@ -6,6 +6,7 @@ import type {
CompanySecret,
EnvBinding,
} from "@paperclipai/shared";
import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS } from "@paperclipai/shared";
import type { AdapterModel } from "../api/agents";
import { agentsApi } from "../api/agents";
import { secretsApi } from "../api/secrets";
@@ -921,7 +922,7 @@ export function AgentConfigForm(props: AgentConfigFormProps) {
value={eff(
"heartbeat",
"maxConcurrentRuns",
Number(heartbeat.maxConcurrentRuns ?? 1),
Number(heartbeat.maxConcurrentRuns ?? AGENT_DEFAULT_MAX_CONCURRENT_RUNS),
)}
onCommit={(v) => mark("heartbeat", "maxConcurrentRuns", v)}
immediate
+2 -2
View File
@@ -10,7 +10,7 @@ describe("buildNewAgentRuntimeConfig", () => {
intervalSec: 300,
wakeOnDemand: true,
cooldownSec: 10,
maxConcurrentRuns: 1,
maxConcurrentRuns: 5,
},
});
});
@@ -27,7 +27,7 @@ describe("buildNewAgentRuntimeConfig", () => {
intervalSec: 3600,
wakeOnDemand: true,
cooldownSec: 10,
maxConcurrentRuns: 1,
maxConcurrentRuns: 5,
},
});
});
+2 -1
View File
@@ -1,3 +1,4 @@
import { AGENT_DEFAULT_MAX_CONCURRENT_RUNS } from "@paperclipai/shared";
import { defaultCreateValues } from "../components/agent-config-defaults";
export function buildNewAgentRuntimeConfig(input?: {
@@ -10,7 +11,7 @@ export function buildNewAgentRuntimeConfig(input?: {
intervalSec: input?.intervalSec ?? defaultCreateValues.intervalSec,
wakeOnDemand: true,
cooldownSec: 10,
maxConcurrentRuns: 1,
maxConcurrentRuns: AGENT_DEFAULT_MAX_CONCURRENT_RUNS,
},
};
}